Loading drivers/staging/line6/Kconfig +65 −2 Original line number Original line Diff line number Diff line config LINE6_USB menuconfig LINE6_USB tristate "Line6 USB support" tristate "Line6 USB support" depends on USB && SND depends on USB && SND select SND_RAWMIDI select SND_RAWMIDI Loading @@ -18,5 +18,68 @@ config LINE6_USB * Signal routing (record clean/processed guitar signal, * Signal routing (record clean/processed guitar signal, re-amping) 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 drivers/staging/line6/audio.c +7 −6 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -9,12 +9,12 @@ * * */ */ #include "driver.h" #include "audio.h" #include <sound/core.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/initval.h> #include "driver.h" #include "audio.h" static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; Loading @@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6) line6->card = card; line6->card = card; strcpy(card->id, line6->properties->id); strcpy(card->driver, DRIVER_NAME); 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, sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ return 0; return 0; Loading drivers/staging/line6/audio.h +2 −2 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading drivers/staging/line6/capture.c +117 −113 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -9,27 +9,24 @@ * * */ */ #include "driver.h" #include <linux/slab.h> #include <sound/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/pcm_params.h> #include "audio.h" #include "audio.h" #include "capture.h" #include "driver.h" #include "pcm.h" #include "pcm.h" #include "pod.h" #include "pod.h" #include "capture.h" /* /* Find a free URB and submit it. 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; unsigned long flags; struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int i, urb_size; int i, urb_size; struct urb *urb_in; struct urb *urb_in; Loading @@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) index = index = find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); 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); 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; return -EINVAL; } } Loading @@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) line6pcm->buffer_in + line6pcm->buffer_in + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_in->transfer_buffer_length = urb_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) if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0) set_bit(index, &line6pcm->active_urb_in); set_bit(index, &line6pcm->active_urb_in); else else dev_err(s2m(substream), "URB in #%d submission failed\n", dev_err(line6pcm->line6->ifcdev, index); "URB in #%d submission failed\n", index); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); return 0; return 0; Loading @@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) /* /* Submit all currently available capture URBs. 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; int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { ret = submit_audio_in_urb(substream); ret = submit_audio_in_urb(line6pcm); if (ret < 0) if (ret < 0) return ret; return ret; } } Loading @@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) /* /* Unlink all currently active capture URBs. 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; unsigned int i; Loading Loading @@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); } while (--timeout > 0); if (alive) if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", 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. 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); 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. Callback for completed capture URB. */ */ static void audio_in_callback(struct urb *urb) static void audio_in_callback(struct urb *urb) { { int i, index, length = 0, shutdown = 0; int i, index, length = 0, shutdown = 0; int frames; unsigned long flags; unsigned long flags; struct snd_pcm_substream *substream = struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; (struct snd_pcm_substream *)urb->context; struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); line6pcm->last_frame_in = urb->start_frame; const int bytes_per_frame = line6pcm->properties->bytes_per_frame; struct snd_pcm_runtime *runtime = substream->runtime; /* find index of URB */ /* find index of URB */ for (index = 0; index < LINE6_ISO_BUFFERS; ++index) for (index = 0; index < LINE6_ISO_BUFFERS; ++index) if (urb == line6pcm->urb_audio_in[index]) if (urb == line6pcm->urb_audio_in[index]) break; break; #if DO_DUMP_PCM_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_PCM for (i = 0; i < LINE6_ISO_PACKETS; ++i) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) { struct usb_iso_packet_descriptor *fout = struct usb_iso_packet_descriptor *fout = &urb->iso_frame_desc[i]; &urb->iso_frame_desc[i]; Loading @@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb) fbuf = urb->transfer_buffer + fin->offset; fbuf = urb->transfer_buffer + fin->offset; fsize = fin->actual_length; fsize = fin->actual_length; length += fsize; if (fsize > 0) { if (fsize > line6pcm->max_packet_size) { frames = fsize / bytes_per_frame; 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 > length += fsize; 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) { /* the following assumes LINE6_ISO_PACKETS == 1: */ memcpy(runtime->dma_area + #if LINE6_BACKUP_MONITOR_SIGNAL line6pcm->pos_in_done * memcpy(line6pcm->prev_fbuf, fbuf, fsize); bytes_per_frame, fbuf, #else len * bytes_per_frame); line6pcm->prev_fbuf = fbuf; memcpy(runtime->dma_area, #endif fbuf + len * bytes_per_frame, line6pcm->prev_fsize = fsize; (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); } line6pcm->pos_in_done += frames; #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE if (line6pcm->pos_in_done >= runtime->buffer_size) if (!(line6pcm->flags & MASK_PCM_IMPULSE)) line6pcm->pos_in_done -= runtime->buffer_size; #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); 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; shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); if (!shutdown) { if (!shutdown) { submit_audio_in_urb(substream); submit_audio_in_urb(line6pcm); line6pcm->bytes_in += length; if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)) if (line6pcm->bytes_in >= line6pcm->period_in) { line6_capture_check_period(line6pcm, length); line6pcm->bytes_in -= line6pcm->period_in; snd_pcm_period_elapsed(substream); } } } } } Loading Loading @@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, return ret; return ret; line6pcm->period_in = params_period_bytes(hw_params); 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; return 0; } } /* hw_free capture callback */ /* hw_free capture callback */ static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) { { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); unlink_wait_clear_audio_in_urbs(line6pcm); kfree(line6pcm->buffer_in); line6pcm->buffer_in = NULL; return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream); } } /* trigger callback */ /* 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; int err; line6pcm->count_in = 0; switch (cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START: if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { #ifdef CONFIG_PM err = submit_audio_in_all_urbs(substream); case SNDRV_PCM_TRIGGER_RESUME: #endif err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE); if (err < 0) { if (err < 0) clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags); return err; return err; } } break; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP: if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) #ifdef CONFIG_PM unlink_audio_in_urbs(line6pcm); case SNDRV_PCM_TRIGGER_SUSPEND: #endif err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE); if (err < 0) return err; break; break; Loading Loading @@ -372,7 +376,7 @@ struct snd_pcm_ops snd_line6_capture_ops = { .pointer = snd_line6_capture_pointer, .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; int i; Loading drivers/staging/line6/capture.h +11 −10 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -13,20 +13,21 @@ #define CAPTURE_H #define CAPTURE_H #include "driver.h" #include <sound/pcm.h> #include <sound/pcm.h> #include "driver.h" #include "pcm.h" #include "pcm.h" extern struct snd_pcm_ops snd_line6_capture_ops; extern struct snd_pcm_ops snd_line6_capture_ops; extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); int fsize); extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); int cmd); extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); extern void unlink_wait_clear_audio_in_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 #endif Loading
drivers/staging/line6/Kconfig +65 −2 Original line number Original line Diff line number Diff line config LINE6_USB menuconfig LINE6_USB tristate "Line6 USB support" tristate "Line6 USB support" depends on USB && SND depends on USB && SND select SND_RAWMIDI select SND_RAWMIDI Loading @@ -18,5 +18,68 @@ config LINE6_USB * Signal routing (record clean/processed guitar signal, * Signal routing (record clean/processed guitar signal, re-amping) 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
drivers/staging/line6/audio.c +7 −6 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -9,12 +9,12 @@ * * */ */ #include "driver.h" #include "audio.h" #include <sound/core.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/initval.h> #include "driver.h" #include "audio.h" static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; Loading @@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6) line6->card = card; line6->card = card; strcpy(card->id, line6->properties->id); strcpy(card->driver, DRIVER_NAME); 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, sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ return 0; return 0; Loading
drivers/staging/line6/audio.h +2 −2 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading
drivers/staging/line6/capture.c +117 −113 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -9,27 +9,24 @@ * * */ */ #include "driver.h" #include <linux/slab.h> #include <sound/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/pcm_params.h> #include "audio.h" #include "audio.h" #include "capture.h" #include "driver.h" #include "pcm.h" #include "pcm.h" #include "pod.h" #include "pod.h" #include "capture.h" /* /* Find a free URB and submit it. 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; unsigned long flags; struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int i, urb_size; int i, urb_size; struct urb *urb_in; struct urb *urb_in; Loading @@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) index = index = find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); 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); 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; return -EINVAL; } } Loading @@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) line6pcm->buffer_in + line6pcm->buffer_in + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_in->transfer_buffer_length = urb_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) if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0) set_bit(index, &line6pcm->active_urb_in); set_bit(index, &line6pcm->active_urb_in); else else dev_err(s2m(substream), "URB in #%d submission failed\n", dev_err(line6pcm->line6->ifcdev, index); "URB in #%d submission failed\n", index); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); return 0; return 0; Loading @@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) /* /* Submit all currently available capture URBs. 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; int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { ret = submit_audio_in_urb(substream); ret = submit_audio_in_urb(line6pcm); if (ret < 0) if (ret < 0) return ret; return ret; } } Loading @@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) /* /* Unlink all currently active capture URBs. 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; unsigned int i; Loading Loading @@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); } while (--timeout > 0); if (alive) if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", 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. 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); 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. Callback for completed capture URB. */ */ static void audio_in_callback(struct urb *urb) static void audio_in_callback(struct urb *urb) { { int i, index, length = 0, shutdown = 0; int i, index, length = 0, shutdown = 0; int frames; unsigned long flags; unsigned long flags; struct snd_pcm_substream *substream = struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; (struct snd_pcm_substream *)urb->context; struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); line6pcm->last_frame_in = urb->start_frame; const int bytes_per_frame = line6pcm->properties->bytes_per_frame; struct snd_pcm_runtime *runtime = substream->runtime; /* find index of URB */ /* find index of URB */ for (index = 0; index < LINE6_ISO_BUFFERS; ++index) for (index = 0; index < LINE6_ISO_BUFFERS; ++index) if (urb == line6pcm->urb_audio_in[index]) if (urb == line6pcm->urb_audio_in[index]) break; break; #if DO_DUMP_PCM_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_PCM for (i = 0; i < LINE6_ISO_PACKETS; ++i) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) { struct usb_iso_packet_descriptor *fout = struct usb_iso_packet_descriptor *fout = &urb->iso_frame_desc[i]; &urb->iso_frame_desc[i]; Loading @@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb) fbuf = urb->transfer_buffer + fin->offset; fbuf = urb->transfer_buffer + fin->offset; fsize = fin->actual_length; fsize = fin->actual_length; length += fsize; if (fsize > 0) { if (fsize > line6pcm->max_packet_size) { frames = fsize / bytes_per_frame; 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 > length += fsize; 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) { /* the following assumes LINE6_ISO_PACKETS == 1: */ memcpy(runtime->dma_area + #if LINE6_BACKUP_MONITOR_SIGNAL line6pcm->pos_in_done * memcpy(line6pcm->prev_fbuf, fbuf, fsize); bytes_per_frame, fbuf, #else len * bytes_per_frame); line6pcm->prev_fbuf = fbuf; memcpy(runtime->dma_area, #endif fbuf + len * bytes_per_frame, line6pcm->prev_fsize = fsize; (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); } line6pcm->pos_in_done += frames; #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE if (line6pcm->pos_in_done >= runtime->buffer_size) if (!(line6pcm->flags & MASK_PCM_IMPULSE)) line6pcm->pos_in_done -= runtime->buffer_size; #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); 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; shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); if (!shutdown) { if (!shutdown) { submit_audio_in_urb(substream); submit_audio_in_urb(line6pcm); line6pcm->bytes_in += length; if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)) if (line6pcm->bytes_in >= line6pcm->period_in) { line6_capture_check_period(line6pcm, length); line6pcm->bytes_in -= line6pcm->period_in; snd_pcm_period_elapsed(substream); } } } } } Loading Loading @@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, return ret; return ret; line6pcm->period_in = params_period_bytes(hw_params); 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; return 0; } } /* hw_free capture callback */ /* hw_free capture callback */ static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) { { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); unlink_wait_clear_audio_in_urbs(line6pcm); kfree(line6pcm->buffer_in); line6pcm->buffer_in = NULL; return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream); } } /* trigger callback */ /* 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; int err; line6pcm->count_in = 0; switch (cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START: if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { #ifdef CONFIG_PM err = submit_audio_in_all_urbs(substream); case SNDRV_PCM_TRIGGER_RESUME: #endif err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE); if (err < 0) { if (err < 0) clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags); return err; return err; } } break; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP: if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) #ifdef CONFIG_PM unlink_audio_in_urbs(line6pcm); case SNDRV_PCM_TRIGGER_SUSPEND: #endif err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE); if (err < 0) return err; break; break; Loading Loading @@ -372,7 +376,7 @@ struct snd_pcm_ops snd_line6_capture_ops = { .pointer = snd_line6_capture_pointer, .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; int i; Loading
drivers/staging/line6/capture.h +11 −10 Original line number Original line 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 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as Loading @@ -13,20 +13,21 @@ #define CAPTURE_H #define CAPTURE_H #include "driver.h" #include <sound/pcm.h> #include <sound/pcm.h> #include "driver.h" #include "pcm.h" #include "pcm.h" extern struct snd_pcm_ops snd_line6_capture_ops; extern struct snd_pcm_ops snd_line6_capture_ops; extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); int fsize); extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); int cmd); extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); extern void unlink_wait_clear_audio_in_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 #endif