Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1027f476 authored by Markus Grabner's avatar Markus Grabner Committed by Greg Kroah-Hartman
Browse files

staging: line6: sync with upstream



Big upstream sync.

Signed-off-by: default avatarMarkus Grabner <grabner@icg.tugraz.at>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4498dbcd
Loading
Loading
Loading
Loading
+65 −2
Original line number Diff line number Diff line
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
+7 −6
Original line number Diff line number Diff line
/*
 * 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;
+2 −2
Original line number Diff line number Diff line
/*
 * 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
+117 −113
Original line number Diff line number Diff line
/*
 * 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);
}

/*
	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;
		length += fsize;

		if (fsize > 0) {
			frames = fsize / bytes_per_frame;
		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);
		}

			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;
		length += fsize;

				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);
		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;

@@ -372,7 +376,7 @@ struct snd_pcm_ops snd_line6_capture_ops = {
	.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;

+11 −10
Original line number Diff line number Diff line
/*
 * 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
Loading