diff options
author | Markus Grabner <grabner@icg.tugraz.at> | 2010-08-12 01:35:30 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-31 15:28:15 -0700 |
commit | 1027f476f507ef7ed9919cd3e3d32310f3985da1 (patch) | |
tree | 5f0da58a5bc15338c71e848693a5ae259b97a207 /drivers/staging/line6 | |
parent | 4498dbcd2d85b67e9f46790bcfee5860d2dd1762 (diff) | |
download | linux-3.10-1027f476f507ef7ed9919cd3e3d32310f3985da1.tar.gz linux-3.10-1027f476f507ef7ed9919cd3e3d32310f3985da1.tar.bz2 linux-3.10-1027f476f507ef7ed9919cd3e3d32310f3985da1.zip |
staging: line6: sync with upstream
Big upstream sync.
Signed-off-by: Markus Grabner <grabner@icg.tugraz.at>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/line6')
27 files changed, 2135 insertions, 1175 deletions
diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig index bc1ffbed3c8..43120ff2ab7 100644 --- a/drivers/staging/line6/Kconfig +++ b/drivers/staging/line6/Kconfig @@ -1,4 +1,4 @@ -config LINE6_USB +menuconfig LINE6_USB tristate "Line6 USB support" depends on USB && SND select SND_RAWMIDI @@ -18,5 +18,68 @@ config LINE6_USB * Signal routing (record clean/processed guitar signal, re-amping) - Preliminary support for the Variax Workbench is included. + Preliminary support for the Variax Workbench and TonePort + devices is included. +if LINE6_USB + +config LINE6_USB_DEBUG + bool "print debug messages" + default n + help + Say Y here to write debug messages to the syslog. + + If unsure, say N. + +config LINE6_USB_DUMP_CTRL + bool "dump control messages" + default n + help + Say Y here to write control messages sent to and received from + Line6 devices to the syslog. + + If unsure, say N. + +config LINE6_USB_DUMP_MIDI + bool "dump MIDI messages" + default n + help + Say Y here to write MIDI messages sent to and received from + Line6 devices to the syslog. + + If unsure, say N. + +config LINE6_USB_DUMP_PCM + bool "dump PCM data" + default n + help + Say Y here to write PCM data sent to and received from Line6 + devices to the syslog. This will produce a huge amount of + syslog data during playback and capture. + + If unsure, say N. + +config LINE6_USB_RAW + bool "raw data communication" + default n + help + Say Y here to create special files which allow to send raw data + to the device. This bypasses any sanity checks, so if you discover + the code to erase the firmware, feel free to render your device + useless, but only after reading the GPL section "NO WARRANTY". + + If unsure, say N. + +config LINE6_USB_IMPULSE_RESPONSE + bool "measure impulse response" + default n + help + Say Y here to add code to measure the impulse response of a Line6 + device. This is more accurate than user-space methods since it + bypasses any PCM data buffering (e.g., by ALSA or jack). This is + useful for assessing the performance of new devices, but is not + required for normal operation. + + If unsure, say N. + +endif # LINE6_USB diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c index e2ac8d60f8c..7b58b8b4a7d 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,12 +9,12 @@ * */ -#include "driver.h" -#include "audio.h" - #include <sound/core.h> #include <sound/initval.h> +#include "driver.h" +#include "audio.h" + static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6) line6->card = card; + strcpy(card->id, line6->properties->id); strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "Line6-USB"); + strcpy(card->shortname, line6->properties->name); sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ return 0; diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h index cc0245adbcd..f12acac8e42 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c index ca092247f36..d4b70d38de0 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,27 +9,24 @@ * */ -#include "driver.h" - -#include <linux/slab.h> - #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include "audio.h" +#include "capture.h" +#include "driver.h" #include "pcm.h" #include "pod.h" -#include "capture.h" + /* Find a free URB and submit it. */ -static int submit_audio_in_urb(struct snd_pcm_substream *substream) +static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) { - unsigned int index; + int index; unsigned long flags; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int i, urb_size; struct urb *urb_in; @@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) index = find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); - if (index >= LINE6_ISO_BUFFERS) { + if (index < 0 || index >= LINE6_ISO_BUFFERS) { spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - dev_err(s2m(substream), "no free URB found\n"); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) line6pcm->buffer_in + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_in->transfer_buffer_length = urb_size; - urb_in->context = substream; + urb_in->context = line6pcm; if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0) set_bit(index, &line6pcm->active_urb_in); else - dev_err(s2m(substream), "URB in #%d submission failed\n", - index); + dev_err(line6pcm->line6->ifcdev, + "URB in #%d submission failed\n", index); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); return 0; @@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) /* Submit all currently available capture URBs. */ -static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) +int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) { int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_in_urb(substream); + ret = submit_audio_in_urb(line6pcm); if (ret < 0) return ret; } @@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) /* Unlink all currently active capture URBs. */ -static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) { unsigned int i; @@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - - line6pcm->active_urb_in = 0; - line6pcm->unlink_urb_in = 0; } /* Unlink all currently active capture URBs, and wait for finishing. */ -void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) { - unlink_audio_in_urbs(line6pcm); + line6_unlink_audio_in_urbs(line6pcm); wait_clear_audio_in_urbs(line6pcm); } /* - Callback for completed capture URB. + Copy data into ALSA capture buffer. +*/ +void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + struct snd_pcm_runtime *runtime = substream->runtime; + const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + int frames = fsize / bytes_per_frame; + + if (line6pcm->pos_in_done + frames > runtime->buffer_size) { + /* + The transferred area goes over buffer boundary, + copy two separate chunks. + */ + int len; + len = runtime->buffer_size - line6pcm->pos_in_done; + + if (len > 0) { + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, + len * bytes_per_frame); + memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, + (frames - len) * bytes_per_frame); + } else + dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */ + } else { + /* copy single chunk */ + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); + } + + if ((line6pcm->pos_in_done += frames) >= runtime->buffer_size) + line6pcm->pos_in_done -= runtime->buffer_size; +} + +void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + + if ((line6pcm->bytes_in += length) >= line6pcm->period_in) { + line6pcm->bytes_in %= line6pcm->period_in; + snd_pcm_period_elapsed(substream); + } +} + +/* + Callback for completed capture URB. */ static void audio_in_callback(struct urb *urb) { int i, index, length = 0, shutdown = 0; - int frames; unsigned long flags; - struct snd_pcm_substream *substream = - (struct snd_pcm_substream *)urb->context; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; + + line6pcm->last_frame_in = urb->start_frame; /* find index of URB */ for (index = 0; index < LINE6_ISO_BUFFERS; ++index) if (urb == line6pcm->urb_audio_in[index]) break; -#if DO_DUMP_PCM_RECEIVE +#ifdef CONFIG_LINE6_USB_DUMP_PCM for (i = 0; i < LINE6_ISO_PACKETS; ++i) { struct usb_iso_packet_descriptor *fout = &urb->iso_frame_desc[i]; @@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb) fbuf = urb->transfer_buffer + fin->offset; fsize = fin->actual_length; + + if (fsize > line6pcm->max_packet_size) { + dev_err(line6pcm->line6->ifcdev, + "driver and/or device bug: packet too large (%d > %d)\n", + fsize, line6pcm->max_packet_size); + } + length += fsize; - if (fsize > 0) { - frames = fsize / bytes_per_frame; - - if (line6pcm->pos_in_done + frames > - runtime->buffer_size) { - /* - The transferred area goes over buffer - boundary, copy two separate chunks. - */ - int len; - len = - runtime->buffer_size - - line6pcm->pos_in_done; - - if (len > 0) { - memcpy(runtime->dma_area + - line6pcm->pos_in_done * - bytes_per_frame, fbuf, - len * bytes_per_frame); - memcpy(runtime->dma_area, - fbuf + len * bytes_per_frame, - (frames - - len) * bytes_per_frame); - } else { - /* this is somewhat paranoid */ - dev_err(s2m(substream), - "driver bug: len = %d\n", len); - } - } else { - /* copy single chunk */ - memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, - fbuf, fsize * bytes_per_frame); - } + /* the following assumes LINE6_ISO_PACKETS == 1: */ +#if LINE6_BACKUP_MONITOR_SIGNAL + memcpy(line6pcm->prev_fbuf, fbuf, fsize); +#else + line6pcm->prev_fbuf = fbuf; +#endif + line6pcm->prev_fsize = fsize; - line6pcm->pos_in_done += frames; - if (line6pcm->pos_in_done >= runtime->buffer_size) - line6pcm->pos_in_done -= runtime->buffer_size; - } +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (!(line6pcm->flags & MASK_PCM_IMPULSE)) +#endif + if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags) + && (fsize > 0)) + line6_capture_copy(line6pcm, fbuf, fsize); } clear_bit(index, &line6pcm->active_urb_in); - if (test_bit(index, &line6pcm->unlink_urb_in)) + if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); if (!shutdown) { - submit_audio_in_urb(substream); + submit_audio_in_urb(line6pcm); - line6pcm->bytes_in += length; - if (line6pcm->bytes_in >= line6pcm->period_in) { - line6pcm->bytes_in -= line6pcm->period_in; - snd_pcm_period_elapsed(substream); - } + if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)) + line6_capture_check_period(line6pcm, length); } } @@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, return ret; line6pcm->period_in = params_period_bytes(hw_params); - line6pcm->buffer_in = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL); - - if (!line6pcm->buffer_in) { - dev_err(s2m(substream), "cannot malloc buffer_in\n"); - return -ENOMEM; - } - return 0; } /* hw_free capture callback */ static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - unlink_wait_clear_audio_in_urbs(line6pcm); - - kfree(line6pcm->buffer_in); - line6pcm->buffer_in = NULL; - return snd_pcm_lib_free_pages(substream); } /* trigger callback */ -int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int err; - line6pcm->count_in = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { - err = submit_audio_in_all_urbs(substream); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE); - if (err < 0) { - clear_bit(BIT_RUNNING_CAPTURE, - &line6pcm->flags); - return err; - } - } + if (err < 0) + return err; break; case SNDRV_PCM_TRIGGER_STOP: - if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) - unlink_audio_in_urbs(line6pcm); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE); + + if (err < 0) + return err; break; @@ -362,17 +366,17 @@ snd_line6_capture_pointer(struct snd_pcm_substream *substream) /* capture operators */ struct snd_pcm_ops snd_line6_capture_ops = { - .open = snd_line6_capture_open, - .close = snd_line6_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_capture_hw_params, - .hw_free = snd_line6_capture_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_capture_pointer, + .open = snd_line6_capture_open, + .close = snd_line6_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_capture_hw_params, + .hw_free = snd_line6_capture_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_capture_pointer, }; -int create_audio_in_urbs(struct snd_line6_pcm *line6pcm) +int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) { int i; diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h index 5c44464d29d..c9ea063c829 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,20 +13,21 @@ #define CAPTURE_H -#include "driver.h" - #include <sound/pcm.h> +#include "driver.h" #include "pcm.h" extern struct snd_pcm_ops snd_line6_capture_ops; - -extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, - int cmd); -extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); - +extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, + int fsize); +extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm + *line6pcm); +extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/drivers/staging/line6/control.c b/drivers/staging/line6/control.c index 0b598526de6..b848db077cb 100644 --- a/drivers/staging/line6/control.c +++ b/drivers/staging/line6/control.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,11 +9,10 @@ * */ -#include "driver.h" - #include <linux/usb.h> #include "control.h" +#include "driver.h" #include "pod.h" #include "usbdefs.h" #include "variax.h" @@ -45,7 +44,7 @@ static ssize_t pod_get_param_int(struct device *dev, char *buf, int param) { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int retval = line6_wait_dump(&pod->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&pod->dumpreq); if (retval < 0) return retval; return sprintf(buf, "%d\n", pod->prog_data.control[param]); @@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf, if (retval) return retval; - pod_transmit_parameter(pod, param, value); + line6_pod_transmit_parameter(pod, param, value); return count; } @@ -71,7 +70,7 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param) { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_variax *variax = usb_get_intfdata(interface); - int retval = line6_wait_dump(&variax->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&variax->dumpreq); if (retval < 0) return retval; return sprintf(buf, "%d\n", variax->model_data.control[param]); @@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param) static ssize_t variax_get_param_float(struct device *dev, char *buf, int param) { /* - We do our own floating point handling here since floats in the - kernel are problematic for at least two reasons: - many distros - are still shipped with binary kernels optimized for the ancient - 80386 without FPU - - there isn't a printf("%f") - (see http://www.kernelthread.com/publications/faq/335.html) + We do our own floating point handling here since at the time + this code was written (Jan 2006) it was highly discouraged to + use floating point arithmetic in the kernel. If you think that + this no longer applies, feel free to replace this by generic + floating point code. */ static const int BIAS = 0x7f; @@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param) struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_variax *variax = usb_get_intfdata(interface); const unsigned char *p = variax->model_data.control + param; - int retval = line6_wait_dump(&variax->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&variax->dumpreq); if (retval < 0) return retval; @@ -530,7 +528,7 @@ static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write); static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring, line6_nop_write); -int pod_create_files(int firmware, int type, struct device *dev) +int line6_pod_create_files(int firmware, int type, struct device *dev) { int err; CHECK_RETURN(device_create_file(dev, &dev_attr_tweak)); @@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev) (dev, &dev_attr_band_6_gain__bass)); return 0; } -EXPORT_SYMBOL(pod_create_files); -void pod_remove_files(int firmware, int type, struct device *dev) +EXPORT_SYMBOL(line6_pod_create_files); + +void line6_pod_remove_files(int firmware, int type, struct device *dev) { device_remove_file(dev, &dev_attr_tweak); device_remove_file(dev, &dev_attr_wah_position); @@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev) if (firmware >= 200) device_remove_file(dev, &dev_attr_band_6_gain__bass); } -EXPORT_SYMBOL(pod_remove_files); -int variax_create_files(int firmware, int type, struct device *dev) +EXPORT_SYMBOL(line6_pod_remove_files); + +int line6_variax_create_files(int firmware, int type, struct device *dev) { int err; CHECK_RETURN(device_create_file(dev, &dev_attr_body)); @@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev) CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring)); return 0; } -EXPORT_SYMBOL(variax_create_files); -void variax_remove_files(int firmware, int type, struct device *dev) +EXPORT_SYMBOL(line6_variax_create_files); + +void line6_variax_remove_files(int firmware, int type, struct device *dev) { device_remove_file(dev, &dev_attr_body); device_remove_file(dev, &dev_attr_pickup1_enable); @@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev) device_remove_file(dev, &dev_attr_mix1); device_remove_file(dev, &dev_attr_pickup_wiring); } -EXPORT_SYMBOL(variax_remove_files); + +EXPORT_SYMBOL(line6_variax_remove_files); diff --git a/drivers/staging/line6/control.h b/drivers/staging/line6/control.h index 47e18ab6d5b..1c1595367b0 100644 --- a/drivers/staging/line6/control.h +++ b/drivers/staging/line6/control.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,54 +12,38 @@ #ifndef LINE6_CONTROL_H #define LINE6_CONTROL_H - /** List of PODxt Pro controls. See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6. Comments after the number refer to the PODxt Pro firmware version required for this feature. + + Please *don't* reformat this file since "control.c" is created automatically + from "control.h", and this process depends on the exact formatting of the + code and the comments below! */ +/* *INDENT-OFF* */ enum { POD_tweak = 1, POD_wah_position = 4, - - /* device: LINE6_BITS_PODXTALL */ - POD_compression_gain = 5, - + POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */ POD_vol_pedal_position = 7, POD_compression_threshold = 9, POD_pan = 10, POD_amp_model_setup = 11, - POD_amp_model = 12, /* firmware: 2.0 */ + POD_amp_model = 12, /* firmware: 2.0 */ POD_drive = 13, POD_bass = 14, - - /* device: LINE6_BITS_PODXTALL */ - POD_mid = 15, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_lowmid = 15, - - /* device: LINE6_BITS_PODXTALL */ - POD_treble = 16, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_highmid = 16, - + POD_mid = 15, /* device: LINE6_BITS_PODXTALL */ + POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */ + POD_treble = 16, /* device: LINE6_BITS_PODXTALL */ + POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */ POD_chan_vol = 17, - - /* device: LINE6_BITS_PODXTALL */ - POD_reverb_mix = 18, - + POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */ POD_effect_setup = 19, POD_band_1_frequency = 20, /* firmware: 2.0 */ - - /* device: LINE6_BITS_PODXTALL */ - POD_presence = 21, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_treble__bass = 21, - + POD_presence = 21, /* device: LINE6_BITS_PODXTALL */ + POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */ POD_noise_gate_enable = 22, POD_gate_threshold = 23, POD_gate_decay_time = 24, @@ -70,137 +54,78 @@ enum { POD_mod_param_1 = 29, POD_delay_param_1 = 30, POD_delay_param_1_note_value = 31, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_2_frequency__bass = 32, /* firmware: 2.0 */ - + POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_delay_param_2 = 33, POD_delay_volume_mix = 34, POD_delay_param_3 = 35, - - /* device: LINE6_BITS_PODXTALL */ - POD_reverb_enable = 36, - POD_reverb_type = 37, - POD_reverb_decay = 38, - POD_reverb_tone = 39, - POD_reverb_pre_delay = 40, - POD_reverb_pre_post = 41, - POD_band_2_frequency = 42, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_3_frequency__bass = 42, /* firmware: 2.0 */ - + POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */ + POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */ + POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */ + POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */ + POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */ + POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */ + POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_wah_enable = 43, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_modulation_lo_cut = 44, - POD_delay_reverb_lo_cut = 45, - - /* device: LINE6_BITS_PODXTALL */ - POD_volume_pedal_minimum = 46, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_eq_pre_post = 46, /* firmware: 2.0 */ - + POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */ + POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */ + POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_volume_pre_post = 47, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_di_model = 48, - POD_di_delay = 49, - + POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */ + POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */ POD_mod_enable = 50, POD_mod_param_1_note_value = 51, POD_mod_param_2 = 52, POD_mod_param_3 = 53, POD_mod_param_4 = 54, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_mod_param_5 = 55, - + POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */ POD_mod_volume_mix = 56, POD_mod_pre_post = 57, POD_modulation_model = 58, - - /* device: LINE6_BITS_PODXTALL */ - POD_band_3_frequency = 60, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_4_frequency__bass = 60, /* firmware: 2.0 */ - + POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_mod_param_1_double_precision = 61, POD_delay_param_1_double_precision = 62, POD_eq_enable = 63, /* firmware: 2.0 */ POD_tap = 64, POD_volume_tweak_pedal_assign = 65, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_5_frequency = 68, /* firmware: 2.0 */ - + POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_tuner = 69, POD_mic_selection = 70, POD_cabinet_model = 71, POD_stomp_model = 75, POD_roomlevel = 76, - - /* device: LINE6_BITS_PODXTALL */ - POD_band_4_frequency = 77, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_6_frequency = 77, /* firmware: 2.0 */ - + POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ POD_stomp_param_1_note_value = 78, POD_stomp_param_2 = 79, POD_stomp_param_3 = 80, POD_stomp_param_4 = 81, POD_stomp_param_5 = 82, POD_stomp_param_6 = 83, - - /* device: LINE6_BITS_LIVE */ - POD_amp_switch_select = 84, - + POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */ POD_delay_param_4 = 85, POD_delay_param_5 = 86, POD_delay_pre_post = 87, - - /* device: LINE6_BITS_PODXTALL */ - POD_delay_model = 88, - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_delay_verb_model = 88, - + POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */ + POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */ POD_tempo_msb = 89, POD_tempo_lsb = 90, POD_wah_model = 91, /* firmware: 3.0 */ POD_bypass_volume = 105, /* firmware: 2.14 */ - - /* device: LINE6_BITS_PRO */ - POD_fx_loop_on_off = 107, - + POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */ POD_tweak_param_select = 108, POD_amp1_engage = 111, POD_band_1_gain = 114, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_2_gain__bass = 115, /* firmware: 2.0 */ - - /* device: LINE6_BITS_PODXTALL */ - POD_band_2_gain = 116, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_3_gain__bass = 116, /* firmware: 2.0 */ - - /* device: LINE6_BITS_PODXTALL */ - POD_band_3_gain = 117, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_4_gain__bass = 117, /* firmware: 2.0 */ - POD_band_5_gain__bass = 118, /* firmware: 2.0 */ - - /* device: LINE6_BITS_PODXTALL */ - POD_band_4_gain = 119, /* firmware: 2.0 */ - - /* device: LINE6_BITS_BASSPODXTALL */ - POD_band_6_gain__bass = 119 /* firmware: 2.0 */ + POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ + POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_3_gain__bass = 116, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ + POD_band_3_gain = 117, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ + POD_band_5_gain__bass = 118, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ + POD_band_4_gain = 119, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ + POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ }; /** @@ -218,8 +143,7 @@ enum { VARIAX_pickup2_position = 23, /* type: 24 bit float */ VARIAX_pickup2_angle = 26, /* type: 24 bit float */ VARIAX_pickup2_level = 29, /* type: 24 bit float */ - VARIAX_pickup_phase = 32, /* 0: in phase, - 1: out of phase */ + VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */ VARIAX_capacitance = 33, /* type: 24 bit float */ VARIAX_tone_resistance = 36, /* type: 24 bit float */ VARIAX_volume_resistance = 39, /* type: 24 bit float */ @@ -258,10 +182,10 @@ enum { }; -extern int pod_create_files(int firmware, int type, struct device *dev); -extern void pod_remove_files(int firmware, int type, struct device *dev); -extern int variax_create_files(int firmware, int type, struct device *dev); -extern void variax_remove_files(int firmware, int type, struct device *dev); +extern int line6_pod_create_files(int firmware, int type, struct device *dev); +extern void line6_pod_remove_files(int firmware, int type, struct device *dev); +extern int line6_variax_create_files(int firmware, int type, struct device *dev); +extern void line6_variax_remove_files(int firmware, int type, struct device *dev); #endif diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 27b986a50a0..62a9da53196 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,8 +9,6 @@ * */ -#include "driver.h" - #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -19,6 +17,7 @@ #include "audio.h" #include "capture.h" #include "control.h" +#include "driver.h" #include "midi.h" #include "playback.h" #include "pod.h" @@ -30,7 +29,7 @@ #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" #define DRIVER_DESC "Line6 USB Driver" -#define DRIVER_VERSION "0.8.0" +#define DRIVER_VERSION "0.9.0" /* table of devices that work with this driver */ @@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = { { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) }, + { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX) }, + { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1) }, + { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) }, @@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = { MODULE_DEVICE_TABLE(usb, line6_id_table); static struct line6_properties line6_properties_table[] = { - { "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM }, - { "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM }, - { "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM }, - { "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM }, - { "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM }, - { "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM }, - { "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM }, - { "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM }, - { "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM }, - { "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM }, - { "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM }, - { "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM }, - { "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM }, - { "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL } + { "BassPODxt", "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM_HWMON }, + { "BassPODxtLive", "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON }, + { "BassPODxtPro", "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON }, + { "GuitarPort", "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM }, + { "PocketPOD", "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL }, + { "PODStudioGX", "POD Studio GX", LINE6_BIT_PODSTUDIO_GX, LINE6_BIT_PCM }, + { "PODStudioUX1", "POD Studio UX1", LINE6_BIT_PODSTUDIO_UX1, LINE6_BIT_PCM }, + { "PODStudioUX2", "POD Studio UX2", LINE6_BIT_PODSTUDIO_UX2, LINE6_BIT_PCM }, + { "PODX3", "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM }, + { "PODX3Live", "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM }, + { "PODxt", "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM_HWMON }, + { "PODxtLive", "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON }, + { "PODxtPro", "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON }, + { "TonePortGX", "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM }, + { "TonePortUX1", "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM }, + { "TonePortUX2", "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM }, + { "Variax", "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL } }; /* This is Line6's MIDI manufacturer ID. */ -const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c }; +const unsigned char line6_midi_id[] = { + 0x00, 0x01, 0x0c +}; + +/* + Code to request version of POD, Variax interface + (and maybe other devices). +*/ +static const char line6_request_version0[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 +}; + +/* + Copy of version request code with GFP_KERNEL flag for use in URB. +*/ +static const char *line6_request_version; + struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; -struct workqueue_struct *line6_workqueue; /** @@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg, */ static int line6_start_listen(struct usb_line6 *line6) { + int err; usb_fill_int_urb(line6->urb_listen, line6->usbdev, usb_rcvintpipe(line6->usbdev, line6->ep_control_read), line6->buffer_listen, LINE6_BUFSIZE_LISTEN, line6_data_received, line6, line6->interval); line6->urb_listen->actual_length = 0; - return usb_submit_urb(line6->urb_listen, GFP_KERNEL); + err = usb_submit_urb(line6->urb_listen, GFP_KERNEL); + return err; +} + +/* + Stop listening on endpoint. +*/ +static void line6_stop_listen(struct usb_line6 *line6) +{ + usb_kill_urb(line6->urb_listen); } -#if DO_DUMP_ANY +#ifdef CONFIG_LINE6_USB_DUMP_ANY /* Write hexdump to syslog. */ @@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir, } #endif -#if DO_DUMP_URB_RECEIVE +#ifdef CONFIG_LINE6_USB_DUMP_CTRL /* Dump URB data to syslog. */ @@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb) #endif /* - Send raw message in pieces of max_packet_size bytes. + Send raw message in pieces of wMaxPacketSize bytes. */ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size) { int i, done = 0; - int actual_size; -#if DO_DUMP_URB_SEND +#ifdef CONFIG_LINE6_USB_DUMP_CTRL line6_write_hexdump(line6, 'S', buffer, size); #endif - for (i = 0; i < size; i += actual_size) { + for (i = 0; i < size; i += line6->max_packet_size) { + int partial; const char *frag_buf = buffer + i; int frag_size = min(line6->max_packet_size, size - i); int retval; @@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, usb_sndintpipe(line6->usbdev, line6->ep_control_write), (char *)frag_buf, frag_size, - &actual_size, LINE6_TIMEOUT * HZ); + &partial, LINE6_TIMEOUT * HZ); if (retval) { dev_err(line6->ifcdev, @@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, break; } - done += actual_size; + done += frag_size; } return done; @@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg, (char *)msg->buffer + done, bytes, line6_async_request_sent, msg, line6->interval); -#if DO_DUMP_URB_SEND +#ifdef CONFIG_LINE6_USB_DUMP_CTRL line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes); #endif @@ -253,6 +283,17 @@ static int line6_send_raw_message_async_part(struct message *msg, } /* + 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, @@ -289,6 +330,14 @@ 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) +{ + return line6_send_raw_message_async(line6, line6_request_version, sizeof(line6_request_version0)); +} + +/* Send sysex message in pieces of wMaxPacketSize bytes. */ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, @@ -298,6 +347,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, } /* + Send sysex message in pieces of wMaxPacketSize bytes. +*/ +int line6_send_sysex_message_async(struct usb_line6 *line6, const char *buffer, + int size) +{ + return line6_send_raw_message_async(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE; +} + +/* Allocate buffer for sysex message and prepare header. @param code sysex message code @param size number of bytes between code and sysex end @@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size) { - char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL); + char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); if (!buffer) { dev_err(line6->ifcdev, "out of memory\n"); @@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb) if (urb->status == -ESHUTDOWN) return; -#if DO_DUMP_URB_RECEIVE +#ifdef CONFIG_LINE6_USB_DUMP_CTRL line6_dump_urb(urb); #endif - done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length); + done = line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); if (done < urb->actual_length) { - midibuf_ignore(mb, done); + line6_midibuf_ignore(mb, done); DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length)); } for (;;) { - done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN); + done = line6_midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN); if (done == 0) break; /* MIDI input filter */ - if (midibuf_skip_message(mb, line6->line6midi->midi_mask_receive)) + if (line6_midibuf_skip_message(mb, line6->line6midi->midi_mask_receive)) continue; line6->message_length = done; -#if DO_DUMP_MIDI_RECEIVE +#ifdef CONFIG_LINE6_USB_DUMP_MIDI line6_write_hexdump(line6, 'r', line6->buffer_message, done); #endif line6_midi_receive(line6, line6->buffer_message, done); @@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb) case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_POCKETPOD: - pod_process_message((struct usb_line6_pod *)line6); + line6_pod_process_message((struct usb_line6_pod *)line6); break; case LINE6_DEVID_PODXTLIVE: switch (line6->interface_number) { case PODXTLIVE_INTERFACE_POD: - pod_process_message((struct usb_line6_pod *)line6); + line6_pod_process_message((struct usb_line6_pod *)line6); break; case PODXTLIVE_INTERFACE_VARIAX: - variax_process_message((struct usb_line6_variax *)line6); + line6_variax_process_message((struct usb_line6_variax *)line6); break; default: @@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb) break; case LINE6_DEVID_VARIAX: - variax_process_message((struct usb_line6_variax *)line6); + line6_variax_process_message((struct usb_line6_variax *)line6); break; default: @@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb) line6_start_listen(line6); } -static int line6_send(struct usb_line6 *line6, unsigned char *buf, size_t len) -{ - int retval; - int partial; - -#if DO_DUMP_URB_SEND - line6_write_hexdump(line6, 'S', buf, len); -#endif - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->ep_control_write), - buf, len, &partial, - LINE6_TIMEOUT * HZ); - - if (retval) { - dev_err(line6->ifcdev, - "usb_interrupt_msg failed (%d)\n", retval); - } - - if (partial != len) { - dev_err(line6->ifcdev, - "usb_interrupt_msg sent partial message (%d)\n", - retval); - } - - return retval; -} - /* Send channel number (i.e., switch to a different sound). */ int line6_send_program(struct usb_line6 *line6, int value) { + int retval; unsigned char *buffer; - size_t len = 2; + int partial; + + buffer = kmalloc(2, GFP_KERNEL); - buffer = kmalloc(len, GFP_KERNEL); if (!buffer) { dev_err(line6->ifcdev, "out of memory\n"); return -ENOMEM; @@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value) buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; buffer[1] = value; - return line6_send(line6, buffer, len); +#ifdef CONFIG_LINE6_USB_DUMP_CTRL + line6_write_hexdump(line6, 'S', buffer, 2); +#endif + + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, + line6->ep_control_write), + buffer, 2, &partial, LINE6_TIMEOUT * HZ); + + if (retval) + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); + + kfree(buffer); + return retval; } /* @@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value) */ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) { + int retval; unsigned char *buffer; - size_t len = 3; + int partial; + + buffer = kmalloc(3, GFP_KERNEL); - buffer = kmalloc(len, GFP_KERNEL); if (!buffer) { dev_err(line6->ifcdev, "out of memory\n"); return -ENOMEM; @@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) buffer[1] = param; buffer[2] = value; - return line6_send(line6, buffer, len); +#ifdef CONFIG_LINE6_USB_DUMP_CTRL + line6_write_hexdump(line6, 'S', buffer, 3); +#endif + + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, line6->ep_control_write), + buffer, 3, &partial, LINE6_TIMEOUT * HZ); + + if (retval) + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); + + kfree(buffer); + return retval; } /* @@ -477,10 +535,9 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat /* query the serial number: */ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE - | USB_DIR_OUT, - (datalen << 8) | 0x21, address, - NULL, 0, LINE6_TIMEOUT * HZ); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + (datalen << 8) | 0x21, address, + NULL, 0, LINE6_TIMEOUT * HZ); if (ret < 0) { dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); @@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr, /* "write" request on "raw" special file. */ -#if CREATE_RAW_FILE +#ifdef CONFIG_LINE6_USB_RAW ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface) kfree(line6); } -static void line6_list_devices(void) -{ - int i; - - for (i = 0; i < LINE6_MAX_DEVICES; ++i) { - struct usb_line6 *dev = line6_devices[i]; - printk(KERN_INFO "Line6 device %d: ", i); - - if (dev == NULL) - printk("(not used)\n"); - else - printk("%s:%d\n", dev->properties->name, dev->interface_number); - } -} - /* Probe USB device. */ @@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ if (usbdev == NULL) return -ENODEV; - /* increment reference counters: */ - usb_get_intf(interface); - usb_get_dev(usbdev); - /* we don't handle multiple configurations */ if (usbdev->descriptor.bNumConfigurations != 1) { ret = -ENODEV; @@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor); u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - if (idVendor == line6_id_table[devtype].idVendor - && idProduct == line6_id_table[devtype].idProduct) + if (idVendor == line6_id_table[devtype].idVendor && + idProduct == line6_id_table[devtype].idProduct) break; } @@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ switch (product) { case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_VARIAX: alternate = 1; break; + case LINE6_DEVID_POCKETPOD: + switch (interface_number) { + case 0: + return 0; /* this interface has no endpoints */ + case 1: + alternate = 0; + break; + default: + MISSING_CASE; + } + break; + case LINE6_DEVID_PODX3: case LINE6_DEVID_PODX3LIVE: switch (interface_number) { @@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ alternate = 5; break; - case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_GUITARPORT: + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_TONEPORT_GX: + case LINE6_DEVID_TONEPORT_UX1: alternate = 2; /* 1..4 seem to be ok */ break; - case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX2: switch (interface_number) { case 0: /* defaults to 44.1kHz, 16-bit */ alternate = 2; break; case 1: - alternate = 0; + /* don't know yet what this is ... + alternate = 1; break; + */ + return -ENODEV; default: MISSING_CASE; } @@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ case LINE6_DEVID_BASSPODXT: case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: size = sizeof(struct usb_line6_pod); @@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ep_write = 0x03; break; + case LINE6_DEVID_POCKETPOD: + size = sizeof(struct usb_line6_pod); + ep_read = 0x82; + ep_write = 0x02; + break; + case LINE6_DEVID_PODX3: case LINE6_DEVID_PODX3LIVE: /* currently unused! */ @@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ep_write = 0x01; break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: @@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: - ret = pod_init(interface, (struct usb_line6_pod *)line6); + ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); break; case LINE6_DEVID_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: - ret = pod_init(interface, (struct usb_line6_pod *)line6); + ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); break; case PODXTLIVE_INTERFACE_VARIAX: - ret = variax_init(interface, (struct usb_line6_variax *)line6); + ret = line6_variax_init(interface, (struct usb_line6_variax *)line6); break; default: @@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ break; case LINE6_DEVID_VARIAX: - ret = variax_init(interface, (struct usb_line6_variax *)line6); + ret = line6_variax_init(interface, (struct usb_line6_variax *)line6); break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_GUITARPORT: - ret = toneport_init(interface, (struct usb_line6_toneport *)line6); + ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6); break; default: @@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ if (ret < 0) goto err_destruct; + /* creation of additional special files should go here */ + dev_info(&interface->dev, "Line6 %s now attached\n", line6->properties->name); line6_devices[devnum] = line6; - line6_list_devices(); + + switch(product) { + case LINE6_DEVID_PODX3: + case LINE6_DEVID_PODX3LIVE: + dev_info(&interface->dev, "NOTE: the Line6 %s is detected, but not yet supported\n", + line6->properties->name); + } + + /* increment reference counters: */ + usb_get_intf(interface); + usb_get_dev(usbdev); + return 0; err_destruct: @@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *interface) if (usbdev == NULL) return; + /* removal of additional special files should go here */ + sysfs_remove_link(&interface->dev.kobj, "usb_device"); interface_number = interface->cur_altsetting->desc.bInterfaceNumber; @@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface) if (line6 != NULL) { if (line6->urb_listen != NULL) - usb_kill_urb(line6->urb_listen); + line6_stop_listen(line6); if (usbdev != line6->usbdev) dev_err(line6->ifcdev, @@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface) case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: - pod_disconnect(interface); + line6_pod_disconnect(interface); break; case LINE6_DEVID_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: - pod_disconnect(interface); + line6_pod_disconnect(interface); break; case PODXTLIVE_INTERFACE_VARIAX: - variax_disconnect(interface); + line6_variax_disconnect(interface); break; } break; case LINE6_DEVID_VARIAX: - variax_disconnect(interface); + line6_variax_disconnect(interface); break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_GUITARPORT: - toneport_disconnect(interface); + line6_toneport_disconnect(interface); break; default: @@ -1055,10 +1139,9 @@ static void line6_disconnect(struct usb_interface *interface) dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name); - for (i = LINE6_MAX_DEVICES; i--;) { + for (i = LINE6_MAX_DEVICES; i--;) if (line6_devices[i] == line6) line6_devices[i] = NULL; - } } line6_destruct(interface); @@ -1066,14 +1149,78 @@ static void line6_disconnect(struct usb_interface *interface) /* decrement reference counters: */ usb_put_intf(interface); usb_put_dev(usbdev); +} + +#ifdef CONFIG_PM + +/* + Suspend Line6 device. +*/ +static int line6_suspend(struct usb_interface *interface, pm_message_t message) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; + + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); + + if (line6->properties->capabilities & LINE6_BIT_CONTROL) + line6_stop_listen(line6); + + if (line6pcm != NULL) { + snd_pcm_suspend_all(line6pcm->pcm); + line6_pcm_disconnect(line6pcm); + line6pcm->flags = 0; + } + + return 0; +} + +/* + Resume Line6 device. +*/ +static int line6_resume(struct usb_interface *interface) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); + + if (line6->properties->capabilities & LINE6_BIT_CONTROL) + line6_start_listen(line6); + + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); + return 0; +} + +/* + Resume Line6 device after reset. +*/ +static int line6_reset_resume(struct usb_interface *interface) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); - line6_list_devices(); + switch (line6->usbdev->descriptor.idProduct) { + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: + case LINE6_DEVID_TONEPORT_GX: + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_GUITARPORT: + line6_toneport_reset_resume((struct usb_line6_toneport *)line6); + } + + return line6_resume(interface); } +#endif /* CONFIG_PM */ + static struct usb_driver line6_driver = { .name = DRIVER_NAME, .probe = line6_probe, .disconnect = line6_disconnect, +#ifdef CONFIG_PM + .suspend = line6_suspend, + .resume = line6_resume, + .reset_resume = line6_reset_resume, +#endif .id_table = line6_id_table, }; @@ -1086,20 +1233,27 @@ static int __init line6_init(void) printk(KERN_INFO "%s driver version %s%s\n", DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION); - line6_workqueue = create_workqueue(DRIVER_NAME); - - if (line6_workqueue == NULL) { - err("couldn't create workqueue"); - return -EINVAL; - } for (i = LINE6_MAX_DEVICES; i--;) line6_devices[i] = NULL; retval = usb_register(&line6_driver); - if (retval) + if (retval) { err("usb_register failed. Error number %d", retval); + return retval; + } + + line6_request_version = kmalloc(sizeof(line6_request_version0), + GFP_KERNEL); + + if (line6_request_version == NULL) { + err("Out of memory"); + return -ENOMEM; + } + + memcpy((char *)line6_request_version, line6_request_version0, + sizeof(line6_request_version0)); return retval; } @@ -1109,7 +1263,7 @@ static int __init line6_init(void) */ static void __exit line6_exit(void) { - destroy_workqueue(line6_workqueue); + kfree(line6_request_version); usb_deregister(&line6_driver); } diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 9908bfa6afa..b1e5557d5a8 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,23 +13,24 @@ #define DRIVER_H -#include "config.h" - #include <linux/spinlock.h> #include <linux/usb.h> -#include <linux/wait.h> #include <sound/core.h> #include "midi.h" + #define DRIVER_NAME "line6usb" +#if defined(CONFIG_LINE6_USB_DUMP_CTRL) || defined(CONFIG_LINE6_USB_DUMP_MIDI) || defined(CONFIG_LINE6_USB_DUMP_PCM) +#define CONFIG_LINE6_USB_DUMP_ANY +#endif + #define LINE6_TIMEOUT 1 #define LINE6_MAX_DEVICES 8 #define LINE6_BUFSIZE_LISTEN 32 #define LINE6_MESSAGE_MAXLEN 256 - /* Line6 MIDI control commands */ @@ -54,6 +55,12 @@ #define LINE6_CHANNEL_MASK 0x0f +#ifdef CONFIG_LINE6_USB_DEBUG +#define DEBUG_MESSAGES(x) (x) +#else +#define DEBUG_MESSAGES(x) +#endif + #define MISSING_CASE \ printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \ @@ -67,10 +74,14 @@ do { \ return err; \ } while (0) +#define CHECK_STARTUP_PROGRESS(x, n) \ + if((x) >= (n)) \ + return; \ + x = (n); + extern const unsigned char line6_midi_id[3]; extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; -extern struct workqueue_struct *line6_workqueue; static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; @@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; Common properties of Line6 devices. */ struct line6_properties { + /** + Card id string (maximum 16 characters). + This can be used to address the device in ALSA programs as + "default:CARD=<id>" + */ + const char *id; + + /** + Card short name (maximum 32 characters). + */ const char *name; + + /** + Bit identifying this device in the line6usb driver. + */ int device_bit; + + /** + Bit vector defining this device's capabilities in the + line6usb driver. + */ int capabilities; }; @@ -191,14 +221,22 @@ extern int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, int size); extern int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size); +extern int line6_send_sysex_message_async(struct usb_line6 *line6, + const char *buffer, int size); extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, + void (*function)(unsigned long), unsigned long data); extern int line6_transmit_parameter(struct usb_line6 *line6, int param, int value); +extern int line6_version_request_async(struct usb_line6 *line6); extern int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen); + +#ifdef CONFIG_LINE6_USB_DUMP_ANY extern void line6_write_hexdump(struct usb_line6 *line6, char dir, const unsigned char *buffer, int size); +#endif #endif diff --git a/drivers/staging/line6/dumprequest.c b/drivers/staging/line6/dumprequest.c index cd468c39da5..891a6976e99 100644 --- a/drivers/staging/line6/dumprequest.c +++ b/drivers/staging/line6/dumprequest.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,10 +9,9 @@ * */ -#include "driver.h" - #include <linux/slab.h> +#include "driver.h" #include "dumprequest.h" @@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr) void line6_dump_finished(struct line6_dump_request *l6dr) { l6dr->in_progress = LINE6_DUMP_NONE; - wake_up_interruptible(&l6dr->wait); + wake_up(&l6dr->wait); } /* Send an asynchronous channel dump request. */ int line6_dump_request_async(struct line6_dump_request *l6dr, - struct usb_line6 *line6, int num) + struct usb_line6 *line6, int num, int dest) { int ret; - line6_invalidate_current(l6dr); + line6_dump_started(l6dr, dest); ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer, l6dr->reqbufs[num].length); @@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr, } /* - Send an asynchronous dump request after a given interval. + Wait for completion (interruptible). */ -void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, - void (*function)(unsigned long), void *data) +int line6_dump_wait_interruptible(struct line6_dump_request *l6dr) { - l6dr->timer.expires = jiffies + seconds * HZ; - l6dr->timer.function = function; - l6dr->timer.data = (unsigned long)data; - add_timer(&l6dr->timer); + return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE); } /* Wait for completion. */ -int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock) +void line6_dump_wait(struct line6_dump_request *l6dr) +{ + wait_event(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE); +} + +/* + Wait for completion (with timeout). +*/ +int line6_dump_wait_timeout(struct line6_dump_request *l6dr, long timeout) { - 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; + return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout); } /* @@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, if (ret < 0) return ret; init_waitqueue_head(&l6dr->wait); - init_timer(&l6dr->timer); return 0; } @@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr) if (l6dr->reqbufs[0].buffer == NULL) return; line6_dumpreq_destructbuf(l6dr, 0); - l6dr->ok = 1; - del_timer_sync(&l6dr->timer); } diff --git a/drivers/staging/line6/dumprequest.h b/drivers/staging/line6/dumprequest.h index 1975d54b3c2..fce2306d234 100644 --- a/drivers/staging/line6/dumprequest.h +++ b/drivers/staging/line6/dumprequest.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -15,7 +15,6 @@ #include <linux/usb.h> #include <linux/wait.h> - #include <sound/core.h> @@ -56,16 +55,6 @@ struct line6_dump_request { int in_progress; /** - Timer for delayed dump request. - */ - struct timer_list timer; - - /** - Flag if initial dump request has been successful. - */ - char ok; - - /** Dump request buffers */ struct line6_dump_reqbuf reqbufs[1]; @@ -73,7 +62,7 @@ struct line6_dump_request { extern void line6_dump_finished(struct line6_dump_request *l6dr); extern int line6_dump_request_async(struct line6_dump_request *l6dr, - struct usb_line6 *line6, int num); + struct usb_line6 *line6, int num, int dest); extern void line6_dump_started(struct line6_dump_request *l6dr, int dest); extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr); extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num); @@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf, size_t len, int num); extern void line6_invalidate_current(struct line6_dump_request *l6dr); -extern void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, - void (*function)(unsigned long), void *data); -extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock); +extern void line6_dump_wait(struct line6_dump_request *l6dr); +extern int line6_dump_wait_interruptible(struct line6_dump_request *l6dr); +extern int line6_dump_wait_timeout(struct line6_dump_request *l6dr, + long timeout); #endif diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c index 32b6ca75cad..3c7baff7366 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,24 +9,18 @@ * */ -#include "driver.h" - -#include <linux/usb.h> #include <linux/slab.h> - +#include <linux/usb.h> #include <sound/core.h> #include <sound/rawmidi.h> #include "audio.h" +#include "driver.h" #include "midi.h" #include "pod.h" #include "usbdefs.h" -#define USE_MIDIBUF 1 -#define OUTPUT_DUMP_ONLY 0 - - #define line6_rawmidi_substream_midi(substream) \ ((struct snd_line6_midi *)((substream)->rmidi->private_data)) @@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream) spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); for (;;) { - req = min(midibuf_bytes_free(mb), line6->max_packet_size); + req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); done = snd_rawmidi_transmit_peek(substream, chunk, req); if (done == 0) break; -#if DO_DUMP_MIDI_SEND +#ifdef CONFIG_LINE6_USB_DUMP_MIDI line6_write_hexdump(line6, 's', chunk, done); #endif - midibuf_write(mb, chunk, done); + line6_midibuf_write(mb, chunk, done); snd_rawmidi_transmit_ack(substream, done); } for (;;) { - done = midibuf_read(mb, chunk, line6->max_packet_size); + done = line6_midibuf_read(mb, chunk, line6->max_packet_size); if (done == 0) break; - if (midibuf_skip_message(mb, line6midi->midi_mask_transmit)) + if (line6_midibuf_skip_message(mb, line6midi->midi_mask_transmit)) continue; send_midi_async(line6, chunk, done); @@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb) } if (num == 0) - wake_up_interruptible(&line6->line6midi->send_wait); + wake_up(&line6->line6midi->send_wait); spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); } @@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, return -ENOMEM; } -#if DO_DUMP_URB_SEND +#ifdef CONFIG_LINE6_USB_DUMP_CTRL line6_write_hexdump(line6, 'S', data, length); #endif @@ -176,8 +170,8 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_POCKETPOD: - pod_midi_postprocess((struct usb_line6_pod *)line6, data, - length); + line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data, + length); break; default: @@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) { struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; - wait_queue_head_t *head = &line6->line6midi->send_wait; - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(head, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (line6->line6midi->num_active_send_urbs > 0) - if (signal_pending(current)) - break; - else - schedule(); - - current->state = TASK_RUNNING; - remove_wait_queue(head, &wait); + struct snd_line6_midi *midi = line6->line6midi; + wait_event_interruptible(midi->send_wait, midi->num_active_send_urbs == 0); } static int line6_midi_input_open(struct snd_rawmidi_substream *substream) @@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi) rmidi->private_data = line6midi; rmidi->private_free = line6_cleanup_midi; + strcpy(rmidi->id, line6midi->line6->properties->id); strcpy(rmidi->name, line6midi->line6->properties->name); rmidi->info_flags = @@ -371,8 +355,8 @@ static int snd_line6_midi_free(struct snd_device *device) struct snd_line6_midi *line6midi = device->device_data; device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_transmit); device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive); - midibuf_destroy(&line6midi->midibuf_in); - midibuf_destroy(&line6midi->midibuf_out); + line6_midibuf_destroy(&line6midi->midibuf_in); + line6_midibuf_destroy(&line6midi->midibuf_out); return 0; } @@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6) if (line6midi == NULL) return -ENOMEM; - err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); + err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); if (err < 0) return err; - err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); + err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); if (err < 0) return err; diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h index c69fd118957..968cf574512 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c index ab0a5f30fbc..de8eef77e43 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,8 +9,6 @@ * */ -#include "config.h" - #include <linux/slab.h> #include "midibuf.h" @@ -25,9 +23,9 @@ static int midibuf_message_length(unsigned char code) return length[(code >> 4) - 8]; } else { /* - Note that according to the MIDI specification 0xf2 is - the "Song Position Pointer", but this is used by Line6 - to send sysex messages to the host. + Note that according to the MIDI specification 0xf2 is + the "Song Position Pointer", but this is used by Line6 + to send sysex messages to the host. */ static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1 }; @@ -35,13 +33,23 @@ static int midibuf_message_length(unsigned char code) } } -void midibuf_reset(struct MidiBuffer *this) +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; +} + +void line6_midibuf_reset(struct MidiBuffer *this) { this->pos_read = this->pos_write = this->full = 0; this->command_prev = -1; } -int midibuf_init(struct MidiBuffer *this, int size, int split) +int line6_midibuf_init(struct MidiBuffer *this, int size, int split) { this->buf = kmalloc(size, GFP_KERNEL); @@ -50,28 +58,18 @@ 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 MidiBuffer *this) { printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d " "full=%d command_prev=%02x\n", this->size, this->split, this->pos_read, this->pos_write, this->full, this->command_prev); } -static int midibuf_is_empty(struct MidiBuffer *this) -{ - return (this->pos_read == this->pos_write) && !this->full; -} - -static int midibuf_is_full(struct MidiBuffer *this) -{ - return this->full; -} - -int midibuf_bytes_free(struct MidiBuffer *this) +int line6_midibuf_bytes_free(struct MidiBuffer *this) { return midibuf_is_full(this) ? @@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this) (this->pos_read - this->pos_write + this->size - 1) % this->size + 1; } -int midibuf_bytes_used(struct MidiBuffer *this) +int line6_midibuf_bytes_used(struct MidiBuffer *this) { return midibuf_is_empty(this) ? @@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this) (this->pos_write - this->pos_read + this->size - 1) % this->size + 1; } -int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) +int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) { int bytes_free; int length1, length2; @@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) skip_active_sense = 1; } - bytes_free = midibuf_bytes_free(this); + bytes_free = line6_midibuf_bytes_free(this); if (length > bytes_free) length = bytes_free; @@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) return length + skip_active_sense; } -int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) +int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) { int bytes_used; int length1, length2; @@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) if (midibuf_is_empty(this)) return 0; - bytes_used = midibuf_bytes_used(this); + bytes_used = line6_midibuf_bytes_used(this); if (length > bytes_used) length = bytes_used; @@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) return length + repeat; } -int midibuf_ignore(struct MidiBuffer *this, int length) +int line6_midibuf_ignore(struct MidiBuffer *this, int length) { - int bytes_used = midibuf_bytes_used(this); + int bytes_used = line6_midibuf_bytes_used(this); if (length > bytes_used) length = bytes_used; @@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length) return length; } -int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) +int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) { int cmd = this->command_prev; @@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) return 0; } -void midibuf_destroy(struct MidiBuffer *this) +void line6_midibuf_destroy(struct MidiBuffer *this) { kfree(this->buf); this->buf = NULL; diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h index 9877581bcd9..c3c86f4ad2e 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,17 +23,17 @@ struct MidiBuffer { }; -extern int midibuf_bytes_used(struct MidiBuffer *mb); -extern int midibuf_bytes_free(struct MidiBuffer *mb); -extern void midibuf_destroy(struct MidiBuffer *mb); -extern int midibuf_ignore(struct MidiBuffer *mb, int length); -extern int midibuf_init(struct MidiBuffer *mb, int size, int split); -extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length); -extern void midibuf_reset(struct MidiBuffer *mb); -extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask); -extern void midibuf_status(struct MidiBuffer *mb); -extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data, - int length); +extern int line6_midibuf_bytes_used(struct MidiBuffer *mb); +extern int line6_midibuf_bytes_free(struct MidiBuffer *mb); +extern void line6_midibuf_destroy(struct MidiBuffer *mb); +extern int line6_midibuf_ignore(struct MidiBuffer *mb, int length); +extern int line6_midibuf_init(struct MidiBuffer *mb, int size, int split); +extern int line6_midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length); +extern void line6_midibuf_reset(struct MidiBuffer *mb); +extern int line6_midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask); +extern void line6_midibuf_status(struct MidiBuffer *mb); +extern int line6_midibuf_write(struct MidiBuffer *mb, unsigned char *data, + int length); #endif diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index fbe4b083eac..0eac29123f7 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,10 +9,7 @@ * */ -#include "driver.h" - #include <linux/slab.h> - #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> @@ -20,10 +17,176 @@ #include "audio.h" #include "capture.h" +#include "driver.h" #include "playback.h" #include "pod.h" +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +static struct snd_line6_pcm* dev2pcm(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; + return line6pcm; +} + +/* + "read" request on "impulse_volume" special file. +*/ +static ssize_t pcm_get_impulse_volume(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); +} + +/* + "write" request on "impulse_volume" special file. +*/ +static ssize_t pcm_set_impulse_volume(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_line6_pcm *line6pcm = dev2pcm(dev); + int value = simple_strtoul(buf, NULL, 10); + line6pcm->impulse_volume = value; + + if(value > 0) + line6_pcm_start(line6pcm, MASK_PCM_IMPULSE); + else + line6_pcm_stop(line6pcm, MASK_PCM_IMPULSE); + + return count; +} + +/* + "read" request on "impulse_period" special file. +*/ +static ssize_t pcm_get_impulse_period(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); +} + +/* + "write" request on "impulse_period" special file. +*/ +static ssize_t pcm_set_impulse_period(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + dev2pcm(dev)->impulse_period = simple_strtoul(buf, NULL, 10); + return count; +} + +static DEVICE_ATTR(impulse_volume, S_IWUGO | S_IRUGO, pcm_get_impulse_volume, pcm_set_impulse_volume); +static DEVICE_ATTR(impulse_period, S_IWUGO | S_IRUGO, pcm_get_impulse_period, pcm_set_impulse_period); + +#endif + +int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old = __sync_fetch_and_or(&line6pcm->flags, channels); + unsigned long flags_new = flags_old | channels; + int err = 0; + +#if LINE6_BACKUP_MONITOR_SIGNAL + if (!(line6pcm->line6->properties->capabilities & LINE6_BIT_HWMON)) { + line6pcm->prev_fbuf = kmalloc(LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); + + if (!line6pcm->prev_fbuf) { + dev_err(line6pcm->line6->ifcdev, "cannot malloc monitor buffer\n"); + return -ENOMEM; + } + } +#else + line6pcm->prev_fbuf = NULL; +#endif + + if (((flags_old & MASK_CAPTURE) == 0) && + ((flags_new & MASK_CAPTURE) != 0)) { + /* + Waiting for completion of active URBs in the stop handler is + a bug, we therefore report an error if capturing is restarted + too soon. + */ + if(line6pcm->active_urb_in | line6pcm->unlink_urb_in) + return -EBUSY; + + line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); + + if (!line6pcm->buffer_in) { + dev_err(line6pcm->line6->ifcdev, "cannot malloc capture buffer\n"); + return -ENOMEM; + } + + line6pcm->count_in = 0; + line6pcm->prev_fsize = 0; + err = line6_submit_audio_in_all_urbs(line6pcm); + + if (err < 0) { + __sync_fetch_and_and(&line6pcm->flags, ~channels); + return err; + } + } + + if (((flags_old & MASK_PLAYBACK) == 0) && + ((flags_new & MASK_PLAYBACK) != 0)) { + /* + See comment above regarding PCM restart. + */ + if(line6pcm->active_urb_out | line6pcm->unlink_urb_out) + return -EBUSY; + + line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); + + if (!line6pcm->buffer_out) { + dev_err(line6pcm->line6->ifcdev, "cannot malloc playback buffer\n"); + return -ENOMEM; + } + + line6pcm->count_out = 0; + err = line6_submit_audio_out_all_urbs(line6pcm); + + if (err < 0) { + __sync_fetch_and_and(&line6pcm->flags, ~channels); + return err; + } + } + + return 0; +} + +int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old = __sync_fetch_and_and(&line6pcm->flags, ~channels); + unsigned long flags_new = flags_old & ~channels; + + if (((flags_old & MASK_CAPTURE) != 0) && + ((flags_new & MASK_CAPTURE) == 0)) { + line6_unlink_audio_in_urbs(line6pcm); + kfree(line6pcm->buffer_in); + line6pcm->buffer_in = NULL; + } + + if (((flags_old & MASK_PLAYBACK) != 0) && + ((flags_new & MASK_PLAYBACK) == 0)) { + line6_unlink_audio_out_urbs(line6pcm); + kfree(line6pcm->buffer_out); + line6pcm->buffer_out = NULL; + } + +#if LINE6_BACKUP_MONITOR_SIGNAL + if (line6pcm->prev_fbuf != NULL) + kfree(line6pcm->prev_fbuf); +#endif + + return 0; +} + /* trigger callback */ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) snd_pcm_group_for_each_entry(s, substream) { switch (s->stream) { case SNDRV_PCM_STREAM_PLAYBACK: - err = snd_line6_playback_trigger(s, cmd); + err = snd_line6_playback_trigger(line6pcm, cmd); if (err < 0) { spin_unlock_irqrestore(&line6pcm->lock_trigger, @@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_STREAM_CAPTURE: - err = snd_line6_capture_trigger(s, cmd); + err = snd_line6_capture_trigger(line6pcm, cmd); if (err < 0) { spin_unlock_irqrestore(&line6pcm->lock_trigger, @@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) break; default: - dev_err(s2m(substream), "Unknown stream direction %d\n", + dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n", s->stream); } } @@ -70,8 +233,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) } /* control info callback */ -static int snd_line6_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol, } /* control get callback */ -static int snd_line6_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int i; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 2; i--;) - ucontrol->value.integer.value[i] = line6pcm->volume[i]; + ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; return 0; } /* control put callback */ -static int snd_line6_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int i, changed = 0; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 2; i--;) - if (line6pcm->volume[i] != ucontrol->value.integer.value[i]) { - line6pcm->volume[i] = ucontrol->value.integer.value[i]; + if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) { + line6pcm->volume_playback[i] = ucontrol->value.integer.value[i]; changed = 1; } @@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol, } /* control definition */ -static struct snd_kcontrol_new line6_control = { +static struct snd_kcontrol_new line6_control_playback = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_line6_control_info, - .get = snd_line6_control_get, - .put = snd_line6_control_put + .info = snd_line6_control_playback_info, + .get = snd_line6_control_playback_get, + .put = snd_line6_control_playback_put }; /* @@ -128,6 +291,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm) int i; struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); +#endif + for (i = LINE6_ISO_BUFFERS; i--;) { if (line6pcm->urb_audio_out[i]) { usb_kill_urb(line6pcm->urb_audio_out[i]); @@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_line6_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_line6_capture_ops); /* pre-allocation of buffers */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, @@ -177,6 +346,27 @@ 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_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + } +} + +/* + Stop PCM stream. +*/ +void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) +{ + pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE)); + pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + line6_unlink_wait_clear_audio_in_urbs(line6pcm); +} + +/* Create and register the PCM device and mixer entries. Create URBs for playback and capture. */ @@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6, break; case LINE6_DEVID_GUITARPORT: + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_TONEPORT_UX2: ep_read = 0x82; ep_write = 0x01; break; - case LINE6_DEVID_TONEPORT_UX1: - ep_read = 0x00; - ep_write = 0x00; - break; - + /* this is for interface_number == 1: case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX2: ep_read = 0x87; ep_write = 0x00; break; + */ default: MISSING_CASE; @@ -242,12 +435,13 @@ int line6_init_pcm(struct usb_line6 *line6, if (line6pcm == NULL) return -ENOMEM; - line6pcm->volume[0] = line6pcm->volume[1] = 128; + line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; + line6pcm->volume_monitor = 255; line6pcm->line6 = line6; line6pcm->ep_audio_read = ep_read; line6pcm->ep_audio_write = ep_write; line6pcm->max_packet_size = usb_maxpacket(line6->usbdev, - usb_rcvintpipe(line6->usbdev, + usb_rcvintpipe(line6->usbdev, ep_read), 0); line6pcm->properties = properties; @@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6, spin_lock_init(&line6pcm->lock_audio_in); spin_lock_init(&line6pcm->lock_trigger); - err = create_audio_out_urbs(line6pcm); + err = line6_create_audio_out_urbs(line6pcm); if (err < 0) return err; - err = create_audio_in_urbs(line6pcm); + err = line6_create_audio_in_urbs(line6pcm); if (err < 0) return err; /* mixer: */ - err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm)); + err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control_playback, line6pcm)); + if (err < 0) + return err; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /* impulse response test: */ + err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); if (err < 0) return err; + err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); + if (err < 0) + return err; + + line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; +#endif + return 0; } @@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream) struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { - unlink_wait_clear_audio_out_urbs(line6pcm); + line6pcm->count_out = 0; line6pcm->pos_out = 0; line6pcm->pos_out_done = 0; - - unlink_wait_clear_audio_in_urbs(line6pcm); line6pcm->bytes_out = 0; + line6pcm->count_in = 0; line6pcm->pos_in_done = 0; line6pcm->bytes_in = 0; } diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h index 53db217cd42..c9ff95a0c82 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -24,30 +24,79 @@ /* number of URBs */ -#define LINE6_ISO_BUFFERS 8 +#define LINE6_ISO_BUFFERS 2 -/* number of USB frames per URB */ -#define LINE6_ISO_PACKETS 2 +/* + number of USB frames per URB + The Line6 Windows driver always transmits two frames per packet, but + the Linux driver performs significantly better (i.e., lower latency) + with only one frame per packet. +*/ +#define LINE6_ISO_PACKETS 1 /* in a "full speed" device (such as the PODxt Pro) this means 1ms */ #define LINE6_ISO_INTERVAL 1 -/* this should be queried dynamically from the USB interface! */ -#define LINE6_ISO_PACKET_SIZE_MAX 252 +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE +#define LINE6_IMPULSE_DEFAULT_PERIOD 100 +#endif + +#define LINE6_BACKUP_MONITOR_SIGNAL 0 +#define LINE6_REUSE_DMA_AREA_FOR_PLAYBACK 0 /* - Extract the messaging device from the substream instance + Get substream from Line6 PCM data structure */ -#define s2m(s) (((struct snd_line6_pcm *) \ - snd_pcm_substream_chip(s))->line6->ifcdev) +#define get_substream(line6pcm, stream) (line6pcm->pcm->streams[stream].substream) +/* + PCM mode bits and masks. + "ALSA": operations triggered by applications via ALSA + "MONITOR": software monitoring + "IMPULSE": optional impulse response operation +*/ enum { - BIT_RUNNING_PLAYBACK, - BIT_RUNNING_CAPTURE, + /* individual bits: */ + BIT_PCM_ALSA_PLAYBACK, + BIT_PCM_ALSA_CAPTURE, + BIT_PCM_MONITOR_PLAYBACK, + BIT_PCM_MONITOR_CAPTURE, +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + BIT_PCM_IMPULSE_PLAYBACK, + BIT_PCM_IMPULSE_CAPTURE, +#endif BIT_PAUSE_PLAYBACK, - BIT_PREPARED + BIT_PREPARED, + + /* individual masks: */ + MASK_PCM_ALSA_PLAYBACK = 1 << BIT_PCM_ALSA_PLAYBACK, + MASK_PCM_ALSA_CAPTURE = 1 << BIT_PCM_ALSA_CAPTURE, + MASK_PCM_MONITOR_PLAYBACK = 1 << BIT_PCM_MONITOR_PLAYBACK, + MASK_PCM_MONITOR_CAPTURE = 1 << BIT_PCM_MONITOR_CAPTURE, +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + MASK_PCM_IMPULSE_PLAYBACK = 1 << BIT_PCM_IMPULSE_PLAYBACK, + MASK_PCM_IMPULSE_CAPTURE = 1 << BIT_PCM_IMPULSE_CAPTURE, +#endif + MASK_PAUSE_PLAYBACK = 1 << BIT_PAUSE_PLAYBACK, + MASK_PREPARED = 1 << BIT_PREPARED, + + /* combined masks (by operation): */ + MASK_PCM_ALSA = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_ALSA_CAPTURE, + MASK_PCM_MONITOR = MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_MONITOR_CAPTURE, +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + MASK_PCM_IMPULSE = MASK_PCM_IMPULSE_PLAYBACK | MASK_PCM_IMPULSE_CAPTURE, +#endif + + /* combined masks (by direction): */ +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_IMPULSE_PLAYBACK, + MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE | MASK_PCM_IMPULSE_CAPTURE +#else + MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK, + MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE +#endif }; struct line6_pcm_properties { @@ -83,9 +132,11 @@ struct snd_line6_pcm { struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; /** - Temporary buffer to hold data when playback buffer wraps. + Temporary buffer for playback. + Since the packet size is not known in advance, this buffer is + large enough to store maximum size packets. */ - unsigned char *wrap_out; + unsigned char *buffer_out; /** Temporary buffer for capture. @@ -95,6 +146,21 @@ struct snd_line6_pcm { unsigned char *buffer_in; /** + Temporary buffer index for playback. + */ + int index_out; + + /** + Previously captured frame (for software monitoring). + */ + unsigned char *prev_fbuf; + + /** + Size of previously captured frame (for software monitoring). + */ + int prev_fsize; + + /** Free frame position in the playback buffer. */ snd_pcm_uframes_t pos_out; @@ -204,12 +270,36 @@ struct snd_line6_pcm { /** PCM playback volume (left and right). */ - int volume[2]; + int volume_playback[2]; + + /** + PCM monitor volume. + */ + int volume_monitor; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /** + Volume of impulse response test signal (if zero, test is disabled). + */ + int impulse_volume; + + /** + Period of impulse response test signal. + */ + int impulse_period; + + /** + Counter for impulse response test signal. + */ + int impulse_count; +#endif /** Several status bits (see BIT_*). */ unsigned long flags; + + int last_frame_in, last_frame_out; }; @@ -217,6 +307,19 @@ extern int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties); extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); extern int snd_line6_prepare(struct snd_pcm_substream *substream); +extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); +extern int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels); +extern int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels); + + +#define PRINT_FRAME_DIFF(op) { \ + static int diff_prev = 1000; \ + int diff = line6pcm->last_frame_out - line6pcm->last_frame_in; \ + if((diff != diff_prev) && (abs(diff) < 100)) { \ + printk("%s frame diff = %d\n", op, diff); \ + diff_prev = diff; \ + } \ + } #endif diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index fbcd6e150aa..0c8c0c8bcd9 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,15 +9,13 @@ * */ -#include "driver.h" - -#include <linux/slab.h> - #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include "audio.h" +#include "capture.h" +#include "driver.h" #include "pcm.h" #include "pod.h" #include "playback.h" @@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[], } } +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +/* + Create signal for impulse response test. +*/ +static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, + struct urb *urb_out, int bytes_per_frame) +{ + int frames = urb_out->transfer_buffer_length / bytes_per_frame; + + if (bytes_per_frame == 4) { + /* TODO: add code for TonePort etc. */ + } else if (bytes_per_frame == 6) { + int i, j; + unsigned char *pi = line6pcm->prev_fbuf; + unsigned char *po = urb_out->transfer_buffer; + + for (i = 0; i < frames; ++i) { + for (j = 0; j < bytes_per_frame / 2; ++j) + po[j] = pi[j]; + + for (; j < bytes_per_frame; ++j) + po[j] = 0; + + pi += bytes_per_frame; + po += bytes_per_frame; + } + + if (--line6pcm->impulse_count <= 0) { + ((unsigned char *)(urb_out-> + transfer_buffer))[bytes_per_frame - + 1] = + line6pcm->impulse_volume; + line6pcm->impulse_count = line6pcm->impulse_period; + } + } +} + +#endif + +/* + Add signal to buffer for software monitoring. +*/ +static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, + int volume, int bytes_per_frame) +{ + if (volume == 0) + return; /* zero volume - no change */ + + if (bytes_per_frame == 4) { + short *pi, *po, *buf_end; + pi = (short *)signal; + po = (short *)urb_out->transfer_buffer; + buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); + + for (; po < buf_end; ++pi, ++po) + *po += (*pi * volume) >> 8; + } + + /* + We don't need to handle devices with 6 bytes per frame here + since they all support hardware monitoring. + */ +} + /* Find a free URB, prepare audio data, and submit URB. */ -static int submit_audio_out_urb(struct snd_pcm_substream *substream) +static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) { int index; unsigned long flags; int i, urb_size, urb_frames; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); const int bytes_per_frame = line6pcm->properties->bytes_per_frame; const int frame_increment = line6pcm->properties->snd_line6_rates.rats[0].num_min; const int frame_factor = line6pcm->properties->snd_line6_rates.rats[0].den * (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); - struct snd_pcm_runtime *runtime = substream->runtime; struct urb *urb_out; spin_lock_irqsave(&line6pcm->lock_audio_out, flags); @@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) if (index < 0 || index >= LINE6_ISO_BUFFERS) { spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - dev_err(s2m(substream), "no free URB found\n"); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) for (i = 0; i < LINE6_ISO_PACKETS; ++i) { /* compute frame size for given sampling rate */ - int n, fs; + int fsize = 0; struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; - line6pcm->count_out += frame_increment; - n = line6pcm->count_out / frame_factor; - line6pcm->count_out -= n * frame_factor; - fs = n * bytes_per_frame; + + if (line6pcm->flags & MASK_CAPTURE) { + fsize = line6pcm->prev_fsize; + } + + if (fsize == 0) { + int n; + line6pcm->count_out += frame_increment; + n = line6pcm->count_out / frame_factor; + line6pcm->count_out -= n * frame_factor; + fsize = n * bytes_per_frame; + } + fout->offset = urb_size; - fout->length = fs; - urb_size += fs; + fout->length = fsize; + urb_size += fsize; + } + + if (urb_size == 0) { + /* can't determine URB size */ + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); /* this is somewhat paranoid */ + return -EINVAL; } urb_frames = urb_size / bytes_per_frame; + urb_out->transfer_buffer = + line6pcm->buffer_out + + line6pcm->max_packet_size * line6pcm->index_out; + urb_out->transfer_buffer_length = urb_size; + urb_out->context = line6pcm; + + if (++line6pcm->index_out == LINE6_ISO_BUFFERS) + line6pcm->index_out = 0; + + if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags) && + !test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; - if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) { - urb_out->transfer_buffer = line6pcm->wrap_out; - memset(line6pcm->wrap_out, 0, urb_size); - } else { if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { /* The transferred area goes over buffer boundary, @@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) */ int len; len = runtime->buffer_size - line6pcm->pos_out; - urb_out->transfer_buffer = line6pcm->wrap_out; if (len > 0) { - memcpy(line6pcm->wrap_out, + memcpy(urb_out->transfer_buffer, runtime->dma_area + line6pcm->pos_out * bytes_per_frame, len * bytes_per_frame); - memcpy(line6pcm->wrap_out + + memcpy(urb_out->transfer_buffer + len * bytes_per_frame, runtime->dma_area, (urb_frames - len) * bytes_per_frame); - } else { - /* this is somewhat paranoid */ - dev_err(s2m(substream), - "driver bug: len = %d\n", len); - } + } else + dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */ } else { +#if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK /* set the buffer pointer */ urb_out->transfer_buffer = runtime->dma_area + line6pcm->pos_out * bytes_per_frame; +#else + /* copy data */ + memcpy(urb_out->transfer_buffer, + runtime->dma_area + + line6pcm->pos_out * bytes_per_frame, + urb_out->transfer_buffer_length); +#endif } - } - line6pcm->pos_out += urb_frames; - if (line6pcm->pos_out >= runtime->buffer_size) - line6pcm->pos_out -= runtime->buffer_size; + if ((line6pcm->pos_out += urb_frames) >= runtime->buffer_size) + line6pcm->pos_out -= runtime->buffer_size; + } else { + memset(urb_out->transfer_buffer, 0, + urb_out->transfer_buffer_length); + } - urb_out->transfer_buffer_length = urb_size; - urb_out->context = substream; - change_volume(urb_out, line6pcm->volume, bytes_per_frame); + change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); -#if DO_DUMP_PCM_SEND + if (line6pcm->prev_fbuf != 0) { +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (line6pcm->flags & MASK_PCM_IMPULSE) { + create_impulse_test_signal(line6pcm, urb_out, + bytes_per_frame); + if (line6pcm->flags & MASK_PCM_ALSA_CAPTURE) { + line6_capture_copy(line6pcm, urb_out->transfer_buffer, + urb_out->transfer_buffer_length); + } + } else { +#endif + if (! + (line6pcm->line6->properties-> + capabilities & LINE6_BIT_HWMON) + && (line6pcm->flags & MASK_PLAYBACK) + && (line6pcm->flags & MASK_CAPTURE)) + add_monitor_signal(urb_out, line6pcm->prev_fbuf, + line6pcm->volume_monitor, + bytes_per_frame); +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + } +#endif + } +#ifdef CONFIG_LINE6_USB_DUMP_PCM for (i = 0; i < LINE6_ISO_PACKETS; ++i) { struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; @@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0) set_bit(index, &line6pcm->active_urb_out); else - dev_err(s2m(substream), "URB out #%d submission failed\n", - index); + dev_err(line6pcm->line6->ifcdev, + "URB out #%d submission failed\n", index); spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); return 0; @@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) /* Submit all currently available playback URBs. */ -static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) +int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) { int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_out_urb(substream); + ret = submit_audio_out_urb(line6pcm); if (ret < 0) return ret; } @@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) /* Unlink all currently active playback URBs. */ -static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) { unsigned int i; @@ -202,7 +315,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) } /* - Wait until unlinking of all currently active playback URBs has been finished. + Wait until unlinking of all currently active playback URBs has been finished. */ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) { @@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - - line6pcm->active_urb_out = 0; - line6pcm->unlink_urb_out = 0; } /* Unlink all currently active playback URBs, and wait for finishing. */ -void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) { - unlink_audio_out_urbs(line6pcm); + line6_unlink_audio_out_urbs(line6pcm); wait_clear_audio_out_urbs(line6pcm); } @@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb) int i, index, length = 0, shutdown = 0; unsigned long flags; + struct snd_line6_pcm *line6pcm = + (struct snd_line6_pcm *)urb->context; struct snd_pcm_substream *substream = - (struct snd_pcm_substream *)urb->context; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); + +#if USE_CLEAR_BUFFER_WORKAROUND + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); +#endif + + line6pcm->last_frame_out = urb->start_frame; /* find index of URB */ for (index = LINE6_ISO_BUFFERS; index--;) @@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb) length += urb->iso_frame_desc[i].length; spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - line6pcm->pos_out_done += - length / line6pcm->properties->bytes_per_frame; - if (line6pcm->pos_out_done >= runtime->buffer_size) - line6pcm->pos_out_done -= runtime->buffer_size; + if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = substream->runtime; + line6pcm->pos_out_done += + length / line6pcm->properties->bytes_per_frame; + + if (line6pcm->pos_out_done >= runtime->buffer_size) + line6pcm->pos_out_done -= runtime->buffer_size; + } clear_bit(index, &line6pcm->active_urb_out); @@ -276,18 +396,20 @@ static void audio_out_callback(struct urb *urb) break; } - if (test_bit(index, &line6pcm->unlink_urb_out)) + if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); if (!shutdown) { - submit_audio_out_urb(substream); + submit_audio_out_urb(line6pcm); - line6pcm->bytes_out += length; - if (line6pcm->bytes_out >= line6pcm->period_out) { - line6pcm->bytes_out -= line6pcm->period_out; - snd_pcm_period_elapsed(substream); + if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) { + if ((line6pcm->bytes_out += + length) >= line6pcm->period_out) { + line6pcm->bytes_out %= line6pcm->period_out; + snd_pcm_period_elapsed(substream); + } } } } @@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, return ret; line6pcm->period_out = params_period_bytes(hw_params); - line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL); - - if (!line6pcm->wrap_out) { - dev_err(s2m(substream), "cannot malloc wrap_out\n"); - return -ENOMEM; - } - return 0; } /* hw_free playback callback */ static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - unlink_wait_clear_audio_out_urbs(line6pcm); - - kfree(line6pcm->wrap_out); - line6pcm->wrap_out = NULL; - return snd_pcm_lib_free_pages(substream); } /* trigger playback callback */ -int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int err; - line6pcm->count_out = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) { - err = submit_audio_out_all_urbs(substream); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK); - if (err < 0) { - clear_bit(BIT_RUNNING_PLAYBACK, - &line6pcm->flags); - return err; - } - } + if (err < 0) + return err; break; case SNDRV_PCM_TRIGGER_STOP: - if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) - unlink_audio_out_urbs(line6pcm); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK); + + if (err < 0) + return err; break; @@ -414,17 +524,17 @@ snd_line6_playback_pointer(struct snd_pcm_substream *substream) /* playback operators */ struct snd_pcm_ops snd_line6_playback_ops = { - .open = snd_line6_playback_open, - .close = snd_line6_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_playback_hw_params, - .hw_free = snd_line6_playback_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_playback_pointer, + .open = snd_line6_playback_open, + .close = snd_line6_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_playback_hw_params, + .hw_free = snd_line6_playback_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_playback_pointer, }; -int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) +int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) { int i; diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h index db1e48b3596..8b8b974fd1c 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,18 +13,29 @@ #define PLAYBACK_H -#include "driver.h" - #include <sound/pcm.h> +#include "driver.h" -extern struct snd_pcm_ops snd_line6_playback_ops; + +/* + 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 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 struct snd_pcm_ops snd_line6_playback_ops; +extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm + *line6pcm); +extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 28f514611ab..3aa6f6c8669 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,13 +9,14 @@ * */ -#include "driver.h" - #include <linux/slab.h> +#include <linux/wait.h> +#include <sound/control.h> #include "audio.h" #include "capture.h" #include "control.h" +#include "driver.h" #include "playback.h" #include "pod.h" @@ -45,7 +46,7 @@ enum { POD_tuner_freq = 0x15, POD_tuner_note = 0x16, POD_tuner_pitch = 0x17, - POD_system_invalid = 0x7fff + POD_system_invalid = 0x10000 }; enum { @@ -69,13 +70,16 @@ static struct snd_ratden pod_ratden = { }; static struct line6_pcm_properties pod_pcm_properties = { - .snd_line6_playback_hw = { + .snd_line6_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), .formats = SNDRV_PCM_FMTBIT_S24_3LE, .rates = SNDRV_PCM_RATE_KNOT, .rate_min = 39062, @@ -83,17 +87,20 @@ static struct line6_pcm_properties pod_pcm_properties = { .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 60000, - .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ + .period_bytes_min = 64, .period_bytes_max = 8192, .periods_min = 1, .periods_max = 1024 }, - .snd_line6_capture_hw = { + .snd_line6_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), .formats = SNDRV_PCM_FMTBIT_S24_3LE, .rates = SNDRV_PCM_RATE_KNOT, .rate_min = 39062, @@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = { .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 60000, - .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ + .period_bytes_min = 64, .period_bytes_max = 8192, .periods_min = 1, .periods_max = 1024 @@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = { .bytes_per_frame = POD_BYTES_PER_FRAME }; -static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 }; -static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 }; -static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 }; +static const char pod_request_channel[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 +}; + +static const char pod_version_header[] = { + 0xf2, 0x7e, 0x7f, 0x06, 0x02 +}; + + +/* forward declarations: */ +static void pod_startup2(unsigned long data); +static void pod_startup3(struct usb_line6_pod *pod); +static void pod_startup4(struct usb_line6_pod *pod); /* @@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod) set_bit(i, pod->param_dirty); } -/* - Send an asynchronous request for the POD firmware version and device ID. -*/ -static int pod_version_request_async(struct usb_line6_pod *pod) -{ - return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version)); -} - -static void pod_create_files_work(struct work_struct *work) -{ - struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work); - - pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev); -} - -static void pod_startup_timeout(unsigned long arg) -{ - enum { - REQUEST_NONE, - REQUEST_DUMP, - REQUEST_VERSION - }; - - int request = REQUEST_NONE; - struct usb_line6_pod *pod = (struct usb_line6_pod *)arg; - - if (pod->dumpreq.ok) { - if (!pod->versionreq_ok) - request = REQUEST_VERSION; - } else { - if (pod->versionreq_ok) - request = REQUEST_DUMP; - else if (pod->startup_count++ & 1) - request = REQUEST_DUMP; - else - request = REQUEST_VERSION; - } - - switch (request) { - case REQUEST_DUMP: - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); - break; - - case REQUEST_VERSION: - pod_version_request_async(pod); - break; - - default: - return; - } - - line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod); -} - static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size) { return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size); @@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) } /* - Handle SAVE button + Handle SAVE button. */ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index) { @@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind /* Process a completely received message. */ -void pod_process_message(struct usb_line6_pod *pod) +void line6_pod_process_message(struct usb_line6_pod *pod) { const unsigned char *buf = pod->line6.buffer_message; @@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod) if ((buf[1] == POD_amp_model_setup) || (buf[1] == POD_effect_setup)) /* these also affect other settings */ - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); + line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT); break; @@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod) pod->channel_num = buf[1]; pod->dirty = 0; set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); + line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT); break; case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: @@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod) case LINE6_DUMP_CURRENT: memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data)); pod_mark_batch_all_dirty(pod); - pod->dumpreq.ok = 1; break; case POD_DUMP_MEMORY: @@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod) } line6_dump_finished(&pod->dumpreq); + pod_startup3(pod); } else DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n", pod->line6.message_length, (int)sizeof(pod->prog_data) + 7)); @@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod) #define PROCESS_SYSTEM_PARAM(x) \ case POD_ ## x: \ pod->x.value = value; \ - wake_up_interruptible(&pod->x.wait); \ + wake_up(&pod->x.wait); \ break; switch (buf[6]) { @@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod) case POD_SYSEX_CLIP: DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n")); pod->clipping.value = 1; - wake_up_interruptible(&pod->clipping.wait); + wake_up(&pod->clipping.wait); break; case POD_SYSEX_STORE: @@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod) DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5])); } } else if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { - if (pod->versionreq_ok == 0) { - pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; - pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10]; - pod->versionreq_ok = 1; - - /* Now we know the firmware version, so we schedule a bottom half - handler to create the special files: */ - INIT_WORK(&pod->create_files_work, pod_create_files_work); - queue_work(line6_workqueue, &pod->create_files_work); - } else - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n")); + pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; + pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10]; + pod_startup4(pod); } else DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n")); @@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod) *) This method fails if a param change message is "chopped" after the first byte. */ -void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length) +void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length) { int i; @@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value) /* Transmit PODxt Pro control parameter. */ -void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value) +void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value) { if (line6_transmit_parameter(&pod->line6, param, value) == 0) pod_store_parameter(pod, param, value); @@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char char *p2; char *last_non_space = buf; - int retval = line6_wait_dump(&pod->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&pod->dumpreq); if (retval < 0) return retval; @@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr, { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int retval = line6_wait_dump(&pod->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&pod->dumpreq); if (retval < 0) return retval; memcpy(buf, &pod->prog_data, sizeof(pod->prog_data)); @@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr, if (count != sizeof(pod->prog_data)) { dev_err(pod->line6.ifcdev, - "data block must be exactly %zu bytes\n", - sizeof(pod->prog_data)); + "data block must be exactly %d bytes\n", + (int)sizeof(pod->prog_data)); return -EINVAL; } @@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr, } /* - Request system parameter. + Identify system parameters related to the tuner. +*/ +static bool pod_is_tuner(int code) +{ + return + (code == POD_tuner_mute) || + (code == POD_tuner_freq) || + (code == POD_tuner_note) || + (code == POD_tuner_pitch); +} + +/* + Get system parameter (as integer). @param tuner non-zero, if code refers to a tuner parameter */ -static ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign) +static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value, int code, + struct ValueWait *param, int sign) { char *sysex; - int value; static const int size = 1; int retval = 0; - DECLARE_WAITQUEUE(wait, current); - if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) + if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code)) return -ENODEV; - /* send value request to tuner: */ + /* send value request to device: */ param->value = POD_system_invalid; sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size); + if (!sysex) - return 0; + return -ENOMEM; + sysex[SYSEX_DATA_OFS] = code; line6_send_sysex_message(&pod->line6, sysex, size); kfree(sysex); - /* wait for tuner to respond: */ - add_wait_queue(¶m->wait, &wait); - current->state = TASK_INTERRUPTIBLE; + /* wait for device to respond: */ + retval = wait_event_interruptible(param->wait, param->value != POD_system_invalid); - while (param->value == POD_system_invalid) { - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } else - schedule(); - } + if (retval < 0) + return retval; - current->state = TASK_RUNNING; - remove_wait_queue(¶m->wait, &wait); + *value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value; - if (retval < 0) + if(*value == POD_system_invalid) + *value = 0; /* don't report uninitialized values */ + + return 0; +} + +/* + Get system parameter (as string). + @param tuner non-zero, if code refers to a tuner parameter +*/ +static ssize_t pod_get_system_param_string(struct usb_line6_pod *pod, char *buf, int code, + struct ValueWait *param, int sign) +{ + int retval, value = 0; + retval = pod_get_system_param_int(pod, &value, code, param, sign); + + if(retval < 0) return retval; - value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value; return sprintf(buf, "%d\n", value); } /* - Send system parameter. + Send system parameter (from integer). @param tuner non-zero, if code refers to a tuner parameter */ -static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf, - int count, int code, unsigned short mask, - int tuner) +static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, int code) { char *sysex; static const int size = 5; - unsigned short value; - unsigned long result; - int ret; - if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) + if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code)) return -EINVAL; /* send value to tuner: */ sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); if (!sysex) - return 0; - - ret = strict_strtoul(buf, 10, &result); - if (ret) - return ret; - - value = result & mask; + return -ENOMEM; sysex[SYSEX_DATA_OFS] = code; sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; @@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf, sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f; line6_send_sysex_message(&pod->line6, sysex, size); kfree(sysex); - return count; + return 0; +} + +/* + Send system parameter (from string). + @param tuner non-zero, if code refers to a tuner parameter +*/ +static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod, const char *buf, + int count, int code, unsigned short mask) +{ + int retval; + unsigned short value = simple_strtoul(buf, NULL, 10) & mask; + retval = pod_set_system_param_int(pod, value, code); + return (retval < 0) ? retval : count; } /* @@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev, { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int retval = line6_wait_dump(&pod->dumpreq, 0); + int retval = line6_dump_wait_interruptible(&pod->dumpreq); if (retval < 0) return retval; memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); @@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev, if (count != sizeof(pod->prog_data)) { dev_err(pod->line6.ifcdev, - "data block must be exactly %zu bytes\n", - sizeof(pod->prog_data)); + "data block must be exactly %d bytes\n", + (int)sizeof(pod->prog_data)); return -EINVAL; } @@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev, { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int err = 0; - DECLARE_WAITQUEUE(wait, current); - pod->clipping.value = 0; - add_wait_queue(&pod->clipping.wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (pod->clipping.value == 0) { - if (signal_pending(current)) { - err = -ERESTARTSYS; - break; - } else - schedule(); - } + return wait_event_interruptible(pod->clipping.wait, pod->clipping.value != 0); +} - current->state = TASK_RUNNING; - remove_wait_queue(&pod->clipping.wait, &wait); - return err; +/* + POD startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. +*/ + +static void pod_startup1(struct usb_line6_pod *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, 1); + + /* delay startup procedure: */ + line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, (unsigned long)pod); +} + +static void pod_startup2(unsigned long data) +{ + struct usb_line6_pod *pod = (struct usb_line6_pod *)data; + CHECK_STARTUP_PROGRESS(pod->startup_progress, 2); + + /* current channel dump: */ + line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT); +} + +static void pod_startup3(struct usb_line6_pod *pod) +{ + struct usb_line6 *line6 = &pod->line6; + CHECK_STARTUP_PROGRESS(pod->startup_progress, 3); + + /* request firmware version: */ + line6_version_request_async(line6); } -#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \ +static void pod_startup4(struct usb_line6_pod *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, 4); + + /* schedule work for global work queue: */ + schedule_work(&pod->startup_work); +} + +static void pod_startup5(struct work_struct *work) +{ + struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, startup_work); + struct usb_line6 *line6 = &pod->line6; + + CHECK_STARTUP_PROGRESS(pod->startup_progress, 5); + + /* serial number: */ + line6_read_serial_number(&pod->line6, &pod->serial_number); + + /* ALSA audio interface: */ + line6_register_audio(line6); + + /* device files: */ + line6_pod_create_files(pod->firmware_version, line6->properties->device_bit, line6->ifcdev); +} + +#define POD_GET_SYSTEM_PARAM(code, sign) \ static ssize_t pod_get_ ## code(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_interface *interface = to_usb_interface(dev); \ struct usb_line6_pod *pod = usb_get_intfdata(interface); \ - return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, \ - tuner, sign); \ + return pod_get_system_param_string(pod, buf, POD_ ## code, \ + &pod->code, sign); \ } -#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \ -POD_GET_SYSTEM_PARAM(code, tuner, sign) \ +#define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \ +POD_GET_SYSTEM_PARAM(code, sign) \ static ssize_t pod_set_ ## code(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct usb_interface *interface = to_usb_interface(dev); \ struct usb_line6_pod *pod = usb_get_intfdata(interface); \ - return pod_set_system_param(pod, buf, count, POD_ ## code, mask, \ - tuner); \ + return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \ } -POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0); -POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0); -POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0); -POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0); -POD_GET_SYSTEM_PARAM(tuner_note, 1, 1); -POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1); +POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0); +POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0); +POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0); +POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0); +POD_GET_SYSTEM_PARAM(tuner_note, 1); +POD_GET_SYSTEM_PARAM(tuner_pitch, 1); #undef GET_SET_SYSTEM_PARAM #undef GET_SYSTEM_PARAM @@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write); static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write); -#if CREATE_RAW_FILE +#ifdef CONFIG_LINE6_USB_RAW static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); #endif +/* control info callback */ +static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65535; + return 0; +} + +/* control get callback */ +static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + ucontrol->value.integer.value[0] = pod->monitor_level.value; + return 0; +} + +/* control put callback */ +static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + + if(ucontrol->value.integer.value[0] == pod->monitor_level.value) + return 0; + + pod->monitor_level.value = ucontrol->value.integer.value[0]; + pod_set_system_param_int(pod, ucontrol->value.integer.value[0], POD_monitor_level); + return 1; +} + +/* control definition */ +static struct snd_kcontrol_new pod_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_pod_control_monitor_info, + .get = snd_pod_control_monitor_get, + .put = snd_pod_control_monitor_put +}; + /* POD destructor. */ @@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface) /* free dump request data: */ line6_dumpreq_destruct(&pod->dumpreq); - - kfree(pod->buffer_versionreq); } /* @@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev) CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note)); CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch)); -#if CREATE_RAW_FILE +#ifdef CONFIG_LINE6_USB_RAW CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); #endif @@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev) } /* - Init POD device. + Try to init POD device. */ -int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) +static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod) { int err; struct usb_line6 *line6 = &pod->line6; @@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) init_waitqueue_head(&pod->tuner_note.wait); init_waitqueue_head(&pod->tuner_pitch.wait); init_waitqueue_head(&pod->clipping.wait); + init_timer(&pod->startup_timer); + INIT_WORK(&pod->startup_work, pod_startup5); memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty)); @@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) sizeof(pod_request_channel)); if (err < 0) { dev_err(&interface->dev, "Out of memory\n"); - pod_destruct(interface); - return -ENOMEM; - } - - pod->buffer_versionreq = kmemdup(pod_request_version, - sizeof(pod_request_version), - GFP_KERNEL); - - if (pod->buffer_versionreq == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - pod_destruct(interface); return -ENOMEM; } /* create sysfs entries: */ err = pod_create_files2(&interface->dev); if (err < 0) { - pod_destruct(interface); return err; } /* initialize audio system: */ err = line6_init_audio(line6); if (err < 0) { - pod_destruct(interface); return err; } /* initialize MIDI subsystem: */ err = line6_init_midi(line6); if (err < 0) { - pod_destruct(interface); return err; } /* initialize PCM subsystem: */ err = line6_init_pcm(line6, &pod_pcm_properties); if (err < 0) { - pod_destruct(interface); return err; } - /* register audio system: */ - err = line6_register_audio(line6); + /* register monitor control: */ + err = snd_ctl_add(line6->card, snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); if (err < 0) { - pod_destruct(interface); return err; } + /* + When the sound card is registered at this point, the PODxt Live + displays "Invalid Code Error 07", so we do it later in the event + handler. + */ + if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { - /* query some data: */ - line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY, - pod_startup_timeout, pod); - line6_read_serial_number(&pod->line6, &pod->serial_number); + pod->monitor_level.value = POD_system_invalid; + + /* initiate startup procedure: */ + pod_startup1(pod); } return 0; } /* + Init POD device (and clean up in case of failure). +*/ +int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) +{ + int err = pod_try_init(interface, pod); + + if (err < 0) { + pod_destruct(interface); + } + + return err; +} + +/* POD device disconnected. */ -void pod_disconnect(struct usb_interface *interface) +void line6_pod_disconnect(struct usb_interface *interface) { struct usb_line6_pod *pod; @@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface) struct device *dev = &interface->dev; if (line6pcm != NULL) { - unlink_wait_clear_audio_out_urbs(line6pcm); - unlink_wait_clear_audio_in_urbs(line6pcm); + line6_pcm_disconnect(line6pcm); } if (dev != NULL) { /* remove sysfs entries: */ - if (pod->versionreq_ok) - pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev); + line6_pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev); device_remove_file(dev, &dev_attr_channel); device_remove_file(dev, &dev_attr_clip); @@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface) device_remove_file(dev, &dev_attr_tuner_note); device_remove_file(dev, &dev_attr_tuner_pitch); -#if CREATE_RAW_FILE +#ifdef CONFIG_LINE6_USB_RAW device_remove_file(dev, &dev_attr_raw); #endif } diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 7051ca61381..d8e38a95d07 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,15 +13,14 @@ #define POD_H -#include "driver.h" - +#include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/wait.h> -#include <linux/workqueue.h> #include <sound/core.h> +#include "driver.h" #include "dumprequest.h" @@ -42,163 +41,156 @@ */ #define POD_CONTROL_SIZE 0x80 #define POD_BUFSIZE_DUMPREQ 7 -#define POD_STARTUP_DELAY 3 - +#define POD_STARTUP_DELAY 3000 /** - Data structure for values that need to be requested explicitly. - This is the case for system and tuner settings. + Data structure for values that need to be requested explicitly. + This is the case for system and tuner settings. */ struct ValueWait { - unsigned short value; + int value; wait_queue_head_t wait; }; /** - Binary PodXT Pro program dump + Binary PODxt Pro program dump */ struct pod_program { /** - Header information (including program name). + Header information (including program name). */ unsigned char header[0x20]; /** - Program parameters. + Program parameters. */ unsigned char control[POD_CONTROL_SIZE]; }; struct usb_line6_pod { /** - Generic Line6 USB data. + Generic Line6 USB data. */ struct usb_line6 line6; /** - Dump request structure. + Dump request structure. */ struct line6_dump_request dumpreq; /** - Current program number. + Current program number. */ unsigned char channel_num; /** - Current program settings. + Current program settings. */ struct pod_program prog_data; /** - Buffer for data retrieved from or to be stored on PODxt Pro. + Buffer for data retrieved from or to be stored on PODxt Pro. */ struct pod_program prog_data_buf; /** - Buffer for requesting version number. - */ - unsigned char *buffer_versionreq; - - /** - Tuner mute mode. + Tuner mute mode. */ struct ValueWait tuner_mute; /** - Tuner base frequency (typically 440Hz). + Tuner base frequency (typically 440Hz). */ struct ValueWait tuner_freq; /** - Note received from tuner. + Note received from tuner. */ struct ValueWait tuner_note; /** - Pitch value received from tuner. + Pitch value received from tuner. */ struct ValueWait tuner_pitch; /** - Instrument monitor level. + Instrument monitor level. */ struct ValueWait monitor_level; /** - Audio routing mode. - 0: send processed guitar - 1: send clean guitar - 2: send clean guitar re-amp playback - 3: send re-amp playback + Audio routing mode. + 0: send processed guitar + 1: send clean guitar + 2: send clean guitar re-amp playback + 3: send re-amp playback */ struct ValueWait routing; /** - Wait for audio clipping event. + Wait for audio clipping event. */ struct ValueWait clipping; /** - Bottom-half for creation of sysfs special files. + Timer for device initializaton. */ - struct work_struct create_files_work; + struct timer_list startup_timer; /** - Dirty flags for access to parameter data. + Work handler for device initializaton. */ - unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)]; + struct work_struct startup_work; /** - Some atomic flags. + Current progress in startup procedure. */ - unsigned long atomic_flags; + int startup_progress; /** - Counter for startup process. + Dirty flags for access to parameter data. */ - int startup_count; + unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)]; + + /** + Some atomic flags. + */ + unsigned long atomic_flags; /** - Serial number of device. + Serial number of device. */ int serial_number; /** - Firmware version (x 100). + Firmware version (x 100). */ int firmware_version; /** - Device ID. + Device ID. */ int device_id; /** - Flag to indicate modification of current program settings. + Flag to indicate modification of current program settings. */ char dirty; /** - Flag if initial firmware version request has been successful. - */ - char versionreq_ok; - - /** - Flag to enable MIDI postprocessing. + Flag to enable MIDI postprocessing. */ char midi_postprocess; }; -extern void pod_disconnect(struct usb_interface *interface); -extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod); -extern void pod_midi_postprocess(struct usb_line6_pod *pod, - unsigned char *data, int length); -extern void pod_process_message(struct usb_line6_pod *pod); -extern void pod_receive_parameter(struct usb_line6_pod *pod, int param); -extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param, - int value); +extern void line6_pod_disconnect(struct usb_interface *interface); +extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod); +extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod, + unsigned char *data, int length); +extern void line6_pod_process_message(struct usb_line6_pod *pod); +extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, + int value); #endif diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h index b2a0a85efe6..4bb87fc6675 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 " (revision 665)" #endif diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index e6770ea1793..0e7e871507c 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * Emil Myhrman (emil.myhrman@gmail.com) * * This program is free software; you can redistribute it and/or @@ -10,15 +10,22 @@ * */ -#include "driver.h" +#include <linux/wait.h> +#include <sound/control.h> #include "audio.h" #include "capture.h" +#include "driver.h" #include "playback.h" #include "toneport.h" + static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); + +#define TONEPORT_PCM_DELAY 1 + + static struct snd_ratden toneport_ratden = { .num_min = 44100, .num_max = 44100, @@ -28,43 +35,52 @@ static struct snd_ratden toneport_ratden = { static struct line6_pcm_properties toneport_pcm_properties = { .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 180 * 4, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024 + }, .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 188 * 4, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024 + }, .snd_line6_rates = { - .nrats = 1, - .rats = &toneport_ratden}, + .nrats = 1, + .rats = &toneport_ratden + }, .bytes_per_frame = 4 }; @@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = { static int led_red = 0x00; static int led_green = 0x26; +struct ToneportSourceInfo +{ + const char *name; + int code; +}; + +static const struct ToneportSourceInfo toneport_source_info[] = { + { "Microphone", 0x0a01 }, + { "Line" , 0x0801 }, + { "Instrument", 0x0b01 }, + { "Inst & Mic", 0x0901 } +}; + +static bool toneport_has_led(short product) +{ + return + (product == LINE6_DEVID_GUITARPORT) || + (product == LINE6_DEVID_TONEPORT_GX); + /* add your device here if you are missing support for the LEDs */ +} + static void toneport_update_led(struct device *dev) { struct usb_interface *interface = to_usb_interface(dev); @@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read, static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read, toneport_set_led_green); + static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) { int ret; @@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) return 0; } +/* monitor info callback */ +static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 256; + return 0; +} + +/* monitor get callback */ +static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = line6pcm->volume_monitor; + return 0; +} + +/* monitor put callback */ +static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + if(ucontrol->value.integer.value[0] == line6pcm->volume_monitor) + return 0; + + line6pcm->volume_monitor = ucontrol->value.integer.value[0]; + return 1; +} + +/* source info callback */ +static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const int size = ARRAY_SIZE(toneport_source_info); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = size; + + if(uinfo->value.enumerated.item >= size) + uinfo->value.enumerated.item = size - 1; + + strcpy(uinfo->value.enumerated.name, + toneport_source_info[uinfo->value.enumerated.item].name); + + return 0; +} + +/* source get callback */ +static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6; + ucontrol->value.enumerated.item[0] = toneport->source; + return 0; +} + +/* source put callback */ +static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6; + + if(ucontrol->value.enumerated.item[0] == toneport->source) + return 0; + + toneport->source = ucontrol->value.enumerated.item[0]; + toneport_send_cmd(toneport->line6.usbdev, toneport_source_info[toneport->source].code, 0x0000); + return 1; +} + +static void toneport_start_pcm(unsigned long arg) +{ + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; + struct usb_line6 *line6 = &toneport->line6; + line6_pcm_start(line6->line6pcm, MASK_PCM_MONITOR); +} + +/* control definition */ +static struct snd_kcontrol_new toneport_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_monitor_info, + .get = snd_toneport_monitor_get, + .put = snd_toneport_monitor_put +}; + +/* source selector definition */ +static struct snd_kcontrol_new toneport_control_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_source_info, + .get = snd_toneport_source_get, + .put = snd_toneport_source_put +}; + /* Toneport destructor. */ @@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface) } /* - Init Toneport device. + Setup Toneport device. */ -int toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) +static void toneport_setup(struct usb_line6_toneport *toneport) { - int err, ticks; + int ticks; struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev; + struct usb_device *usbdev = line6->usbdev; + + /* sync time on device with host: */ + ticks = (int)get_seconds(); + line6_write_data(line6, 0x80c6, &ticks, 4); + + /* enable device: */ + toneport_send_cmd(usbdev, 0x0301, 0x0000); + + /* initialize source select: */ + switch(usbdev->descriptor.idProduct) { + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_PODSTUDIO_UX1: + toneport_send_cmd(usbdev, toneport_source_info[toneport->source].code, 0x0000); + } + + if (toneport_has_led(usbdev->descriptor.idProduct)) + toneport_update_led(&usbdev->dev); +} + +/* + Try to init Toneport device. +*/ +static int toneport_try_init(struct usb_interface *interface, + struct usb_line6_toneport *toneport) +{ + int err; + struct usb_line6 *line6 = &toneport->line6; + struct usb_device *usbdev = line6->usbdev; if ((interface == NULL) || (toneport == NULL)) return -ENODEV; @@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface, /* initialize audio system: */ err = line6_init_audio(line6); if (err < 0) { - toneport_destruct(interface); return err; } /* initialize PCM subsystem: */ err = line6_init_pcm(line6, &toneport_pcm_properties); if (err < 0) { - toneport_destruct(interface); return err; } + /* register monitor control: */ + err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_monitor, line6->line6pcm)); + if (err < 0) { + return err; + } + + /* register source select control: */ + switch(usbdev->descriptor.idProduct) { + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_PODSTUDIO_UX1: + err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_source, line6->line6pcm)); + if (err < 0) { + return err; + } + } + /* register audio system: */ err = line6_register_audio(line6); if (err < 0) { - toneport_destruct(interface); return err; } - usbdev = line6->usbdev; line6_read_serial_number(line6, &toneport->serial_number); line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); - /* sync time on device with host: */ - ticks = (int)get_seconds(); - line6_write_data(line6, 0x80c6, &ticks, 4); + if (toneport_has_led(usbdev->descriptor.idProduct)) { + CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red)); + CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green)); + } - /* - 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; if (interface == NULL) return; + toneport = usb_get_intfdata(interface); + del_timer_sync(&toneport->timer); - if (toneport->line6.usbdev->descriptor.idProduct != - LINE6_DEVID_GUITARPORT) { + if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) { device_remove_file(&interface->dev, &dev_attr_led_red); device_remove_file(&interface->dev, &dev_attr_led_green); } @@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface) struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; if (line6pcm != NULL) { - unlink_wait_clear_audio_out_urbs(line6pcm); - unlink_wait_clear_audio_in_urbs(line6pcm); + line6_pcm_stop(line6pcm, MASK_PCM_MONITOR); + line6_pcm_disconnect(line6pcm); } } diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h index bddc58dd7e3..c2adee3779a 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,33 +13,44 @@ #define TONEPORT_H -#include "driver.h" - #include <linux/usb.h> #include <sound/core.h> +#include "driver.h" + struct usb_line6_toneport { /** - Generic Line6 USB data. + Generic Line6 USB data. */ struct usb_line6 line6; /** - Serial number of device. + Source selector. + */ + int source; + + /** + Serial number of device. */ int serial_number; /** - Firmware version (x 100). + Firmware version (x 100). */ int firmware_version; + + /** + Timer for delayed PCM startup. + */ + struct timer_list timer; }; -extern void toneport_disconnect(struct usb_interface *interface); -extern int toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport); +extern void line6_toneport_disconnect(struct usb_interface *interface); +extern int line6_toneport_init(struct usb_interface *interface, + struct usb_line6_toneport *toneport); +extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); #endif diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index c38f31f2f42..1e392c78202 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.0 * * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at) * @@ -25,6 +25,9 @@ #define LINE6_DEVID_BASSPODXTPRO 0x4252 #define LINE6_DEVID_GUITARPORT 0x4750 #define LINE6_DEVID_POCKETPOD 0x5051 +#define LINE6_DEVID_PODSTUDIO_GX 0x4153 +#define LINE6_DEVID_PODSTUDIO_UX1 0x4150 +#define LINE6_DEVID_PODSTUDIO_UX2 0x4151 #define LINE6_DEVID_PODX3 0x414a #define LINE6_DEVID_PODX3LIVE 0x414b #define LINE6_DEVID_PODXT 0x5044 @@ -35,20 +38,23 @@ #define LINE6_DEVID_TONEPORT_UX2 0x4142 #define LINE6_DEVID_VARIAX 0x534d -#define LINE6_BIT_BASSPODXT (1 << 0) -#define LINE6_BIT_BASSPODXTLIVE (1 << 1) -#define LINE6_BIT_BASSPODXTPRO (1 << 2) -#define LINE6_BIT_GUITARPORT (1 << 3) -#define LINE6_BIT_POCKETPOD (1 << 4) -#define LINE6_BIT_PODX3 (1 << 5) -#define LINE6_BIT_PODX3LIVE (1 << 6) -#define LINE6_BIT_PODXT (1 << 7) -#define LINE6_BIT_PODXTLIVE (1 << 8) -#define LINE6_BIT_PODXTPRO (1 << 9) -#define LINE6_BIT_TONEPORT_GX (1 << 10) -#define LINE6_BIT_TONEPORT_UX1 (1 << 11) -#define LINE6_BIT_TONEPORT_UX2 (1 << 12) -#define LINE6_BIT_VARIAX (1 << 13) +#define LINE6_BIT_BASSPODXT (1 << 0) +#define LINE6_BIT_BASSPODXTLIVE (1 << 1) +#define LINE6_BIT_BASSPODXTPRO (1 << 2) +#define LINE6_BIT_GUITARPORT (1 << 3) +#define LINE6_BIT_POCKETPOD (1 << 4) +#define LINE6_BIT_PODSTUDIO_GX (1 << 5) +#define LINE6_BIT_PODSTUDIO_UX1 (1 << 6) +#define LINE6_BIT_PODSTUDIO_UX2 (1 << 7) +#define LINE6_BIT_PODX3 (1 << 8) +#define LINE6_BIT_PODX3LIVE (1 << 9) +#define LINE6_BIT_PODXT (1 << 10) +#define LINE6_BIT_PODXTLIVE (1 << 11) +#define LINE6_BIT_PODXTPRO (1 << 12) +#define LINE6_BIT_TONEPORT_GX (1 << 13) +#define LINE6_BIT_TONEPORT_UX1 (1 << 14) +#define LINE6_BIT_TONEPORT_UX2 (1 << 15) +#define LINE6_BIT_VARIAX (1 << 16) #define LINE6_BITS_PRO (LINE6_BIT_BASSPODXTPRO | \ LINE6_BIT_PODXTPRO) @@ -66,9 +72,13 @@ #define LINE6_BIT_CONTROL (1 << 0) /* device supports PCM input/output via USB */ #define LINE6_BIT_PCM (1 << 1) -#define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM) +/* device support hardware monitoring */ +#define LINE6_BIT_HWMON (1 << 2) + +#define LINE6_BIT_CONTROL_PCM_HWMON (LINE6_BIT_CONTROL | LINE6_BIT_PCM | LINE6_BIT_HWMON) #define LINE6_FALLBACK_INTERVAL 10 #define LINE6_FALLBACK_MAXPACKETSIZE 16 + #endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index 58ddbe6393f..9f1b085aee9 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,12 +9,11 @@ * */ -#include "driver.h" - #include <linux/slab.h> #include "audio.h" #include "control.h" +#include "driver.h" #include "variax.h" @@ -26,18 +25,46 @@ #define VARIAX_OFFSET_ACTIVATE 7 +/* + This message is sent by the device during initialization and identifies + the connected guitar model. +*/ +static const char variax_init_model[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02, + 0x00 +}; + +/* + This message is sent by the device during initialization and identifies + the connected guitar version. +*/ +static const char variax_init_version[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, + 0x07, 0x00, 0x00, 0x00 +}; + +/* + This message is the last one sent by the device during initialization. +*/ +static const char variax_init_done[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b +}; + static const char variax_activate[] = { 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, 0xf7 }; + static const char variax_request_bank[] = { 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 }; + static const char variax_request_model1[] = { 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, 0x00, 0x00, 0x00, 0xf7 }; + static const char variax_request_model2[] = { 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, @@ -45,6 +72,13 @@ static const char variax_request_model2[] = { }; +/* forward declarations: */ +static int variax_create_files2(struct device *dev); +static void variax_startup2(unsigned long data); +static void variax_startup4(unsigned long data); +static void variax_startup5(unsigned long data); + + /* Decode data transmitted by workbench. */ @@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data, } } -static void variax_activate_timeout(unsigned long arg) +static void variax_activate_async(struct usb_line6_variax *variax, int a) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; - variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1; + variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate)); } /* - Send an asynchronous activation request after a given interval. + Variax startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. */ -static void variax_activate_delayed(struct usb_line6_variax *variax, - int seconds) + +static void variax_startup1(struct usb_line6_variax *variax) { - variax->activate_timer.expires = jiffies + seconds * HZ; - variax->activate_timer.function = variax_activate_timeout; - variax->activate_timer.data = (unsigned long)variax; - add_timer(&variax->activate_timer); + CHECK_STARTUP_PROGRESS(variax->startup_progress, 1); + + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY1, variax_startup2, (unsigned long)variax); } -static void variax_startup_timeout(unsigned long arg) +static void variax_startup2(unsigned long data) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6 *line6 = &variax->line6; + CHECK_STARTUP_PROGRESS(variax->startup_progress, 2); - if (variax->dumpreq.ok) - return; + /* request firmware version: */ + line6_version_request_async(line6); +} - line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); - line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout, - variax); +static void variax_startup3(struct usb_line6_variax *variax) +{ + CHECK_STARTUP_PROGRESS(variax->startup_progress, 3); + + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY3, variax_startup4, (unsigned long)variax); +} + +static void variax_startup4(unsigned long data) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + CHECK_STARTUP_PROGRESS(variax->startup_progress, 4); + + /* activate device: */ + variax_activate_async(variax, 1); + line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY4, variax_startup5, (unsigned long)variax); +} + +static void variax_startup5(unsigned long data) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + CHECK_STARTUP_PROGRESS(variax->startup_progress, 5); + + /* current model dump: */ + line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1); + /* passes 2 and 3 are performed implicitly before entering variax_startup6 */ +} + +static void variax_startup6(struct usb_line6_variax *variax) +{ + CHECK_STARTUP_PROGRESS(variax->startup_progress, 6); + + /* schedule work for global work queue: */ + schedule_work(&variax->startup_work); +} + +static void variax_startup7(struct work_struct *work) +{ + struct usb_line6_variax *variax = container_of(work, struct usb_line6_variax, startup_work); + struct usb_line6 *line6 = &variax->line6; + + CHECK_STARTUP_PROGRESS(variax->startup_progress, 7); + + /* ALSA audio interface: */ + line6_register_audio(&variax->line6); + + /* device files: */ + line6_variax_create_files(0, 0, line6->ifcdev); + variax_create_files2(line6->ifcdev); } /* Process a completely received message. */ -void variax_process_message(struct usb_line6_variax *variax) +void line6_variax_process_message(struct usb_line6_variax *variax) { const unsigned char *buf = variax->line6.buffer_message; @@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax) case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: variax->model = buf[1]; - line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); + line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1); break; case LINE6_RESET: dev_info(variax->line6.ifcdev, "VARIAX reset\n"); - variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); break; case LINE6_SYSEX_BEGIN: @@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax) case VARIAX_DUMP_PASS1: variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data, (sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2); - line6_dump_request_async(&variax->dumpreq, &variax->line6, 1); - line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2); + line6_dump_request_async(&variax->dumpreq, &variax->line6, 1, VARIAX_DUMP_PASS2); break; case VARIAX_DUMP_PASS2: @@ -141,21 +224,31 @@ void variax_process_message(struct usb_line6_variax *variax) variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2, sizeof(variax->model_data.control) / 2 * 2); - variax->dumpreq.ok = 1; - line6_dump_request_async(&variax->dumpreq, &variax->line6, 2); - line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3); + line6_dump_request_async(&variax->dumpreq, &variax->line6, 2, VARIAX_DUMP_PASS3); } } else { DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length)); line6_dump_finished(&variax->dumpreq); } } else if (memcmp(buf + 1, variax_request_bank + 1, - sizeof(variax_request_bank) - 2) == 0) { + sizeof(variax_request_bank) - 2) == 0) { memcpy(variax->bank, buf + sizeof(variax_request_bank) - 1, sizeof(variax->bank)); - variax->dumpreq.ok = 1; line6_dump_finished(&variax->dumpreq); + variax_startup6(variax); + } else if (memcmp(buf + 1, variax_init_model + 1, + sizeof(variax_init_model) - 1) == 0) { + memcpy(variax->guitar, + buf + sizeof(variax_init_model), + sizeof(variax->guitar)); + } else if (memcmp(buf + 1, variax_init_version + 1, + sizeof(variax_init_version) - 1) == 0) { + variax_startup3(variax); + } else if (memcmp(buf + 1, variax_init_done + 1, + sizeof(variax_init_done) - 1) == 0) { + /* notify of complete initialization: */ + variax_startup4((unsigned long)variax); } break; @@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev, if (ret) return ret; - variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value ? 1 : 0; - line6_send_raw_message_async(&variax->line6, variax->buffer_activate, - sizeof(variax_activate)); + variax_activate_async(variax, value ? 1 : 0); return count; } @@ -317,7 +408,7 @@ static ssize_t variax_get_name(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - line6_wait_dump(&variax->dumpreq, 0); + line6_dump_wait_interruptible(&variax->dumpreq); return get_string(buf, variax->model_data.name, sizeof(variax->model_data.name)); } @@ -329,7 +420,7 @@ static ssize_t variax_get_bank(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - line6_wait_dump(&variax->dumpreq, 0); + line6_dump_wait_interruptible(&variax->dumpreq); return get_string(buf, variax->bank, sizeof(variax->bank)); } @@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev, { struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); int retval; - retval = line6_wait_dump(&variax->dumpreq, 0); + retval = line6_dump_wait_interruptible(&variax->dumpreq); if (retval < 0) return retval; memcpy(buf, &variax->model_data.control, @@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev, return sizeof(variax->model_data.control); } -#if CREATE_RAW_FILE +/* + "read" request on "guitar" special file. +*/ +static ssize_t variax_get_guitar(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); + return sprintf(buf, "%s\n", variax->guitar); +} + +#ifdef CONFIG_LINE6_USB_RAW + +static char *variax_alloc_sysex_buffer(struct usb_line6_variax *variax, int code, int size) +{ + return line6_alloc_sysex_buffer(&variax->line6, VARIAX_SYSEX_CODE, code, size); +} /* "write" request on "raw" special file. @@ -396,8 +502,9 @@ static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write); static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write); static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write); static DEVICE_ATTR(active, S_IWUGO | S_IRUGO, variax_get_active, variax_set_active); +static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write); -#if CREATE_RAW_FILE +#ifdef CONFIG_LINE6_USB_RAW static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2); #endif @@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface) line6_dumpreq_destruct(&variax->dumpreq); kfree(variax->buffer_activate); - del_timer_sync(&variax->activate_timer); } /* @@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev) CHECK_RETURN(device_create_file(dev, &dev_attr_bank)); CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); CHECK_RETURN(device_create_file(dev, &dev_attr_active)); -#if CREATE_RAW_FILE + CHECK_RETURN(device_create_file(dev, &dev_attr_guitar)); +#ifdef CONFIG_LINE6_USB_RAW CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); CHECK_RETURN(device_create_file(dev, &dev_attr_raw2)); #endif @@ -448,23 +555,25 @@ static int variax_create_files2(struct device *dev) } /* - Init workbench device. + Try to init workbench device. */ -int variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax) +static int variax_try_init(struct usb_interface *interface, + struct usb_line6_variax *variax) { int err; if ((interface == NULL) || (variax == NULL)) return -ENODEV; + init_timer(&variax->startup_timer); + INIT_WORK(&variax->startup_work, variax_startup7); + /* initialize USB buffers: */ err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1, sizeof(variax_request_model1)); if (err < 0) { dev_err(&interface->dev, "Out of memory\n"); - variax_destruct(interface); return err; } @@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface, if (err < 0) { dev_err(&interface->dev, "Out of memory\n"); - variax_destruct(interface); return err; } @@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface, if (err < 0) { dev_err(&interface->dev, "Out of memory\n"); - variax_destruct(interface); return err; } @@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface, if (variax->buffer_activate == NULL) { dev_err(&interface->dev, "Out of memory\n"); - variax_destruct(interface); return -ENOMEM; } - init_timer(&variax->activate_timer); - - /* create sysfs entries: */ - err = variax_create_files(0, 0, &interface->dev); - if (err < 0) { - variax_destruct(interface); - return err; - } - - err = variax_create_files2(&interface->dev); - if (err < 0) { - variax_destruct(interface); - return err; - } - /* initialize audio system: */ err = line6_init_audio(&variax->line6); if (err < 0) { - variax_destruct(interface); return err; } /* initialize MIDI subsystem: */ err = line6_init_midi(&variax->line6); if (err < 0) { - variax_destruct(interface); return err; } - /* register audio system: */ - err = line6_register_audio(&variax->line6); + /* initiate startup procedure: */ + variax_startup1(variax); + return 0; +} + +/* + Init workbench device (and clean up in case of failure). +*/ +int line6_variax_init(struct usb_interface *interface, + struct usb_line6_variax *variax) +{ + int err = variax_try_init(interface, variax); + if (err < 0) { variax_destruct(interface); - return err; } - variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); - line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY, - variax_startup_timeout, variax); - return 0; + return err; } /* Workbench device disconnected. */ -void variax_disconnect(struct usb_interface *interface) +void line6_variax_disconnect(struct usb_interface *interface) { struct device *dev; @@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface) if (dev != NULL) { /* remove sysfs entries: */ - variax_remove_files(0, 0, dev); + line6_variax_remove_files(0, 0, dev); device_remove_file(dev, &dev_attr_model); device_remove_file(dev, &dev_attr_volume); device_remove_file(dev, &dev_attr_tone); @@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface) device_remove_file(dev, &dev_attr_bank); device_remove_file(dev, &dev_attr_dump); device_remove_file(dev, &dev_attr_active); -#if CREATE_RAW_FILE + device_remove_file(dev, &dev_attr_guitar); +#ifdef CONFIG_LINE6_USB_RAW device_remove_file(dev, &dev_attr_raw); device_remove_file(dev, &dev_attr_raw2); #endif diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index ee330ba3089..12cb5f21706 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.0 * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,19 +13,18 @@ #define VARIAX_H -#include "driver.h" - #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/wait.h> - #include <sound/core.h> +#include "driver.h" #include "dumprequest.h" -#define VARIAX_ACTIVATE_DELAY 10 -#define VARIAX_STARTUP_DELAY 3 +#define VARIAX_STARTUP_DELAY1 1000 +#define VARIAX_STARTUP_DELAY3 100 +#define VARIAX_STARTUP_DELAY4 100 enum { @@ -36,73 +35,88 @@ enum { /** - Binary Variax model dump + Binary Variax model dump */ struct variax_model { /** - Header information (including program name). + Header information (including program name). */ unsigned char name[18]; /** - Model parameters. + Model parameters. */ unsigned char control[78 * 2]; }; struct usb_line6_variax { /** - Generic Line6 USB data. + Generic Line6 USB data. */ struct usb_line6 line6; /** - Dump request structure. - Append two extra buffers for 3-pass data query. + Dump request structure. + Append two extra buffers for 3-pass data query. */ struct line6_dump_request dumpreq; struct line6_dump_reqbuf extrabuf[2]; /** - Buffer for activation code. + Buffer for activation code. */ unsigned char *buffer_activate; /** - Model number. + Model number. */ int model; /** - Current model settings. + Current model settings. */ struct variax_model model_data; /** - Name of current model bank. + Name of connected guitar. + */ + unsigned char guitar[18]; + + /** + Name of current model bank. */ unsigned char bank[18]; /** - Position of volume dial. + Position of volume dial. */ int volume; /** - Position of tone control dial. + Position of tone control dial. */ int tone; /** - Timer for delayed activation request. + Handler for device initializaton. + */ + struct work_struct startup_work; + + /** + Timer for device initializaton. + */ + struct timer_list startup_timer; + + /** + Current progress in startup procedure. */ - 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 |