From bc700ab1407864ebee838de53c9565a394f4da38 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Mar 2010 19:46:11 +0100 Subject: ALSA: usb-audio: move ua101 driver As part of the USB audio code cleanup, move the non-standard ua101 driver out of the way. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 5 +- sound/usb/misc/Makefile | 2 + sound/usb/misc/ua101.c | 1387 +++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/ua101.c | 1387 ----------------------------------------------- 4 files changed, 1391 insertions(+), 1390 deletions(-) create mode 100644 sound/usb/misc/Makefile create mode 100644 sound/usb/misc/ua101.c delete mode 100644 sound/usb/ua101.c (limited to 'sound') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 5bf64ae..b0e5597 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -4,12 +4,11 @@ snd-usb-audio-objs := usbaudio.o usbmixer.o snd-usb-lib-objs := usbmidi.o -snd-ua101-objs := ua101.o # Toplevel Module Dependency obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o -obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o +obj-$(CONFIG_SND_USB_UA101) += snd-usb-lib.o obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o -obj-$(CONFIG_SND) += usx2y/ caiaq/ +obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ diff --git a/sound/usb/misc/Makefile b/sound/usb/misc/Makefile new file mode 100644 index 0000000..ccefd81 --- /dev/null +++ b/sound/usb/misc/Makefile @@ -0,0 +1,2 @@ +snd-ua101-objs := ua101.o +obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c new file mode 100644 index 0000000..e9b0ae5 --- /dev/null +++ b/sound/usb/misc/ua101.c @@ -0,0 +1,1387 @@ +/* + * Edirol UA-101/UA-1000 driver + * Copyright (c) Clemens Ladisch + * + * This driver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../usbaudio.h" + +MODULE_DESCRIPTION("Edirol UA-101/1000 driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101},{Edirol,UA-1000}}"); + +/* + * Should not be lower than the minimum scheduling delay of the host + * controller. Some Intel controllers need more than one frame; as long as + * that driver doesn't tell us about this, use 1.5 frames just to be sure. + */ +#define MIN_QUEUE_LENGTH 12 +/* Somewhat random. */ +#define MAX_QUEUE_LENGTH 30 +/* + * This magic value optimizes memory usage efficiency for the UA-101's packet + * sizes at all sample rates, taking into account the stupid cache pool sizes + * that usb_buffer_alloc() uses. + */ +#define DEFAULT_QUEUE_LENGTH 21 + +#define MAX_PACKET_SIZE 672 /* hardware specific */ +#define MAX_MEMORY_BUFFERS DIV_ROUND_UP(MAX_QUEUE_LENGTH, \ + PAGE_SIZE / MAX_PACKET_SIZE) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static unsigned int queue_length = 21; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); +module_param(queue_length, uint, 0644); +MODULE_PARM_DESC(queue_length, "USB queue length in microframes, " + __stringify(MIN_QUEUE_LENGTH)"-"__stringify(MAX_QUEUE_LENGTH)); + +enum { + INTF_PLAYBACK, + INTF_CAPTURE, + INTF_MIDI, + + INTF_COUNT +}; + +/* bits in struct ua101::states */ +enum { + USB_CAPTURE_RUNNING, + USB_PLAYBACK_RUNNING, + ALSA_CAPTURE_OPEN, + ALSA_PLAYBACK_OPEN, + ALSA_CAPTURE_RUNNING, + ALSA_PLAYBACK_RUNNING, + CAPTURE_URB_COMPLETED, + PLAYBACK_URB_COMPLETED, + DISCONNECTED, +}; + +struct ua101 { + struct usb_device *dev; + struct snd_card *card; + struct usb_interface *intf[INTF_COUNT]; + int card_index; + struct snd_pcm *pcm; + struct list_head midi_list; + u64 format_bit; + unsigned int rate; + unsigned int packets_per_second; + spinlock_t lock; + struct mutex mutex; + unsigned long states; + + /* FIFO to synchronize playback rate to capture rate */ + unsigned int rate_feedback_start; + unsigned int rate_feedback_count; + u8 rate_feedback[MAX_QUEUE_LENGTH]; + + struct list_head ready_playback_urbs; + struct tasklet_struct playback_tasklet; + wait_queue_head_t alsa_capture_wait; + wait_queue_head_t rate_feedback_wait; + wait_queue_head_t alsa_playback_wait; + struct ua101_stream { + struct snd_pcm_substream *substream; + unsigned int usb_pipe; + unsigned int channels; + unsigned int frame_bytes; + unsigned int max_packet_bytes; + unsigned int period_pos; + unsigned int buffer_pos; + unsigned int queue_length; + struct ua101_urb { + struct urb urb; + struct usb_iso_packet_descriptor iso_frame_desc[1]; + struct list_head ready_list; + } *urbs[MAX_QUEUE_LENGTH]; + struct { + unsigned int size; + void *addr; + dma_addr_t dma; + } buffers[MAX_MEMORY_BUFFERS]; + } capture, playback; +}; + +static DEFINE_MUTEX(devices_mutex); +static unsigned int devices_used; +static struct usb_driver ua101_driver; + +static void abort_alsa_playback(struct ua101 *ua); +static void abort_alsa_capture(struct ua101 *ua); + +static const char *usb_error_string(int err) +{ + switch (err) { + case -ENODEV: + return "no device"; + case -ENOENT: + return "endpoint not enabled"; + case -EPIPE: + return "endpoint stalled"; + case -ENOSPC: + return "not enough bandwidth"; + case -ESHUTDOWN: + return "device disabled"; + case -EHOSTUNREACH: + return "device suspended"; + case -EINVAL: + case -EAGAIN: + case -EFBIG: + case -EMSGSIZE: + return "internal error"; + default: + return "unknown error"; + } +} + +static void abort_usb_capture(struct ua101 *ua) +{ + if (test_and_clear_bit(USB_CAPTURE_RUNNING, &ua->states)) { + wake_up(&ua->alsa_capture_wait); + wake_up(&ua->rate_feedback_wait); + } +} + +static void abort_usb_playback(struct ua101 *ua) +{ + if (test_and_clear_bit(USB_PLAYBACK_RUNNING, &ua->states)) + wake_up(&ua->alsa_playback_wait); +} + +static void playback_urb_complete(struct urb *usb_urb) +{ + struct ua101_urb *urb = (struct ua101_urb *)usb_urb; + struct ua101 *ua = urb->urb.context; + unsigned long flags; + + if (unlikely(urb->urb.status == -ENOENT || /* unlinked */ + urb->urb.status == -ENODEV || /* device removed */ + urb->urb.status == -ECONNRESET || /* unlinked */ + urb->urb.status == -ESHUTDOWN)) { /* device disabled */ + abort_usb_playback(ua); + abort_alsa_playback(ua); + return; + } + + if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) { + /* append URB to FIFO */ + spin_lock_irqsave(&ua->lock, flags); + list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); + if (ua->rate_feedback_count > 0) + tasklet_schedule(&ua->playback_tasklet); + ua->playback.substream->runtime->delay -= + urb->urb.iso_frame_desc[0].length / + ua->playback.frame_bytes; + spin_unlock_irqrestore(&ua->lock, flags); + } +} + +static void first_playback_urb_complete(struct urb *urb) +{ + struct ua101 *ua = urb->context; + + urb->complete = playback_urb_complete; + playback_urb_complete(urb); + + set_bit(PLAYBACK_URB_COMPLETED, &ua->states); + wake_up(&ua->alsa_playback_wait); +} + +/* copy data from the ALSA ring buffer into the URB buffer */ +static bool copy_playback_data(struct ua101_stream *stream, struct urb *urb, + unsigned int frames) +{ + struct snd_pcm_runtime *runtime; + unsigned int frame_bytes, frames1; + const u8 *source; + + runtime = stream->substream->runtime; + frame_bytes = stream->frame_bytes; + source = runtime->dma_area + stream->buffer_pos * frame_bytes; + if (stream->buffer_pos + frames <= runtime->buffer_size) { + memcpy(urb->transfer_buffer, source, frames * frame_bytes); + } else { + /* wrap around at end of ring buffer */ + frames1 = runtime->buffer_size - stream->buffer_pos; + memcpy(urb->transfer_buffer, source, frames1 * frame_bytes); + memcpy(urb->transfer_buffer + frames1 * frame_bytes, + runtime->dma_area, (frames - frames1) * frame_bytes); + } + + stream->buffer_pos += frames; + if (stream->buffer_pos >= runtime->buffer_size) + stream->buffer_pos -= runtime->buffer_size; + stream->period_pos += frames; + if (stream->period_pos >= runtime->period_size) { + stream->period_pos -= runtime->period_size; + return true; + } + return false; +} + +static inline void add_with_wraparound(struct ua101 *ua, + unsigned int *value, unsigned int add) +{ + *value += add; + if (*value >= ua->playback.queue_length) + *value -= ua->playback.queue_length; +} + +static void playback_tasklet(unsigned long data) +{ + struct ua101 *ua = (void *)data; + unsigned long flags; + unsigned int frames; + struct ua101_urb *urb; + bool do_period_elapsed = false; + int err; + + if (unlikely(!test_bit(USB_PLAYBACK_RUNNING, &ua->states))) + return; + + /* + * Synchronizing the playback rate to the capture rate is done by using + * the same sequence of packet sizes for both streams. + * Submitting a playback URB therefore requires both a ready URB and + * the size of the corresponding capture packet, i.e., both playback + * and capture URBs must have been completed. Since the USB core does + * not guarantee that playback and capture complete callbacks are + * called alternately, we use two FIFOs for packet sizes and read URBs; + * submitting playback URBs is possible as long as both FIFOs are + * nonempty. + */ + spin_lock_irqsave(&ua->lock, flags); + while (ua->rate_feedback_count > 0 && + !list_empty(&ua->ready_playback_urbs)) { + /* take packet size out of FIFO */ + frames = ua->rate_feedback[ua->rate_feedback_start]; + add_with_wraparound(ua, &ua->rate_feedback_start, 1); + ua->rate_feedback_count--; + + /* take URB out of FIFO */ + urb = list_first_entry(&ua->ready_playback_urbs, + struct ua101_urb, ready_list); + list_del(&urb->ready_list); + + /* fill packet with data or silence */ + urb->urb.iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + do_period_elapsed |= copy_playback_data(&ua->playback, + &urb->urb, + frames); + else + memset(urb->urb.transfer_buffer, 0, + urb->urb.iso_frame_desc[0].length); + + /* and off you go ... */ + err = usb_submit_urb(&urb->urb, GFP_ATOMIC); + if (unlikely(err < 0)) { + spin_unlock_irqrestore(&ua->lock, flags); + abort_usb_playback(ua); + abort_alsa_playback(ua); + dev_err(&ua->dev->dev, "USB request error %d: %s\n", + err, usb_error_string(err)); + return; + } + ua->playback.substream->runtime->delay += frames; + } + spin_unlock_irqrestore(&ua->lock, flags); + if (do_period_elapsed) + snd_pcm_period_elapsed(ua->playback.substream); +} + +/* copy data from the URB buffer into the ALSA ring buffer */ +static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, + unsigned int frames) +{ + struct snd_pcm_runtime *runtime; + unsigned int frame_bytes, frames1; + u8 *dest; + + runtime = stream->substream->runtime; + frame_bytes = stream->frame_bytes; + dest = runtime->dma_area + stream->buffer_pos * frame_bytes; + if (stream->buffer_pos + frames <= runtime->buffer_size) { + memcpy(dest, urb->transfer_buffer, frames * frame_bytes); + } else { + /* wrap around at end of ring buffer */ + frames1 = runtime->buffer_size - stream->buffer_pos; + memcpy(dest, urb->transfer_buffer, frames1 * frame_bytes); + memcpy(runtime->dma_area, + urb->transfer_buffer + frames1 * frame_bytes, + (frames - frames1) * frame_bytes); + } + + stream->buffer_pos += frames; + if (stream->buffer_pos >= runtime->buffer_size) + stream->buffer_pos -= runtime->buffer_size; + stream->period_pos += frames; + if (stream->period_pos >= runtime->period_size) { + stream->period_pos -= runtime->period_size; + return true; + } + return false; +} + +static void capture_urb_complete(struct urb *urb) +{ + struct ua101 *ua = urb->context; + struct ua101_stream *stream = &ua->capture; + unsigned long flags; + unsigned int frames, write_ptr; + bool do_period_elapsed; + int err; + + if (unlikely(urb->status == -ENOENT || /* unlinked */ + urb->status == -ENODEV || /* device removed */ + urb->status == -ECONNRESET || /* unlinked */ + urb->status == -ESHUTDOWN)) /* device disabled */ + goto stream_stopped; + + if (urb->status >= 0 && urb->iso_frame_desc[0].status >= 0) + frames = urb->iso_frame_desc[0].actual_length / + stream->frame_bytes; + else + frames = 0; + + spin_lock_irqsave(&ua->lock, flags); + + if (frames > 0 && test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) + do_period_elapsed = copy_capture_data(stream, urb, frames); + else + do_period_elapsed = false; + + if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err < 0)) { + spin_unlock_irqrestore(&ua->lock, flags); + dev_err(&ua->dev->dev, "USB request error %d: %s\n", + err, usb_error_string(err)); + goto stream_stopped; + } + + /* append packet size to FIFO */ + write_ptr = ua->rate_feedback_start; + add_with_wraparound(ua, &write_ptr, ua->rate_feedback_count); + ua->rate_feedback[write_ptr] = frames; + if (ua->rate_feedback_count < ua->playback.queue_length) { + ua->rate_feedback_count++; + if (ua->rate_feedback_count == + ua->playback.queue_length) + wake_up(&ua->rate_feedback_wait); + } else { + /* + * Ring buffer overflow; this happens when the playback + * stream is not running. Throw away the oldest entry, + * so that the playback stream, when it starts, sees + * the most recent packet sizes. + */ + add_with_wraparound(ua, &ua->rate_feedback_start, 1); + } + if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) && + !list_empty(&ua->ready_playback_urbs)) + tasklet_schedule(&ua->playback_tasklet); + } + + spin_unlock_irqrestore(&ua->lock, flags); + + if (do_period_elapsed) + snd_pcm_period_elapsed(stream->substream); + + return; + +stream_stopped: + abort_usb_playback(ua); + abort_usb_capture(ua); + abort_alsa_playback(ua); + abort_alsa_capture(ua); +} + +static void first_capture_urb_complete(struct urb *urb) +{ + struct ua101 *ua = urb->context; + + urb->complete = capture_urb_complete; + capture_urb_complete(urb); + + set_bit(CAPTURE_URB_COMPLETED, &ua->states); + wake_up(&ua->alsa_capture_wait); +} + +static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream) +{ + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) { + int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL); + if (err < 0) { + dev_err(&ua->dev->dev, "USB request error %d: %s\n", + err, usb_error_string(err)); + return err; + } + } + return 0; +} + +static void kill_stream_urbs(struct ua101_stream *stream) +{ + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) + usb_kill_urb(&stream->urbs[i]->urb); +} + +static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index) +{ + struct usb_host_interface *alts; + + alts = ua->intf[intf_index]->cur_altsetting; + if (alts->desc.bAlternateSetting != 1) { + int err = usb_set_interface(ua->dev, + alts->desc.bInterfaceNumber, 1); + if (err < 0) { + dev_err(&ua->dev->dev, + "cannot initialize interface; error %d: %s\n", + err, usb_error_string(err)); + return err; + } + } + return 0; +} + +static void disable_iso_interface(struct ua101 *ua, unsigned int intf_index) +{ + struct usb_host_interface *alts; + + alts = ua->intf[intf_index]->cur_altsetting; + if (alts->desc.bAlternateSetting != 0) { + int err = usb_set_interface(ua->dev, + alts->desc.bInterfaceNumber, 0); + if (err < 0 && !test_bit(DISCONNECTED, &ua->states)) + dev_warn(&ua->dev->dev, + "interface reset failed; error %d: %s\n", + err, usb_error_string(err)); + } +} + +static void stop_usb_capture(struct ua101 *ua) +{ + clear_bit(USB_CAPTURE_RUNNING, &ua->states); + + kill_stream_urbs(&ua->capture); + + disable_iso_interface(ua, INTF_CAPTURE); +} + +static int start_usb_capture(struct ua101 *ua) +{ + int err; + + if (test_bit(DISCONNECTED, &ua->states)) + return -ENODEV; + + if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) + return 0; + + kill_stream_urbs(&ua->capture); + + err = enable_iso_interface(ua, INTF_CAPTURE); + if (err < 0) + return err; + + clear_bit(CAPTURE_URB_COMPLETED, &ua->states); + ua->capture.urbs[0]->urb.complete = first_capture_urb_complete; + ua->rate_feedback_start = 0; + ua->rate_feedback_count = 0; + + set_bit(USB_CAPTURE_RUNNING, &ua->states); + err = submit_stream_urbs(ua, &ua->capture); + if (err < 0) + stop_usb_capture(ua); + return err; +} + +static void stop_usb_playback(struct ua101 *ua) +{ + clear_bit(USB_PLAYBACK_RUNNING, &ua->states); + + kill_stream_urbs(&ua->playback); + + tasklet_kill(&ua->playback_tasklet); + + disable_iso_interface(ua, INTF_PLAYBACK); +} + +static int start_usb_playback(struct ua101 *ua) +{ + unsigned int i, frames; + struct urb *urb; + int err = 0; + + if (test_bit(DISCONNECTED, &ua->states)) + return -ENODEV; + + if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) + return 0; + + kill_stream_urbs(&ua->playback); + tasklet_kill(&ua->playback_tasklet); + + err = enable_iso_interface(ua, INTF_PLAYBACK); + if (err < 0) + return err; + + clear_bit(PLAYBACK_URB_COMPLETED, &ua->states); + ua->playback.urbs[0]->urb.complete = + first_playback_urb_complete; + spin_lock_irq(&ua->lock); + INIT_LIST_HEAD(&ua->ready_playback_urbs); + spin_unlock_irq(&ua->lock); + + /* + * We submit the initial URBs all at once, so we have to wait for the + * packet size FIFO to be full. + */ + wait_event(ua->rate_feedback_wait, + ua->rate_feedback_count >= ua->playback.queue_length || + !test_bit(USB_CAPTURE_RUNNING, &ua->states) || + test_bit(DISCONNECTED, &ua->states)); + if (test_bit(DISCONNECTED, &ua->states)) { + stop_usb_playback(ua); + return -ENODEV; + } + if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) { + stop_usb_playback(ua); + return -EIO; + } + + for (i = 0; i < ua->playback.queue_length; ++i) { + /* all initial URBs contain silence */ + spin_lock_irq(&ua->lock); + frames = ua->rate_feedback[ua->rate_feedback_start]; + add_with_wraparound(ua, &ua->rate_feedback_start, 1); + ua->rate_feedback_count--; + spin_unlock_irq(&ua->lock); + urb = &ua->playback.urbs[i]->urb; + urb->iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + memset(urb->transfer_buffer, 0, + urb->iso_frame_desc[0].length); + } + + set_bit(USB_PLAYBACK_RUNNING, &ua->states); + err = submit_stream_urbs(ua, &ua->playback); + if (err < 0) + stop_usb_playback(ua); + return err; +} + +static void abort_alsa_capture(struct ua101 *ua) +{ + if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) + snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN); +} + +static void abort_alsa_playback(struct ua101 *ua) +{ + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN); +} + +static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, + unsigned int channels) +{ + int err; + + substream->runtime->hw.info = + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_FIFO_IN_FRAMES; + substream->runtime->hw.formats = ua->format_bit; + substream->runtime->hw.rates = snd_pcm_rate_to_rate_bit(ua->rate); + substream->runtime->hw.rate_min = ua->rate; + substream->runtime->hw.rate_max = ua->rate; + substream->runtime->hw.channels_min = channels; + substream->runtime->hw.channels_max = channels; + substream->runtime->hw.buffer_bytes_max = 45000 * 1024; + substream->runtime->hw.period_bytes_min = 1; + substream->runtime->hw.period_bytes_max = UINT_MAX; + substream->runtime->hw.periods_min = 2; + substream->runtime->hw.periods_max = UINT_MAX; + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 1500000 / ua->packets_per_second, + 8192000); + if (err < 0) + return err; + err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); + return err; +} + +static int capture_pcm_open(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + int err; + + ua->capture.substream = substream; + err = set_stream_hw(ua, substream, ua->capture.channels); + if (err < 0) + return err; + substream->runtime->hw.fifo_size = + DIV_ROUND_CLOSEST(ua->rate, ua->packets_per_second); + substream->runtime->delay = substream->runtime->hw.fifo_size; + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + if (err >= 0) + set_bit(ALSA_CAPTURE_OPEN, &ua->states); + mutex_unlock(&ua->mutex); + return err; +} + +static int playback_pcm_open(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + int err; + + ua->playback.substream = substream; + err = set_stream_hw(ua, substream, ua->playback.channels); + if (err < 0) + return err; + substream->runtime->hw.fifo_size = + DIV_ROUND_CLOSEST(ua->rate * ua->playback.queue_length, + ua->packets_per_second); + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + if (err < 0) + goto error; + err = start_usb_playback(ua); + if (err < 0) { + if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states)) + stop_usb_capture(ua); + goto error; + } + set_bit(ALSA_PLAYBACK_OPEN, &ua->states); +error: + mutex_unlock(&ua->mutex); + return err; +} + +static int capture_pcm_close(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + + mutex_lock(&ua->mutex); + clear_bit(ALSA_CAPTURE_OPEN, &ua->states); + if (!test_bit(ALSA_PLAYBACK_OPEN, &ua->states)) + stop_usb_capture(ua); + mutex_unlock(&ua->mutex); + return 0; +} + +static int playback_pcm_close(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + + mutex_lock(&ua->mutex); + stop_usb_playback(ua); + clear_bit(ALSA_PLAYBACK_OPEN, &ua->states); + if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states)) + stop_usb_capture(ua); + mutex_unlock(&ua->mutex); + return 0; +} + +static int capture_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct ua101 *ua = substream->private_data; + int err; + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + mutex_unlock(&ua->mutex); + if (err < 0) + return err; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int playback_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct ua101 *ua = substream->private_data; + int err; + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + if (err >= 0) + err = start_usb_playback(ua); + mutex_unlock(&ua->mutex); + if (err < 0) + return err; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int ua101_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int capture_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + int err; + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + mutex_unlock(&ua->mutex); + if (err < 0) + return err; + + /* + * The EHCI driver schedules the first packet of an iso stream at 10 ms + * in the future, i.e., no data is actually captured for that long. + * Take the wait here so that the stream is known to be actually + * running when the start trigger has been called. + */ + wait_event(ua->alsa_capture_wait, + test_bit(CAPTURE_URB_COMPLETED, &ua->states) || + !test_bit(USB_CAPTURE_RUNNING, &ua->states)); + if (test_bit(DISCONNECTED, &ua->states)) + return -ENODEV; + if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) + return -EIO; + + ua->capture.period_pos = 0; + ua->capture.buffer_pos = 0; + return 0; +} + +static int playback_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct ua101 *ua = substream->private_data; + int err; + + mutex_lock(&ua->mutex); + err = start_usb_capture(ua); + if (err >= 0) + err = start_usb_playback(ua); + mutex_unlock(&ua->mutex); + if (err < 0) + return err; + + /* see the comment in capture_pcm_prepare() */ + wait_event(ua->alsa_playback_wait, + test_bit(PLAYBACK_URB_COMPLETED, &ua->states) || + !test_bit(USB_PLAYBACK_RUNNING, &ua->states)); + if (test_bit(DISCONNECTED, &ua->states)) + return -ENODEV; + if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states)) + return -EIO; + + substream->runtime->delay = 0; + ua->playback.period_pos = 0; + ua->playback.buffer_pos = 0; + return 0; +} + +static int capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ua101 *ua = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) + return -EIO; + set_bit(ALSA_CAPTURE_RUNNING, &ua->states); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + clear_bit(ALSA_CAPTURE_RUNNING, &ua->states); + return 0; + default: + return -EINVAL; + } +} + +static int playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ua101 *ua = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states)) + return -EIO; + set_bit(ALSA_PLAYBACK_RUNNING, &ua->states); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + clear_bit(ALSA_PLAYBACK_RUNNING, &ua->states); + return 0; + default: + return -EINVAL; + } +} + +static inline snd_pcm_uframes_t ua101_pcm_pointer(struct ua101 *ua, + struct ua101_stream *stream) +{ + unsigned long flags; + unsigned int pos; + + spin_lock_irqsave(&ua->lock, flags); + pos = stream->buffer_pos; + spin_unlock_irqrestore(&ua->lock, flags); + return pos; +} + +static snd_pcm_uframes_t capture_pcm_pointer(struct snd_pcm_substream *subs) +{ + struct ua101 *ua = subs->private_data; + + return ua101_pcm_pointer(ua, &ua->capture); +} + +static snd_pcm_uframes_t playback_pcm_pointer(struct snd_pcm_substream *subs) +{ + struct ua101 *ua = subs->private_data; + + return ua101_pcm_pointer(ua, &ua->playback); +} + +static struct snd_pcm_ops capture_pcm_ops = { + .open = capture_pcm_open, + .close = capture_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = capture_pcm_hw_params, + .hw_free = ua101_pcm_hw_free, + .prepare = capture_pcm_prepare, + .trigger = capture_pcm_trigger, + .pointer = capture_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +static struct snd_pcm_ops playback_pcm_ops = { + .open = playback_pcm_open, + .close = playback_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = playback_pcm_hw_params, + .hw_free = ua101_pcm_hw_free, + .prepare = playback_pcm_prepare, + .trigger = playback_pcm_trigger, + .pointer = playback_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +static const struct uac_format_type_i_discrete_descriptor * +find_format_descriptor(struct usb_interface *interface) +{ + struct usb_host_interface *alt; + u8 *extra; + int extralen; + + if (interface->num_altsetting != 2) { + dev_err(&interface->dev, "invalid num_altsetting\n"); + return NULL; + } + + alt = &interface->altsetting[0]; + if (alt->desc.bNumEndpoints != 0) { + dev_err(&interface->dev, "invalid bNumEndpoints\n"); + return NULL; + } + + alt = &interface->altsetting[1]; + if (alt->desc.bNumEndpoints != 1) { + dev_err(&interface->dev, "invalid bNumEndpoints\n"); + return NULL; + } + + extra = alt->extra; + extralen = alt->extralen; + while (extralen >= sizeof(struct usb_descriptor_header)) { + struct uac_format_type_i_discrete_descriptor *desc; + + desc = (struct uac_format_type_i_discrete_descriptor *)extra; + if (desc->bLength > extralen) { + dev_err(&interface->dev, "descriptor overflow\n"); + return NULL; + } + if (desc->bLength == UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1) && + desc->bDescriptorType == USB_DT_CS_INTERFACE && + desc->bDescriptorSubtype == UAC_FORMAT_TYPE) { + if (desc->bFormatType != UAC_FORMAT_TYPE_I_PCM || + desc->bSamFreqType != 1) { + dev_err(&interface->dev, + "invalid format type\n"); + return NULL; + } + return desc; + } + extralen -= desc->bLength; + extra += desc->bLength; + } + dev_err(&interface->dev, "sample format descriptor not found\n"); + return NULL; +} + +static int detect_usb_format(struct ua101 *ua) +{ + const struct uac_format_type_i_discrete_descriptor *fmt_capture; + const struct uac_format_type_i_discrete_descriptor *fmt_playback; + const struct usb_endpoint_descriptor *epd; + unsigned int rate2; + + fmt_capture = find_format_descriptor(ua->intf[INTF_CAPTURE]); + fmt_playback = find_format_descriptor(ua->intf[INTF_PLAYBACK]); + if (!fmt_capture || !fmt_playback) + return -ENXIO; + + switch (fmt_capture->bSubframeSize) { + case 3: + ua->format_bit = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + ua->format_bit = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + dev_err(&ua->dev->dev, "sample width is not 24 or 32 bits\n"); + return -ENXIO; + } + if (fmt_capture->bSubframeSize != fmt_playback->bSubframeSize) { + dev_err(&ua->dev->dev, + "playback/capture sample widths do not match\n"); + return -ENXIO; + } + + if (fmt_capture->bBitResolution != 24 || + fmt_playback->bBitResolution != 24) { + dev_err(&ua->dev->dev, "sample width is not 24 bits\n"); + return -ENXIO; + } + + ua->rate = combine_triple(fmt_capture->tSamFreq[0]); + rate2 = combine_triple(fmt_playback->tSamFreq[0]); + if (ua->rate != rate2) { + dev_err(&ua->dev->dev, + "playback/capture rates do not match: %u/%u\n", + rate2, ua->rate); + return -ENXIO; + } + + switch (ua->dev->speed) { + case USB_SPEED_FULL: + ua->packets_per_second = 1000; + break; + case USB_SPEED_HIGH: + ua->packets_per_second = 8000; + break; + default: + dev_err(&ua->dev->dev, "unknown device speed\n"); + return -ENXIO; + } + + ua->capture.channels = fmt_capture->bNrChannels; + ua->playback.channels = fmt_playback->bNrChannels; + ua->capture.frame_bytes = + fmt_capture->bSubframeSize * ua->capture.channels; + ua->playback.frame_bytes = + fmt_playback->bSubframeSize * ua->playback.channels; + + epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc; + if (!usb_endpoint_is_isoc_in(epd)) { + dev_err(&ua->dev->dev, "invalid capture endpoint\n"); + return -ENXIO; + } + ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, usb_endpoint_num(epd)); + ua->capture.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); + + epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc; + if (!usb_endpoint_is_isoc_out(epd)) { + dev_err(&ua->dev->dev, "invalid playback endpoint\n"); + return -ENXIO; + } + ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); + ua->playback.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); + return 0; +} + +static int alloc_stream_buffers(struct ua101 *ua, struct ua101_stream *stream) +{ + unsigned int remaining_packets, packets, packets_per_page, i; + size_t size; + + stream->queue_length = queue_length; + stream->queue_length = max(stream->queue_length, + (unsigned int)MIN_QUEUE_LENGTH); + stream->queue_length = min(stream->queue_length, + (unsigned int)MAX_QUEUE_LENGTH); + + /* + * The cache pool sizes used by usb_buffer_alloc() (128, 512, 2048) are + * quite bad when used with the packet sizes of this device (e.g. 280, + * 520, 624). Therefore, we allocate and subdivide entire pages, using + * a smaller buffer only for the last chunk. + */ + remaining_packets = stream->queue_length; + packets_per_page = PAGE_SIZE / stream->max_packet_bytes; + for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i) { + packets = min(remaining_packets, packets_per_page); + size = packets * stream->max_packet_bytes; + stream->buffers[i].addr = + usb_buffer_alloc(ua->dev, size, GFP_KERNEL, + &stream->buffers[i].dma); + if (!stream->buffers[i].addr) + return -ENOMEM; + stream->buffers[i].size = size; + remaining_packets -= packets; + if (!remaining_packets) + break; + } + if (remaining_packets) { + dev_err(&ua->dev->dev, "too many packets\n"); + return -ENXIO; + } + return 0; +} + +static void free_stream_buffers(struct ua101 *ua, struct ua101_stream *stream) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i) + usb_buffer_free(ua->dev, + stream->buffers[i].size, + stream->buffers[i].addr, + stream->buffers[i].dma); +} + +static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, + void (*urb_complete)(struct urb *)) +{ + unsigned max_packet_size = stream->max_packet_bytes; + struct ua101_urb *urb; + unsigned int b, u = 0; + + for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) { + unsigned int size = stream->buffers[b].size; + u8 *addr = stream->buffers[b].addr; + dma_addr_t dma = stream->buffers[b].dma; + + while (size >= max_packet_size) { + if (u >= stream->queue_length) + goto bufsize_error; + urb = kmalloc(sizeof(*urb), GFP_KERNEL); + if (!urb) + return -ENOMEM; + usb_init_urb(&urb->urb); + urb->urb.dev = ua->dev; + urb->urb.pipe = stream->usb_pipe; + urb->urb.transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + urb->urb.transfer_buffer = addr; + urb->urb.transfer_dma = dma; + urb->urb.transfer_buffer_length = max_packet_size; + urb->urb.number_of_packets = 1; + urb->urb.interval = 1; + urb->urb.context = ua; + urb->urb.complete = urb_complete; + urb->urb.iso_frame_desc[0].offset = 0; + urb->urb.iso_frame_desc[0].length = max_packet_size; + stream->urbs[u++] = urb; + size -= max_packet_size; + addr += max_packet_size; + dma += max_packet_size; + } + } + if (u == stream->queue_length) + return 0; +bufsize_error: + dev_err(&ua->dev->dev, "internal buffer size error\n"); + return -ENXIO; +} + +static void free_stream_urbs(struct ua101_stream *stream) +{ + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) + kfree(stream->urbs[i]); +} + +static void free_usb_related_resources(struct ua101 *ua, + struct usb_interface *interface) +{ + unsigned int i; + + free_stream_urbs(&ua->capture); + free_stream_urbs(&ua->playback); + free_stream_buffers(ua, &ua->capture); + free_stream_buffers(ua, &ua->playback); + + for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) + if (ua->intf[i]) { + usb_set_intfdata(ua->intf[i], NULL); + if (ua->intf[i] != interface) + usb_driver_release_interface(&ua101_driver, + ua->intf[i]); + } +} + +static void ua101_card_free(struct snd_card *card) +{ + struct ua101 *ua = card->private_data; + + mutex_destroy(&ua->mutex); +} + +static int ua101_probe(struct usb_interface *interface, + const struct usb_device_id *usb_id) +{ + static const struct snd_usb_midi_endpoint_info midi_ep = { + .out_cables = 0x0001, + .in_cables = 0x0001 + }; + static const struct snd_usb_audio_quirk midi_quirk = { + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &midi_ep + }; + static const int intf_numbers[2][3] = { + { /* UA-101 */ + [INTF_PLAYBACK] = 0, + [INTF_CAPTURE] = 1, + [INTF_MIDI] = 2, + }, + { /* UA-1000 */ + [INTF_CAPTURE] = 1, + [INTF_PLAYBACK] = 2, + [INTF_MIDI] = 3, + }, + }; + struct snd_card *card; + struct ua101 *ua; + unsigned int card_index, i; + int is_ua1000; + const char *name; + char usb_path[32]; + int err; + + is_ua1000 = usb_id->idProduct == 0x0044; + + if (interface->altsetting->desc.bInterfaceNumber != + intf_numbers[is_ua1000][0]) + return -ENODEV; + + mutex_lock(&devices_mutex); + + for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) + if (enable[card_index] && !(devices_used & (1 << card_index))) + break; + if (card_index >= SNDRV_CARDS) { + mutex_unlock(&devices_mutex); + return -ENOENT; + } + err = snd_card_create(index[card_index], id[card_index], THIS_MODULE, + sizeof(*ua), &card); + if (err < 0) { + mutex_unlock(&devices_mutex); + return err; + } + card->private_free = ua101_card_free; + ua = card->private_data; + ua->dev = interface_to_usbdev(interface); + ua->card = card; + ua->card_index = card_index; + INIT_LIST_HEAD(&ua->midi_list); + spin_lock_init(&ua->lock); + mutex_init(&ua->mutex); + INIT_LIST_HEAD(&ua->ready_playback_urbs); + tasklet_init(&ua->playback_tasklet, + playback_tasklet, (unsigned long)ua); + init_waitqueue_head(&ua->alsa_capture_wait); + init_waitqueue_head(&ua->rate_feedback_wait); + init_waitqueue_head(&ua->alsa_playback_wait); + + ua->intf[0] = interface; + for (i = 1; i < ARRAY_SIZE(ua->intf); ++i) { + ua->intf[i] = usb_ifnum_to_if(ua->dev, + intf_numbers[is_ua1000][i]); + if (!ua->intf[i]) { + dev_err(&ua->dev->dev, "interface %u not found\n", + intf_numbers[is_ua1000][i]); + err = -ENXIO; + goto probe_error; + } + err = usb_driver_claim_interface(&ua101_driver, + ua->intf[i], ua); + if (err < 0) { + ua->intf[i] = NULL; + err = -EBUSY; + goto probe_error; + } + } + + snd_card_set_dev(card, &interface->dev); + + err = detect_usb_format(ua); + if (err < 0) + goto probe_error; + + name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101"; + strcpy(card->driver, "UA-101"); + strcpy(card->shortname, name); + usb_make_path(ua->dev, usb_path, sizeof(usb_path)); + snprintf(ua->card->longname, sizeof(ua->card->longname), + "EDIROL %s (serial %s), %u Hz at %s, %s speed", name, + ua->dev->serial ? ua->dev->serial : "?", ua->rate, usb_path, + ua->dev->speed == USB_SPEED_HIGH ? "high" : "full"); + + err = alloc_stream_buffers(ua, &ua->capture); + if (err < 0) + goto probe_error; + err = alloc_stream_buffers(ua, &ua->playback); + if (err < 0) + goto probe_error; + + err = alloc_stream_urbs(ua, &ua->capture, capture_urb_complete); + if (err < 0) + goto probe_error; + err = alloc_stream_urbs(ua, &ua->playback, playback_urb_complete); + if (err < 0) + goto probe_error; + + err = snd_pcm_new(card, name, 0, 1, 1, &ua->pcm); + if (err < 0) + goto probe_error; + ua->pcm->private_data = ua; + strcpy(ua->pcm->name, name); + snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops); + snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops); + + err = snd_usbmidi_create(card, ua->intf[INTF_MIDI], + &ua->midi_list, &midi_quirk); + if (err < 0) + goto probe_error; + + err = snd_card_register(card); + if (err < 0) + goto probe_error; + + usb_set_intfdata(interface, ua); + devices_used |= 1 << card_index; + + mutex_unlock(&devices_mutex); + return 0; + +probe_error: + free_usb_related_resources(ua, interface); + snd_card_free(card); + mutex_unlock(&devices_mutex); + return err; +} + +static void ua101_disconnect(struct usb_interface *interface) +{ + struct ua101 *ua = usb_get_intfdata(interface); + struct list_head *midi; + + if (!ua) + return; + + mutex_lock(&devices_mutex); + + set_bit(DISCONNECTED, &ua->states); + wake_up(&ua->rate_feedback_wait); + + /* make sure that userspace cannot create new requests */ + snd_card_disconnect(ua->card); + + /* make sure that there are no pending USB requests */ + __list_for_each(midi, &ua->midi_list) + snd_usbmidi_disconnect(midi); + abort_alsa_playback(ua); + abort_alsa_capture(ua); + mutex_lock(&ua->mutex); + stop_usb_playback(ua); + stop_usb_capture(ua); + mutex_unlock(&ua->mutex); + + free_usb_related_resources(ua, interface); + + devices_used &= ~(1 << ua->card_index); + + snd_card_free_when_closed(ua->card); + + mutex_unlock(&devices_mutex); +} + +static struct usb_device_id ua101_ids[] = { + { USB_DEVICE(0x0582, 0x0044) }, /* UA-1000 high speed */ + { USB_DEVICE(0x0582, 0x007d) }, /* UA-101 high speed */ + { USB_DEVICE(0x0582, 0x008d) }, /* UA-101 full speed */ + { } +}; +MODULE_DEVICE_TABLE(usb, ua101_ids); + +static struct usb_driver ua101_driver = { + .name = "snd-ua101", + .id_table = ua101_ids, + .probe = ua101_probe, + .disconnect = ua101_disconnect, +#if 0 + .suspend = ua101_suspend, + .resume = ua101_resume, +#endif +}; + +static int __init alsa_card_ua101_init(void) +{ + return usb_register(&ua101_driver); +} + +static void __exit alsa_card_ua101_exit(void) +{ + usb_deregister(&ua101_driver); + mutex_destroy(&devices_mutex); +} + +module_init(alsa_card_ua101_init); +module_exit(alsa_card_ua101_exit); diff --git a/sound/usb/ua101.c b/sound/usb/ua101.c deleted file mode 100644 index 3d458d3..0000000 --- a/sound/usb/ua101.c +++ /dev/null @@ -1,1387 +0,0 @@ -/* - * Edirol UA-101/UA-1000 driver - * Copyright (c) Clemens Ladisch - * - * This driver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2. - * - * This driver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this driver. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usbaudio.h" - -MODULE_DESCRIPTION("Edirol UA-101/1000 driver"); -MODULE_AUTHOR("Clemens Ladisch "); -MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101},{Edirol,UA-1000}}"); - -/* - * Should not be lower than the minimum scheduling delay of the host - * controller. Some Intel controllers need more than one frame; as long as - * that driver doesn't tell us about this, use 1.5 frames just to be sure. - */ -#define MIN_QUEUE_LENGTH 12 -/* Somewhat random. */ -#define MAX_QUEUE_LENGTH 30 -/* - * This magic value optimizes memory usage efficiency for the UA-101's packet - * sizes at all sample rates, taking into account the stupid cache pool sizes - * that usb_buffer_alloc() uses. - */ -#define DEFAULT_QUEUE_LENGTH 21 - -#define MAX_PACKET_SIZE 672 /* hardware specific */ -#define MAX_MEMORY_BUFFERS DIV_ROUND_UP(MAX_QUEUE_LENGTH, \ - PAGE_SIZE / MAX_PACKET_SIZE) - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; -static unsigned int queue_length = 21; - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "card index"); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string"); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "enable card"); -module_param(queue_length, uint, 0644); -MODULE_PARM_DESC(queue_length, "USB queue length in microframes, " - __stringify(MIN_QUEUE_LENGTH)"-"__stringify(MAX_QUEUE_LENGTH)); - -enum { - INTF_PLAYBACK, - INTF_CAPTURE, - INTF_MIDI, - - INTF_COUNT -}; - -/* bits in struct ua101::states */ -enum { - USB_CAPTURE_RUNNING, - USB_PLAYBACK_RUNNING, - ALSA_CAPTURE_OPEN, - ALSA_PLAYBACK_OPEN, - ALSA_CAPTURE_RUNNING, - ALSA_PLAYBACK_RUNNING, - CAPTURE_URB_COMPLETED, - PLAYBACK_URB_COMPLETED, - DISCONNECTED, -}; - -struct ua101 { - struct usb_device *dev; - struct snd_card *card; - struct usb_interface *intf[INTF_COUNT]; - int card_index; - struct snd_pcm *pcm; - struct list_head midi_list; - u64 format_bit; - unsigned int rate; - unsigned int packets_per_second; - spinlock_t lock; - struct mutex mutex; - unsigned long states; - - /* FIFO to synchronize playback rate to capture rate */ - unsigned int rate_feedback_start; - unsigned int rate_feedback_count; - u8 rate_feedback[MAX_QUEUE_LENGTH]; - - struct list_head ready_playback_urbs; - struct tasklet_struct playback_tasklet; - wait_queue_head_t alsa_capture_wait; - wait_queue_head_t rate_feedback_wait; - wait_queue_head_t alsa_playback_wait; - struct ua101_stream { - struct snd_pcm_substream *substream; - unsigned int usb_pipe; - unsigned int channels; - unsigned int frame_bytes; - unsigned int max_packet_bytes; - unsigned int period_pos; - unsigned int buffer_pos; - unsigned int queue_length; - struct ua101_urb { - struct urb urb; - struct usb_iso_packet_descriptor iso_frame_desc[1]; - struct list_head ready_list; - } *urbs[MAX_QUEUE_LENGTH]; - struct { - unsigned int size; - void *addr; - dma_addr_t dma; - } buffers[MAX_MEMORY_BUFFERS]; - } capture, playback; -}; - -static DEFINE_MUTEX(devices_mutex); -static unsigned int devices_used; -static struct usb_driver ua101_driver; - -static void abort_alsa_playback(struct ua101 *ua); -static void abort_alsa_capture(struct ua101 *ua); - -static const char *usb_error_string(int err) -{ - switch (err) { - case -ENODEV: - return "no device"; - case -ENOENT: - return "endpoint not enabled"; - case -EPIPE: - return "endpoint stalled"; - case -ENOSPC: - return "not enough bandwidth"; - case -ESHUTDOWN: - return "device disabled"; - case -EHOSTUNREACH: - return "device suspended"; - case -EINVAL: - case -EAGAIN: - case -EFBIG: - case -EMSGSIZE: - return "internal error"; - default: - return "unknown error"; - } -} - -static void abort_usb_capture(struct ua101 *ua) -{ - if (test_and_clear_bit(USB_CAPTURE_RUNNING, &ua->states)) { - wake_up(&ua->alsa_capture_wait); - wake_up(&ua->rate_feedback_wait); - } -} - -static void abort_usb_playback(struct ua101 *ua) -{ - if (test_and_clear_bit(USB_PLAYBACK_RUNNING, &ua->states)) - wake_up(&ua->alsa_playback_wait); -} - -static void playback_urb_complete(struct urb *usb_urb) -{ - struct ua101_urb *urb = (struct ua101_urb *)usb_urb; - struct ua101 *ua = urb->urb.context; - unsigned long flags; - - if (unlikely(urb->urb.status == -ENOENT || /* unlinked */ - urb->urb.status == -ENODEV || /* device removed */ - urb->urb.status == -ECONNRESET || /* unlinked */ - urb->urb.status == -ESHUTDOWN)) { /* device disabled */ - abort_usb_playback(ua); - abort_alsa_playback(ua); - return; - } - - if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) { - /* append URB to FIFO */ - spin_lock_irqsave(&ua->lock, flags); - list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); - if (ua->rate_feedback_count > 0) - tasklet_schedule(&ua->playback_tasklet); - ua->playback.substream->runtime->delay -= - urb->urb.iso_frame_desc[0].length / - ua->playback.frame_bytes; - spin_unlock_irqrestore(&ua->lock, flags); - } -} - -static void first_playback_urb_complete(struct urb *urb) -{ - struct ua101 *ua = urb->context; - - urb->complete = playback_urb_complete; - playback_urb_complete(urb); - - set_bit(PLAYBACK_URB_COMPLETED, &ua->states); - wake_up(&ua->alsa_playback_wait); -} - -/* copy data from the ALSA ring buffer into the URB buffer */ -static bool copy_playback_data(struct ua101_stream *stream, struct urb *urb, - unsigned int frames) -{ - struct snd_pcm_runtime *runtime; - unsigned int frame_bytes, frames1; - const u8 *source; - - runtime = stream->substream->runtime; - frame_bytes = stream->frame_bytes; - source = runtime->dma_area + stream->buffer_pos * frame_bytes; - if (stream->buffer_pos + frames <= runtime->buffer_size) { - memcpy(urb->transfer_buffer, source, frames * frame_bytes); - } else { - /* wrap around at end of ring buffer */ - frames1 = runtime->buffer_size - stream->buffer_pos; - memcpy(urb->transfer_buffer, source, frames1 * frame_bytes); - memcpy(urb->transfer_buffer + frames1 * frame_bytes, - runtime->dma_area, (frames - frames1) * frame_bytes); - } - - stream->buffer_pos += frames; - if (stream->buffer_pos >= runtime->buffer_size) - stream->buffer_pos -= runtime->buffer_size; - stream->period_pos += frames; - if (stream->period_pos >= runtime->period_size) { - stream->period_pos -= runtime->period_size; - return true; - } - return false; -} - -static inline void add_with_wraparound(struct ua101 *ua, - unsigned int *value, unsigned int add) -{ - *value += add; - if (*value >= ua->playback.queue_length) - *value -= ua->playback.queue_length; -} - -static void playback_tasklet(unsigned long data) -{ - struct ua101 *ua = (void *)data; - unsigned long flags; - unsigned int frames; - struct ua101_urb *urb; - bool do_period_elapsed = false; - int err; - - if (unlikely(!test_bit(USB_PLAYBACK_RUNNING, &ua->states))) - return; - - /* - * Synchronizing the playback rate to the capture rate is done by using - * the same sequence of packet sizes for both streams. - * Submitting a playback URB therefore requires both a ready URB and - * the size of the corresponding capture packet, i.e., both playback - * and capture URBs must have been completed. Since the USB core does - * not guarantee that playback and capture complete callbacks are - * called alternately, we use two FIFOs for packet sizes and read URBs; - * submitting playback URBs is possible as long as both FIFOs are - * nonempty. - */ - spin_lock_irqsave(&ua->lock, flags); - while (ua->rate_feedback_count > 0 && - !list_empty(&ua->ready_playback_urbs)) { - /* take packet size out of FIFO */ - frames = ua->rate_feedback[ua->rate_feedback_start]; - add_with_wraparound(ua, &ua->rate_feedback_start, 1); - ua->rate_feedback_count--; - - /* take URB out of FIFO */ - urb = list_first_entry(&ua->ready_playback_urbs, - struct ua101_urb, ready_list); - list_del(&urb->ready_list); - - /* fill packet with data or silence */ - urb->urb.iso_frame_desc[0].length = - frames * ua->playback.frame_bytes; - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) - do_period_elapsed |= copy_playback_data(&ua->playback, - &urb->urb, - frames); - else - memset(urb->urb.transfer_buffer, 0, - urb->urb.iso_frame_desc[0].length); - - /* and off you go ... */ - err = usb_submit_urb(&urb->urb, GFP_ATOMIC); - if (unlikely(err < 0)) { - spin_unlock_irqrestore(&ua->lock, flags); - abort_usb_playback(ua); - abort_alsa_playback(ua); - dev_err(&ua->dev->dev, "USB request error %d: %s\n", - err, usb_error_string(err)); - return; - } - ua->playback.substream->runtime->delay += frames; - } - spin_unlock_irqrestore(&ua->lock, flags); - if (do_period_elapsed) - snd_pcm_period_elapsed(ua->playback.substream); -} - -/* copy data from the URB buffer into the ALSA ring buffer */ -static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, - unsigned int frames) -{ - struct snd_pcm_runtime *runtime; - unsigned int frame_bytes, frames1; - u8 *dest; - - runtime = stream->substream->runtime; - frame_bytes = stream->frame_bytes; - dest = runtime->dma_area + stream->buffer_pos * frame_bytes; - if (stream->buffer_pos + frames <= runtime->buffer_size) { - memcpy(dest, urb->transfer_buffer, frames * frame_bytes); - } else { - /* wrap around at end of ring buffer */ - frames1 = runtime->buffer_size - stream->buffer_pos; - memcpy(dest, urb->transfer_buffer, frames1 * frame_bytes); - memcpy(runtime->dma_area, - urb->transfer_buffer + frames1 * frame_bytes, - (frames - frames1) * frame_bytes); - } - - stream->buffer_pos += frames; - if (stream->buffer_pos >= runtime->buffer_size) - stream->buffer_pos -= runtime->buffer_size; - stream->period_pos += frames; - if (stream->period_pos >= runtime->period_size) { - stream->period_pos -= runtime->period_size; - return true; - } - return false; -} - -static void capture_urb_complete(struct urb *urb) -{ - struct ua101 *ua = urb->context; - struct ua101_stream *stream = &ua->capture; - unsigned long flags; - unsigned int frames, write_ptr; - bool do_period_elapsed; - int err; - - if (unlikely(urb->status == -ENOENT || /* unlinked */ - urb->status == -ENODEV || /* device removed */ - urb->status == -ECONNRESET || /* unlinked */ - urb->status == -ESHUTDOWN)) /* device disabled */ - goto stream_stopped; - - if (urb->status >= 0 && urb->iso_frame_desc[0].status >= 0) - frames = urb->iso_frame_desc[0].actual_length / - stream->frame_bytes; - else - frames = 0; - - spin_lock_irqsave(&ua->lock, flags); - - if (frames > 0 && test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) - do_period_elapsed = copy_capture_data(stream, urb, frames); - else - do_period_elapsed = false; - - if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (unlikely(err < 0)) { - spin_unlock_irqrestore(&ua->lock, flags); - dev_err(&ua->dev->dev, "USB request error %d: %s\n", - err, usb_error_string(err)); - goto stream_stopped; - } - - /* append packet size to FIFO */ - write_ptr = ua->rate_feedback_start; - add_with_wraparound(ua, &write_ptr, ua->rate_feedback_count); - ua->rate_feedback[write_ptr] = frames; - if (ua->rate_feedback_count < ua->playback.queue_length) { - ua->rate_feedback_count++; - if (ua->rate_feedback_count == - ua->playback.queue_length) - wake_up(&ua->rate_feedback_wait); - } else { - /* - * Ring buffer overflow; this happens when the playback - * stream is not running. Throw away the oldest entry, - * so that the playback stream, when it starts, sees - * the most recent packet sizes. - */ - add_with_wraparound(ua, &ua->rate_feedback_start, 1); - } - if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) && - !list_empty(&ua->ready_playback_urbs)) - tasklet_schedule(&ua->playback_tasklet); - } - - spin_unlock_irqrestore(&ua->lock, flags); - - if (do_period_elapsed) - snd_pcm_period_elapsed(stream->substream); - - return; - -stream_stopped: - abort_usb_playback(ua); - abort_usb_capture(ua); - abort_alsa_playback(ua); - abort_alsa_capture(ua); -} - -static void first_capture_urb_complete(struct urb *urb) -{ - struct ua101 *ua = urb->context; - - urb->complete = capture_urb_complete; - capture_urb_complete(urb); - - set_bit(CAPTURE_URB_COMPLETED, &ua->states); - wake_up(&ua->alsa_capture_wait); -} - -static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream) -{ - unsigned int i; - - for (i = 0; i < stream->queue_length; ++i) { - int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL); - if (err < 0) { - dev_err(&ua->dev->dev, "USB request error %d: %s\n", - err, usb_error_string(err)); - return err; - } - } - return 0; -} - -static void kill_stream_urbs(struct ua101_stream *stream) -{ - unsigned int i; - - for (i = 0; i < stream->queue_length; ++i) - usb_kill_urb(&stream->urbs[i]->urb); -} - -static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index) -{ - struct usb_host_interface *alts; - - alts = ua->intf[intf_index]->cur_altsetting; - if (alts->desc.bAlternateSetting != 1) { - int err = usb_set_interface(ua->dev, - alts->desc.bInterfaceNumber, 1); - if (err < 0) { - dev_err(&ua->dev->dev, - "cannot initialize interface; error %d: %s\n", - err, usb_error_string(err)); - return err; - } - } - return 0; -} - -static void disable_iso_interface(struct ua101 *ua, unsigned int intf_index) -{ - struct usb_host_interface *alts; - - alts = ua->intf[intf_index]->cur_altsetting; - if (alts->desc.bAlternateSetting != 0) { - int err = usb_set_interface(ua->dev, - alts->desc.bInterfaceNumber, 0); - if (err < 0 && !test_bit(DISCONNECTED, &ua->states)) - dev_warn(&ua->dev->dev, - "interface reset failed; error %d: %s\n", - err, usb_error_string(err)); - } -} - -static void stop_usb_capture(struct ua101 *ua) -{ - clear_bit(USB_CAPTURE_RUNNING, &ua->states); - - kill_stream_urbs(&ua->capture); - - disable_iso_interface(ua, INTF_CAPTURE); -} - -static int start_usb_capture(struct ua101 *ua) -{ - int err; - - if (test_bit(DISCONNECTED, &ua->states)) - return -ENODEV; - - if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) - return 0; - - kill_stream_urbs(&ua->capture); - - err = enable_iso_interface(ua, INTF_CAPTURE); - if (err < 0) - return err; - - clear_bit(CAPTURE_URB_COMPLETED, &ua->states); - ua->capture.urbs[0]->urb.complete = first_capture_urb_complete; - ua->rate_feedback_start = 0; - ua->rate_feedback_count = 0; - - set_bit(USB_CAPTURE_RUNNING, &ua->states); - err = submit_stream_urbs(ua, &ua->capture); - if (err < 0) - stop_usb_capture(ua); - return err; -} - -static void stop_usb_playback(struct ua101 *ua) -{ - clear_bit(USB_PLAYBACK_RUNNING, &ua->states); - - kill_stream_urbs(&ua->playback); - - tasklet_kill(&ua->playback_tasklet); - - disable_iso_interface(ua, INTF_PLAYBACK); -} - -static int start_usb_playback(struct ua101 *ua) -{ - unsigned int i, frames; - struct urb *urb; - int err = 0; - - if (test_bit(DISCONNECTED, &ua->states)) - return -ENODEV; - - if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) - return 0; - - kill_stream_urbs(&ua->playback); - tasklet_kill(&ua->playback_tasklet); - - err = enable_iso_interface(ua, INTF_PLAYBACK); - if (err < 0) - return err; - - clear_bit(PLAYBACK_URB_COMPLETED, &ua->states); - ua->playback.urbs[0]->urb.complete = - first_playback_urb_complete; - spin_lock_irq(&ua->lock); - INIT_LIST_HEAD(&ua->ready_playback_urbs); - spin_unlock_irq(&ua->lock); - - /* - * We submit the initial URBs all at once, so we have to wait for the - * packet size FIFO to be full. - */ - wait_event(ua->rate_feedback_wait, - ua->rate_feedback_count >= ua->playback.queue_length || - !test_bit(USB_CAPTURE_RUNNING, &ua->states) || - test_bit(DISCONNECTED, &ua->states)); - if (test_bit(DISCONNECTED, &ua->states)) { - stop_usb_playback(ua); - return -ENODEV; - } - if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) { - stop_usb_playback(ua); - return -EIO; - } - - for (i = 0; i < ua->playback.queue_length; ++i) { - /* all initial URBs contain silence */ - spin_lock_irq(&ua->lock); - frames = ua->rate_feedback[ua->rate_feedback_start]; - add_with_wraparound(ua, &ua->rate_feedback_start, 1); - ua->rate_feedback_count--; - spin_unlock_irq(&ua->lock); - urb = &ua->playback.urbs[i]->urb; - urb->iso_frame_desc[0].length = - frames * ua->playback.frame_bytes; - memset(urb->transfer_buffer, 0, - urb->iso_frame_desc[0].length); - } - - set_bit(USB_PLAYBACK_RUNNING, &ua->states); - err = submit_stream_urbs(ua, &ua->playback); - if (err < 0) - stop_usb_playback(ua); - return err; -} - -static void abort_alsa_capture(struct ua101 *ua) -{ - if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) - snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN); -} - -static void abort_alsa_playback(struct ua101 *ua) -{ - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) - snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN); -} - -static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, - unsigned int channels) -{ - int err; - - substream->runtime->hw.info = - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_FIFO_IN_FRAMES; - substream->runtime->hw.formats = ua->format_bit; - substream->runtime->hw.rates = snd_pcm_rate_to_rate_bit(ua->rate); - substream->runtime->hw.rate_min = ua->rate; - substream->runtime->hw.rate_max = ua->rate; - substream->runtime->hw.channels_min = channels; - substream->runtime->hw.channels_max = channels; - substream->runtime->hw.buffer_bytes_max = 45000 * 1024; - substream->runtime->hw.period_bytes_min = 1; - substream->runtime->hw.period_bytes_max = UINT_MAX; - substream->runtime->hw.periods_min = 2; - substream->runtime->hw.periods_max = UINT_MAX; - err = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 1500000 / ua->packets_per_second, - 8192000); - if (err < 0) - return err; - err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); - return err; -} - -static int capture_pcm_open(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - int err; - - ua->capture.substream = substream; - err = set_stream_hw(ua, substream, ua->capture.channels); - if (err < 0) - return err; - substream->runtime->hw.fifo_size = - DIV_ROUND_CLOSEST(ua->rate, ua->packets_per_second); - substream->runtime->delay = substream->runtime->hw.fifo_size; - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - if (err >= 0) - set_bit(ALSA_CAPTURE_OPEN, &ua->states); - mutex_unlock(&ua->mutex); - return err; -} - -static int playback_pcm_open(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - int err; - - ua->playback.substream = substream; - err = set_stream_hw(ua, substream, ua->playback.channels); - if (err < 0) - return err; - substream->runtime->hw.fifo_size = - DIV_ROUND_CLOSEST(ua->rate * ua->playback.queue_length, - ua->packets_per_second); - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - if (err < 0) - goto error; - err = start_usb_playback(ua); - if (err < 0) { - if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states)) - stop_usb_capture(ua); - goto error; - } - set_bit(ALSA_PLAYBACK_OPEN, &ua->states); -error: - mutex_unlock(&ua->mutex); - return err; -} - -static int capture_pcm_close(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - - mutex_lock(&ua->mutex); - clear_bit(ALSA_CAPTURE_OPEN, &ua->states); - if (!test_bit(ALSA_PLAYBACK_OPEN, &ua->states)) - stop_usb_capture(ua); - mutex_unlock(&ua->mutex); - return 0; -} - -static int playback_pcm_close(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - - mutex_lock(&ua->mutex); - stop_usb_playback(ua); - clear_bit(ALSA_PLAYBACK_OPEN, &ua->states); - if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states)) - stop_usb_capture(ua); - mutex_unlock(&ua->mutex); - return 0; -} - -static int capture_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct ua101 *ua = substream->private_data; - int err; - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); -} - -static int playback_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct ua101 *ua = substream->private_data; - int err; - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - if (err >= 0) - err = start_usb_playback(ua); - mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); -} - -static int ua101_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int capture_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - int err; - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - /* - * The EHCI driver schedules the first packet of an iso stream at 10 ms - * in the future, i.e., no data is actually captured for that long. - * Take the wait here so that the stream is known to be actually - * running when the start trigger has been called. - */ - wait_event(ua->alsa_capture_wait, - test_bit(CAPTURE_URB_COMPLETED, &ua->states) || - !test_bit(USB_CAPTURE_RUNNING, &ua->states)); - if (test_bit(DISCONNECTED, &ua->states)) - return -ENODEV; - if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) - return -EIO; - - ua->capture.period_pos = 0; - ua->capture.buffer_pos = 0; - return 0; -} - -static int playback_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct ua101 *ua = substream->private_data; - int err; - - mutex_lock(&ua->mutex); - err = start_usb_capture(ua); - if (err >= 0) - err = start_usb_playback(ua); - mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - /* see the comment in capture_pcm_prepare() */ - wait_event(ua->alsa_playback_wait, - test_bit(PLAYBACK_URB_COMPLETED, &ua->states) || - !test_bit(USB_PLAYBACK_RUNNING, &ua->states)); - if (test_bit(DISCONNECTED, &ua->states)) - return -ENODEV; - if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states)) - return -EIO; - - substream->runtime->delay = 0; - ua->playback.period_pos = 0; - ua->playback.buffer_pos = 0; - return 0; -} - -static int capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct ua101 *ua = substream->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) - return -EIO; - set_bit(ALSA_CAPTURE_RUNNING, &ua->states); - return 0; - case SNDRV_PCM_TRIGGER_STOP: - clear_bit(ALSA_CAPTURE_RUNNING, &ua->states); - return 0; - default: - return -EINVAL; - } -} - -static int playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct ua101 *ua = substream->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states)) - return -EIO; - set_bit(ALSA_PLAYBACK_RUNNING, &ua->states); - return 0; - case SNDRV_PCM_TRIGGER_STOP: - clear_bit(ALSA_PLAYBACK_RUNNING, &ua->states); - return 0; - default: - return -EINVAL; - } -} - -static inline snd_pcm_uframes_t ua101_pcm_pointer(struct ua101 *ua, - struct ua101_stream *stream) -{ - unsigned long flags; - unsigned int pos; - - spin_lock_irqsave(&ua->lock, flags); - pos = stream->buffer_pos; - spin_unlock_irqrestore(&ua->lock, flags); - return pos; -} - -static snd_pcm_uframes_t capture_pcm_pointer(struct snd_pcm_substream *subs) -{ - struct ua101 *ua = subs->private_data; - - return ua101_pcm_pointer(ua, &ua->capture); -} - -static snd_pcm_uframes_t playback_pcm_pointer(struct snd_pcm_substream *subs) -{ - struct ua101 *ua = subs->private_data; - - return ua101_pcm_pointer(ua, &ua->playback); -} - -static struct snd_pcm_ops capture_pcm_ops = { - .open = capture_pcm_open, - .close = capture_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = capture_pcm_hw_params, - .hw_free = ua101_pcm_hw_free, - .prepare = capture_pcm_prepare, - .trigger = capture_pcm_trigger, - .pointer = capture_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - -static struct snd_pcm_ops playback_pcm_ops = { - .open = playback_pcm_open, - .close = playback_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = playback_pcm_hw_params, - .hw_free = ua101_pcm_hw_free, - .prepare = playback_pcm_prepare, - .trigger = playback_pcm_trigger, - .pointer = playback_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - -static const struct uac_format_type_i_discrete_descriptor * -find_format_descriptor(struct usb_interface *interface) -{ - struct usb_host_interface *alt; - u8 *extra; - int extralen; - - if (interface->num_altsetting != 2) { - dev_err(&interface->dev, "invalid num_altsetting\n"); - return NULL; - } - - alt = &interface->altsetting[0]; - if (alt->desc.bNumEndpoints != 0) { - dev_err(&interface->dev, "invalid bNumEndpoints\n"); - return NULL; - } - - alt = &interface->altsetting[1]; - if (alt->desc.bNumEndpoints != 1) { - dev_err(&interface->dev, "invalid bNumEndpoints\n"); - return NULL; - } - - extra = alt->extra; - extralen = alt->extralen; - while (extralen >= sizeof(struct usb_descriptor_header)) { - struct uac_format_type_i_discrete_descriptor *desc; - - desc = (struct uac_format_type_i_discrete_descriptor *)extra; - if (desc->bLength > extralen) { - dev_err(&interface->dev, "descriptor overflow\n"); - return NULL; - } - if (desc->bLength == UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1) && - desc->bDescriptorType == USB_DT_CS_INTERFACE && - desc->bDescriptorSubtype == UAC_FORMAT_TYPE) { - if (desc->bFormatType != UAC_FORMAT_TYPE_I_PCM || - desc->bSamFreqType != 1) { - dev_err(&interface->dev, - "invalid format type\n"); - return NULL; - } - return desc; - } - extralen -= desc->bLength; - extra += desc->bLength; - } - dev_err(&interface->dev, "sample format descriptor not found\n"); - return NULL; -} - -static int detect_usb_format(struct ua101 *ua) -{ - const struct uac_format_type_i_discrete_descriptor *fmt_capture; - const struct uac_format_type_i_discrete_descriptor *fmt_playback; - const struct usb_endpoint_descriptor *epd; - unsigned int rate2; - - fmt_capture = find_format_descriptor(ua->intf[INTF_CAPTURE]); - fmt_playback = find_format_descriptor(ua->intf[INTF_PLAYBACK]); - if (!fmt_capture || !fmt_playback) - return -ENXIO; - - switch (fmt_capture->bSubframeSize) { - case 3: - ua->format_bit = SNDRV_PCM_FMTBIT_S24_3LE; - break; - case 4: - ua->format_bit = SNDRV_PCM_FMTBIT_S32_LE; - break; - default: - dev_err(&ua->dev->dev, "sample width is not 24 or 32 bits\n"); - return -ENXIO; - } - if (fmt_capture->bSubframeSize != fmt_playback->bSubframeSize) { - dev_err(&ua->dev->dev, - "playback/capture sample widths do not match\n"); - return -ENXIO; - } - - if (fmt_capture->bBitResolution != 24 || - fmt_playback->bBitResolution != 24) { - dev_err(&ua->dev->dev, "sample width is not 24 bits\n"); - return -ENXIO; - } - - ua->rate = combine_triple(fmt_capture->tSamFreq[0]); - rate2 = combine_triple(fmt_playback->tSamFreq[0]); - if (ua->rate != rate2) { - dev_err(&ua->dev->dev, - "playback/capture rates do not match: %u/%u\n", - rate2, ua->rate); - return -ENXIO; - } - - switch (ua->dev->speed) { - case USB_SPEED_FULL: - ua->packets_per_second = 1000; - break; - case USB_SPEED_HIGH: - ua->packets_per_second = 8000; - break; - default: - dev_err(&ua->dev->dev, "unknown device speed\n"); - return -ENXIO; - } - - ua->capture.channels = fmt_capture->bNrChannels; - ua->playback.channels = fmt_playback->bNrChannels; - ua->capture.frame_bytes = - fmt_capture->bSubframeSize * ua->capture.channels; - ua->playback.frame_bytes = - fmt_playback->bSubframeSize * ua->playback.channels; - - epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc; - if (!usb_endpoint_is_isoc_in(epd)) { - dev_err(&ua->dev->dev, "invalid capture endpoint\n"); - return -ENXIO; - } - ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, usb_endpoint_num(epd)); - ua->capture.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); - - epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc; - if (!usb_endpoint_is_isoc_out(epd)) { - dev_err(&ua->dev->dev, "invalid playback endpoint\n"); - return -ENXIO; - } - ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); - ua->playback.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); - return 0; -} - -static int alloc_stream_buffers(struct ua101 *ua, struct ua101_stream *stream) -{ - unsigned int remaining_packets, packets, packets_per_page, i; - size_t size; - - stream->queue_length = queue_length; - stream->queue_length = max(stream->queue_length, - (unsigned int)MIN_QUEUE_LENGTH); - stream->queue_length = min(stream->queue_length, - (unsigned int)MAX_QUEUE_LENGTH); - - /* - * The cache pool sizes used by usb_buffer_alloc() (128, 512, 2048) are - * quite bad when used with the packet sizes of this device (e.g. 280, - * 520, 624). Therefore, we allocate and subdivide entire pages, using - * a smaller buffer only for the last chunk. - */ - remaining_packets = stream->queue_length; - packets_per_page = PAGE_SIZE / stream->max_packet_bytes; - for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i) { - packets = min(remaining_packets, packets_per_page); - size = packets * stream->max_packet_bytes; - stream->buffers[i].addr = - usb_buffer_alloc(ua->dev, size, GFP_KERNEL, - &stream->buffers[i].dma); - if (!stream->buffers[i].addr) - return -ENOMEM; - stream->buffers[i].size = size; - remaining_packets -= packets; - if (!remaining_packets) - break; - } - if (remaining_packets) { - dev_err(&ua->dev->dev, "too many packets\n"); - return -ENXIO; - } - return 0; -} - -static void free_stream_buffers(struct ua101 *ua, struct ua101_stream *stream) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i) - usb_buffer_free(ua->dev, - stream->buffers[i].size, - stream->buffers[i].addr, - stream->buffers[i].dma); -} - -static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, - void (*urb_complete)(struct urb *)) -{ - unsigned max_packet_size = stream->max_packet_bytes; - struct ua101_urb *urb; - unsigned int b, u = 0; - - for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) { - unsigned int size = stream->buffers[b].size; - u8 *addr = stream->buffers[b].addr; - dma_addr_t dma = stream->buffers[b].dma; - - while (size >= max_packet_size) { - if (u >= stream->queue_length) - goto bufsize_error; - urb = kmalloc(sizeof(*urb), GFP_KERNEL); - if (!urb) - return -ENOMEM; - usb_init_urb(&urb->urb); - urb->urb.dev = ua->dev; - urb->urb.pipe = stream->usb_pipe; - urb->urb.transfer_flags = URB_ISO_ASAP | - URB_NO_TRANSFER_DMA_MAP; - urb->urb.transfer_buffer = addr; - urb->urb.transfer_dma = dma; - urb->urb.transfer_buffer_length = max_packet_size; - urb->urb.number_of_packets = 1; - urb->urb.interval = 1; - urb->urb.context = ua; - urb->urb.complete = urb_complete; - urb->urb.iso_frame_desc[0].offset = 0; - urb->urb.iso_frame_desc[0].length = max_packet_size; - stream->urbs[u++] = urb; - size -= max_packet_size; - addr += max_packet_size; - dma += max_packet_size; - } - } - if (u == stream->queue_length) - return 0; -bufsize_error: - dev_err(&ua->dev->dev, "internal buffer size error\n"); - return -ENXIO; -} - -static void free_stream_urbs(struct ua101_stream *stream) -{ - unsigned int i; - - for (i = 0; i < stream->queue_length; ++i) - kfree(stream->urbs[i]); -} - -static void free_usb_related_resources(struct ua101 *ua, - struct usb_interface *interface) -{ - unsigned int i; - - free_stream_urbs(&ua->capture); - free_stream_urbs(&ua->playback); - free_stream_buffers(ua, &ua->capture); - free_stream_buffers(ua, &ua->playback); - - for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) - if (ua->intf[i]) { - usb_set_intfdata(ua->intf[i], NULL); - if (ua->intf[i] != interface) - usb_driver_release_interface(&ua101_driver, - ua->intf[i]); - } -} - -static void ua101_card_free(struct snd_card *card) -{ - struct ua101 *ua = card->private_data; - - mutex_destroy(&ua->mutex); -} - -static int ua101_probe(struct usb_interface *interface, - const struct usb_device_id *usb_id) -{ - static const struct snd_usb_midi_endpoint_info midi_ep = { - .out_cables = 0x0001, - .in_cables = 0x0001 - }; - static const struct snd_usb_audio_quirk midi_quirk = { - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &midi_ep - }; - static const int intf_numbers[2][3] = { - { /* UA-101 */ - [INTF_PLAYBACK] = 0, - [INTF_CAPTURE] = 1, - [INTF_MIDI] = 2, - }, - { /* UA-1000 */ - [INTF_CAPTURE] = 1, - [INTF_PLAYBACK] = 2, - [INTF_MIDI] = 3, - }, - }; - struct snd_card *card; - struct ua101 *ua; - unsigned int card_index, i; - int is_ua1000; - const char *name; - char usb_path[32]; - int err; - - is_ua1000 = usb_id->idProduct == 0x0044; - - if (interface->altsetting->desc.bInterfaceNumber != - intf_numbers[is_ua1000][0]) - return -ENODEV; - - mutex_lock(&devices_mutex); - - for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) - if (enable[card_index] && !(devices_used & (1 << card_index))) - break; - if (card_index >= SNDRV_CARDS) { - mutex_unlock(&devices_mutex); - return -ENOENT; - } - err = snd_card_create(index[card_index], id[card_index], THIS_MODULE, - sizeof(*ua), &card); - if (err < 0) { - mutex_unlock(&devices_mutex); - return err; - } - card->private_free = ua101_card_free; - ua = card->private_data; - ua->dev = interface_to_usbdev(interface); - ua->card = card; - ua->card_index = card_index; - INIT_LIST_HEAD(&ua->midi_list); - spin_lock_init(&ua->lock); - mutex_init(&ua->mutex); - INIT_LIST_HEAD(&ua->ready_playback_urbs); - tasklet_init(&ua->playback_tasklet, - playback_tasklet, (unsigned long)ua); - init_waitqueue_head(&ua->alsa_capture_wait); - init_waitqueue_head(&ua->rate_feedback_wait); - init_waitqueue_head(&ua->alsa_playback_wait); - - ua->intf[0] = interface; - for (i = 1; i < ARRAY_SIZE(ua->intf); ++i) { - ua->intf[i] = usb_ifnum_to_if(ua->dev, - intf_numbers[is_ua1000][i]); - if (!ua->intf[i]) { - dev_err(&ua->dev->dev, "interface %u not found\n", - intf_numbers[is_ua1000][i]); - err = -ENXIO; - goto probe_error; - } - err = usb_driver_claim_interface(&ua101_driver, - ua->intf[i], ua); - if (err < 0) { - ua->intf[i] = NULL; - err = -EBUSY; - goto probe_error; - } - } - - snd_card_set_dev(card, &interface->dev); - - err = detect_usb_format(ua); - if (err < 0) - goto probe_error; - - name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101"; - strcpy(card->driver, "UA-101"); - strcpy(card->shortname, name); - usb_make_path(ua->dev, usb_path, sizeof(usb_path)); - snprintf(ua->card->longname, sizeof(ua->card->longname), - "EDIROL %s (serial %s), %u Hz at %s, %s speed", name, - ua->dev->serial ? ua->dev->serial : "?", ua->rate, usb_path, - ua->dev->speed == USB_SPEED_HIGH ? "high" : "full"); - - err = alloc_stream_buffers(ua, &ua->capture); - if (err < 0) - goto probe_error; - err = alloc_stream_buffers(ua, &ua->playback); - if (err < 0) - goto probe_error; - - err = alloc_stream_urbs(ua, &ua->capture, capture_urb_complete); - if (err < 0) - goto probe_error; - err = alloc_stream_urbs(ua, &ua->playback, playback_urb_complete); - if (err < 0) - goto probe_error; - - err = snd_pcm_new(card, name, 0, 1, 1, &ua->pcm); - if (err < 0) - goto probe_error; - ua->pcm->private_data = ua; - strcpy(ua->pcm->name, name); - snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops); - snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops); - - err = snd_usbmidi_create(card, ua->intf[INTF_MIDI], - &ua->midi_list, &midi_quirk); - if (err < 0) - goto probe_error; - - err = snd_card_register(card); - if (err < 0) - goto probe_error; - - usb_set_intfdata(interface, ua); - devices_used |= 1 << card_index; - - mutex_unlock(&devices_mutex); - return 0; - -probe_error: - free_usb_related_resources(ua, interface); - snd_card_free(card); - mutex_unlock(&devices_mutex); - return err; -} - -static void ua101_disconnect(struct usb_interface *interface) -{ - struct ua101 *ua = usb_get_intfdata(interface); - struct list_head *midi; - - if (!ua) - return; - - mutex_lock(&devices_mutex); - - set_bit(DISCONNECTED, &ua->states); - wake_up(&ua->rate_feedback_wait); - - /* make sure that userspace cannot create new requests */ - snd_card_disconnect(ua->card); - - /* make sure that there are no pending USB requests */ - __list_for_each(midi, &ua->midi_list) - snd_usbmidi_disconnect(midi); - abort_alsa_playback(ua); - abort_alsa_capture(ua); - mutex_lock(&ua->mutex); - stop_usb_playback(ua); - stop_usb_capture(ua); - mutex_unlock(&ua->mutex); - - free_usb_related_resources(ua, interface); - - devices_used &= ~(1 << ua->card_index); - - snd_card_free_when_closed(ua->card); - - mutex_unlock(&devices_mutex); -} - -static struct usb_device_id ua101_ids[] = { - { USB_DEVICE(0x0582, 0x0044) }, /* UA-1000 high speed */ - { USB_DEVICE(0x0582, 0x007d) }, /* UA-101 high speed */ - { USB_DEVICE(0x0582, 0x008d) }, /* UA-101 full speed */ - { } -}; -MODULE_DEVICE_TABLE(usb, ua101_ids); - -static struct usb_driver ua101_driver = { - .name = "snd-ua101", - .id_table = ua101_ids, - .probe = ua101_probe, - .disconnect = ua101_disconnect, -#if 0 - .suspend = ua101_suspend, - .resume = ua101_resume, -#endif -}; - -static int __init alsa_card_ua101_init(void) -{ - return usb_register(&ua101_driver); -} - -static void __exit alsa_card_ua101_exit(void) -{ - usb_deregister(&ua101_driver); - mutex_destroy(&devices_mutex); -} - -module_init(alsa_card_ua101_init); -module_exit(alsa_card_ua101_exit); -- cgit v1.1 From 3e1aebef6fb55e35668d2d7cf608cf03f30c904f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Mar 2010 19:46:12 +0100 Subject: ALSA: usb-audio: header file cleanups Rename snd-usb-lib to snd-usbmidi-lib as MIDI functions are the only thing it actually contains. Introduce a new header file to only declare these functions. Introduced usbmixer.h for all functions exported by usbmixer.c. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 11 +++++----- sound/usb/misc/ua101.c | 1 + sound/usb/usbaudio.c | 3 ++- sound/usb/usbaudio.h | 51 ---------------------------------------------- sound/usb/usbmidi.c | 1 + sound/usb/usbmidi.h | 48 +++++++++++++++++++++++++++++++++++++++++++ sound/usb/usbmixer.c | 1 + sound/usb/usbmixer.h | 11 ++++++++++ sound/usb/usx2y/us122l.c | 1 + sound/usb/usx2y/usbusx2y.h | 1 + 10 files changed, 72 insertions(+), 57 deletions(-) create mode 100644 sound/usb/usbmidi.h create mode 100644 sound/usb/usbmixer.h (limited to 'sound') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index b0e5597..423d829 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -3,12 +3,13 @@ # snd-usb-audio-objs := usbaudio.o usbmixer.o -snd-usb-lib-objs := usbmidi.o +snd-usbmidi-lib-objs := usbmidi.o # Toplevel Module Dependency -obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o -obj-$(CONFIG_SND_USB_UA101) += snd-usb-lib.o -obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o -obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o +obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o + +obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o +obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o +obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index e9b0ae5..b4a4cb4 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -24,6 +24,7 @@ #include #include #include "../usbaudio.h" +#include "../usbmidi.h" MODULE_DESCRIPTION("Edirol UA-101/1000 driver"); MODULE_AUTHOR("Clemens Ladisch "); diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 11b0826..5b91aa0 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -56,7 +56,8 @@ #include #include "usbaudio.h" - +#include "usbmidi.h" +#include "usbmixer.h" MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("USB Audio"); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 42c299c..49a691a 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -21,9 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* maximum number of endpoints per interface */ -#define MIDI_MAX_ENDPOINTS 2 - /* handling of USB vendor/product ID pairs as 32-bit numbers */ #define USB_ID(vendor, product) (((vendor) << 16) | (product)) #define USB_ID_VENDOR(id) ((id) >> 16) @@ -89,39 +86,6 @@ struct snd_usb_audio_quirk { const void *data; }; -/* data for QUIRK_MIDI_FIXED_ENDPOINT */ -struct snd_usb_midi_endpoint_info { - int8_t out_ep; /* ep number, 0 autodetect */ - uint8_t out_interval; /* interval for interrupt endpoints */ - int8_t in_ep; - uint8_t in_interval; - uint16_t out_cables; /* bitmask */ - uint16_t in_cables; /* bitmask */ -}; - -/* for QUIRK_MIDI_YAMAHA, data is NULL */ - -/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info - * structure (out_cables and in_cables only) */ - -/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk - * structures, terminated with .ifnum = -1 */ - -/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ - -/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */ - -/* for QUIRK_AUDIO_EDIROL_UAXX, data is NULL */ - -/* for QUIRK_IGNORE_INTERFACE, data is NULL */ - -/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */ - -/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info - * structure (out_cables and in_cables only) */ - -/* for QUIRK_MIDI_CME, data is NULL */ - /* */ @@ -148,21 +112,6 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout); -int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, - int ignore_error); -void snd_usb_mixer_disconnect(struct list_head *p); - -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface *iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk); -void snd_usbmidi_input_stop(struct list_head* p); -void snd_usbmidi_input_start(struct list_head* p); -void snd_usbmidi_disconnect(struct list_head *p); - -void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, - unsigned char samplerate_id); - /* * retrieve usb_interface descriptor from the host interface * (conditional for compatibility with the older API) diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 2c59afd..5915a04 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -53,6 +53,7 @@ #include #include #include "usbaudio.h" +#include "usbmidi.h" /* diff --git a/sound/usb/usbmidi.h b/sound/usb/usbmidi.h new file mode 100644 index 0000000..2089ec9 --- /dev/null +++ b/sound/usb/usbmidi.h @@ -0,0 +1,48 @@ +#ifndef __USBMIDI_H +#define __USBMIDI_H + +/* maximum number of endpoints per interface */ +#define MIDI_MAX_ENDPOINTS 2 + +/* data for QUIRK_MIDI_FIXED_ENDPOINT */ +struct snd_usb_midi_endpoint_info { + int8_t out_ep; /* ep number, 0 autodetect */ + uint8_t out_interval; /* interval for interrupt endpoints */ + int8_t in_ep; + uint8_t in_interval; + uint16_t out_cables; /* bitmask */ + uint16_t in_cables; /* bitmask */ +}; + +/* for QUIRK_MIDI_YAMAHA, data is NULL */ + +/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info + * structure (out_cables and in_cables only) */ + +/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk + * structures, terminated with .ifnum = -1 */ + +/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ + +/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */ + +/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */ + +/* for QUIRK_IGNORE_INTERFACE, data is NULL */ + +/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */ + +/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info + * structure (out_cables and in_cables only) */ + +/* for QUIRK_MIDI_CME, data is NULL */ + +int snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk); +void snd_usbmidi_input_stop(struct list_head* p); +void snd_usbmidi_input_start(struct list_head* p); +void snd_usbmidi_disconnect(struct list_head *p); + +#endif /* __USBMIDI_H */ diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 8e8f871b..43d53a3 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -41,6 +41,7 @@ #include #include "usbaudio.h" +#include "usbmixer.h" /* */ diff --git a/sound/usb/usbmixer.h b/sound/usb/usbmixer.h new file mode 100644 index 0000000..e199e4b --- /dev/null +++ b/sound/usb/usbmixer.h @@ -0,0 +1,11 @@ +#ifndef __USBMIXER_H +#define __USBMIXER_H + +int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, + int ignore_error); +void snd_usb_mixer_disconnect(struct list_head *p); + +void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, + unsigned char samplerate_id); + +#endif /* __USBMIXER_H */ diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 44deb21..4f6518c 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -25,6 +25,7 @@ #define MODNAME "US122L" #include "usb_stream.c" #include "../usbaudio.h" +#include "../usbmidi.h" #include "us122l.h" MODULE_AUTHOR("Karsten Wiese "); diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index 1d174ce..9ab97b4 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -1,6 +1,7 @@ #ifndef USBUSX2Y_H #define USBUSX2Y_H #include "../usbaudio.h" +#include "../usbmidi.h" #include "usbus428ctldefs.h" #define NRURBS 2 -- cgit v1.1 From e5779998bf8b70e48a6cc208c8b61b33bd6117ea Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Mar 2010 19:46:13 +0100 Subject: ALSA: usb-audio: refactor code Clean up the usb audio driver by factoring out a lot of functions to separate files. Code for procfs, quirks, urbs, format parsers etc all got a new home now. Moved almost all special quirk handling to quirks.c and introduced new generic functions to handle them, so the exceptions do not pollute the whole driver. Renamed usbaudio.c to card.c because this is what it actually does now. Renamed usbmidi.c to midi.c for namespace clarity. Removed more things from usbaudio.h. The non-standard drivers were adopted accordingly. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 13 +- sound/usb/card.c | 648 +++++++ sound/usb/card.h | 105 ++ sound/usb/debug.h | 15 + sound/usb/endpoint.c | 358 ++++ sound/usb/endpoint.h | 11 + sound/usb/format.c | 445 +++++ sound/usb/format.h | 8 + sound/usb/helper.c | 112 ++ sound/usb/helper.h | 32 + sound/usb/midi.c | 2069 ++++++++++++++++++++++ sound/usb/midi.h | 48 + sound/usb/misc/ua101.c | 2 +- sound/usb/pcm.c | 845 +++++++++ sound/usb/pcm.h | 14 + sound/usb/proc.c | 163 ++ sound/usb/proc.h | 8 + sound/usb/quirks-table.h | 2248 ++++++++++++++++++++++++ sound/usb/quirks.c | 592 +++++++ sound/usb/quirks.h | 23 + sound/usb/urb.c | 989 +++++++++++ sound/usb/urb.h | 21 + sound/usb/usbaudio.c | 4051 -------------------------------------------- sound/usb/usbaudio.h | 42 +- sound/usb/usbmidi.c | 2069 ---------------------- sound/usb/usbmidi.h | 48 - sound/usb/usbmixer.c | 1 + sound/usb/usbquirks.h | 2248 ------------------------ sound/usb/usx2y/us122l.c | 2 +- sound/usb/usx2y/usbusx2y.h | 2 +- 30 files changed, 8774 insertions(+), 8458 deletions(-) create mode 100644 sound/usb/card.c create mode 100644 sound/usb/card.h create mode 100644 sound/usb/debug.h create mode 100644 sound/usb/endpoint.c create mode 100644 sound/usb/endpoint.h create mode 100644 sound/usb/format.c create mode 100644 sound/usb/format.h create mode 100644 sound/usb/helper.c create mode 100644 sound/usb/helper.h create mode 100644 sound/usb/midi.c create mode 100644 sound/usb/midi.h create mode 100644 sound/usb/pcm.c create mode 100644 sound/usb/pcm.h create mode 100644 sound/usb/proc.c create mode 100644 sound/usb/proc.h create mode 100644 sound/usb/quirks-table.h create mode 100644 sound/usb/quirks.c create mode 100644 sound/usb/quirks.h create mode 100644 sound/usb/urb.c create mode 100644 sound/usb/urb.h delete mode 100644 sound/usb/usbaudio.c delete mode 100644 sound/usb/usbmidi.c delete mode 100644 sound/usb/usbmidi.h delete mode 100644 sound/usb/usbquirks.h (limited to 'sound') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 423d829..0758d8d 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -2,8 +2,17 @@ # Makefile for ALSA # -snd-usb-audio-objs := usbaudio.o usbmixer.o -snd-usbmidi-lib-objs := usbmidi.o +snd-usb-audio-objs := card.o \ + usbmixer.o \ + proc.o \ + quirks.o \ + format.o \ + endpoint.o \ + urb.o \ + pcm.o \ + helper.o + +snd-usbmidi-lib-objs := midi.o # Toplevel Module Dependency obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o diff --git a/sound/usb/card.c b/sound/usb/card.c new file mode 100644 index 0000000..426aabc --- /dev/null +++ b/sound/usb/card.c @@ -0,0 +1,648 @@ +/* + * (Tentative) USB Audio Driver for ALSA + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTES: + * + * - async unlink should be used for avoiding the sleep inside lock. + * 2.4.22 usb-uhci seems buggy for async unlinking and results in + * oops. in such a cse, pass async_unlink=0 option. + * - the linked URBs would be preferred but not used so far because of + * the instability of unlinking. + * - type II is not supported properly. there is no device which supports + * this type *correctly*. SB extigy looks as if it supports, but it's + * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream). + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "midi.h" +#include "usbmixer.h" +#include "proc.h" +#include "quirks.h" +#include "endpoint.h" +#include "helper.h" +#include "debug.h" +#include "pcm.h" +#include "urb.h" +#include "format.h" + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("USB Audio"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); + + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ +/* Vendor/product IDs for this card */ +static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; +static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; +static int nrpacks = 8; /* max. number of packets per urb */ +static int async_unlink = 1; +static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ +static int ignore_ctl_error; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the USB audio adapter."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable USB audio adapter."); +module_param_array(vid, int, NULL, 0444); +MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); +module_param_array(pid, int, NULL, 0444); +MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); +module_param(nrpacks, int, 0644); +MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); +module_param(async_unlink, bool, 0444); +MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); +module_param_array(device_setup, int, NULL, 0444); +MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); +module_param(ignore_ctl_error, bool, 0444); +MODULE_PARM_DESC(ignore_ctl_error, + "Ignore errors from USB controller for mixer interfaces."); + +/* + * we keep the snd_usb_audio_t instances by ourselves for merging + * the all interfaces on the same card as one sound device. + */ + +static DEFINE_MUTEX(register_mutex); +static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; +static struct usb_driver usb_audio_driver; + +/* + * disconnect streams + * called from snd_usb_audio_disconnect() + */ +static void snd_usb_stream_disconnect(struct list_head *head) +{ + int idx; + struct snd_usb_stream *as; + struct snd_usb_substream *subs; + + as = list_entry(head, struct snd_usb_stream, list); + for (idx = 0; idx < 2; idx++) { + subs = &as->substream[idx]; + if (!subs->num_formats) + return; + snd_usb_release_substream_urbs(subs, 1); + subs->interface = -1; + } +} + +static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface) +{ + struct usb_device *dev = chip->dev; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_interface *iface = usb_ifnum_to_if(dev, interface); + + if (!iface) { + snd_printk(KERN_ERR "%d:%u:%d : does not exist\n", + dev->devnum, ctrlif, interface); + return -EINVAL; + } + + if (usb_interface_claimed(iface)) { + snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", + dev->devnum, ctrlif, interface); + return -EINVAL; + } + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && + altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { + int err = snd_usbmidi_create(chip->card, iface, + &chip->midi_list, NULL); + if (err < 0) { + snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", + dev->devnum, ctrlif, interface); + return -EINVAL; + } + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + + return 0; + } + + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) { + snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", + dev->devnum, ctrlif, interface, altsd->bInterfaceClass); + /* skip non-supported classes */ + return -EINVAL; + } + + if (snd_usb_get_speed(dev) == USB_SPEED_LOW) { + snd_printk(KERN_ERR "low speed audio streaming not supported\n"); + return -EINVAL; + } + + if (! snd_usb_parse_audio_endpoints(chip, interface)) { + usb_set_interface(dev, interface, 0); /* reset the current interface */ + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + return -EINVAL; + } + + return 0; +} + +/* + * parse audio control descriptor and create pcm/midi streams + */ +static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) +{ + struct usb_device *dev = chip->dev; + struct usb_host_interface *host_iface; + struct usb_interface_descriptor *altsd; + void *control_header; + int i, protocol; + + /* find audiocontrol interface */ + host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; + control_header = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, + NULL, UAC_HEADER); + altsd = get_iface_desc(host_iface); + protocol = altsd->bInterfaceProtocol; + + if (!control_header) { + snd_printk(KERN_ERR "cannot find UAC_HEADER\n"); + return -EINVAL; + } + + switch (protocol) { + case UAC_VERSION_1: { + struct uac_ac_header_descriptor_v1 *h1 = control_header; + + if (!h1->bInCollection) { + snd_printk(KERN_INFO "skipping empty audio interface (v1)\n"); + return -EINVAL; + } + + if (h1->bLength < sizeof(*h1) + h1->bInCollection) { + snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n"); + return -EINVAL; + } + + for (i = 0; i < h1->bInCollection; i++) + snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); + + break; + } + + case UAC_VERSION_2: { + struct uac_clock_source_descriptor *cs; + struct usb_interface_assoc_descriptor *assoc = + usb_ifnum_to_if(dev, ctrlif)->intf_assoc; + + if (!assoc) { + snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n"); + return -EINVAL; + } + + /* FIXME: for now, we expect there is at least one clock source + * descriptor and we always take the first one. + * We should properly support devices with multiple clock sources, + * clock selectors and sample rate conversion units. */ + + cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, + NULL, UAC_CLOCK_SOURCE); + + if (!cs) { + snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n"); + return -EINVAL; + } + + chip->clock_id = cs->bClockID; + + for (i = 0; i < assoc->bInterfaceCount; i++) { + int intf = assoc->bFirstInterface + i; + + if (intf != ctrlif) + snd_usb_create_stream(chip, ctrlif, intf); + } + + break; + } + + default: + snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol); + return -EINVAL; + } + + return 0; +} + +/* + * free the chip instance + * + * here we have to do not much, since pcm and controls are already freed + * + */ + +static int snd_usb_audio_free(struct snd_usb_audio *chip) +{ + kfree(chip); + return 0; +} + +static int snd_usb_audio_dev_free(struct snd_device *device) +{ + struct snd_usb_audio *chip = device->device_data; + return snd_usb_audio_free(chip); +} + + +/* + * create a chip instance and set its names. + */ +static int snd_usb_audio_create(struct usb_device *dev, int idx, + const struct snd_usb_audio_quirk *quirk, + struct snd_usb_audio **rchip) +{ + struct snd_card *card; + struct snd_usb_audio *chip; + int err, len; + char component[14]; + static struct snd_device_ops ops = { + .dev_free = snd_usb_audio_dev_free, + }; + + *rchip = NULL; + + if (snd_usb_get_speed(dev) != USB_SPEED_LOW && + snd_usb_get_speed(dev) != USB_SPEED_FULL && + snd_usb_get_speed(dev) != USB_SPEED_HIGH) { + snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev)); + return -ENXIO; + } + + err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card); + if (err < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", idx); + return err; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (! chip) { + snd_card_free(card); + return -ENOMEM; + } + + chip->index = idx; + chip->dev = dev; + chip->card = card; + chip->setup = device_setup[idx]; + chip->nrpacks = nrpacks; + chip->async_unlink = async_unlink; + + chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->midi_list); + INIT_LIST_HEAD(&chip->mixer_list); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_usb_audio_free(chip); + snd_card_free(card); + return err; + } + + strcpy(card->driver, "USB-Audio"); + sprintf(component, "USB%04x:%04x", + USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); + snd_component_add(card, component); + + /* retrieve the device string as shortname */ + if (quirk && quirk->product_name) { + strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname)); + } else { + if (!dev->descriptor.iProduct || + usb_string(dev, dev->descriptor.iProduct, + card->shortname, sizeof(card->shortname)) <= 0) { + /* no name available from anywhere, so use ID */ + sprintf(card->shortname, "USB Device %#04x:%#04x", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); + } + } + + /* retrieve the vendor and device strings as longname */ + if (quirk && quirk->vendor_name) { + len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname)); + } else { + if (dev->descriptor.iManufacturer) + len = usb_string(dev, dev->descriptor.iManufacturer, + card->longname, sizeof(card->longname)); + else + len = 0; + /* we don't really care if there isn't any vendor string */ + } + if (len > 0) + strlcat(card->longname, " ", sizeof(card->longname)); + + strlcat(card->longname, card->shortname, sizeof(card->longname)); + + len = strlcat(card->longname, " at ", sizeof(card->longname)); + + if (len < sizeof(card->longname)) + usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); + + strlcat(card->longname, + snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" : + snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" : + ", high speed", + sizeof(card->longname)); + + snd_usb_audio_create_proc(chip); + + *rchip = chip; + return 0; +} + +/* + * probe the active usb device + * + * note that this can be called multiple times per a device, when it + * includes multiple audio control interfaces. + * + * thus we check the usb device pointer and creates the card instance + * only at the first time. the successive calls of this function will + * append the pcm interface to the corresponding card. + */ +static void *snd_usb_audio_probe(struct usb_device *dev, + struct usb_interface *intf, + const struct usb_device_id *usb_id) +{ + const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; + int i, err; + struct snd_usb_audio *chip; + struct usb_host_interface *alts; + int ifnum; + u32 id; + + alts = &intf->altsetting[0]; + ifnum = get_iface_desc(alts)->bInterfaceNumber; + id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) + goto __err_val; + + if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0) + goto __err_val; + + /* + * found a config. now register to ALSA + */ + + /* check whether it's already registered */ + chip = NULL; + mutex_lock(®ister_mutex); + for (i = 0; i < SNDRV_CARDS; i++) { + if (usb_chip[i] && usb_chip[i]->dev == dev) { + if (usb_chip[i]->shutdown) { + snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n"); + goto __error; + } + chip = usb_chip[i]; + break; + } + } + if (! chip) { + /* it's a fresh one. + * now look for an empty slot and create a new card instance + */ + for (i = 0; i < SNDRV_CARDS; i++) + if (enable[i] && ! usb_chip[i] && + (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && + (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { + if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) { + goto __error; + } + snd_card_set_dev(chip->card, &intf->dev); + break; + } + if (!chip) { + printk(KERN_ERR "no available usb audio device\n"); + goto __error; + } + } + + chip->txfr_quirk = 0; + err = 1; /* continue */ + if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { + /* need some special handlings */ + if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) + goto __error; + } + + if (err > 0) { + /* create normal USB audio interfaces */ + if (snd_usb_create_streams(chip, ifnum) < 0 || + snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) { + goto __error; + } + } + + /* we are allowed to call snd_card_register() many times */ + if (snd_card_register(chip->card) < 0) { + goto __error; + } + + usb_chip[chip->index] = chip; + chip->num_interfaces++; + mutex_unlock(®ister_mutex); + return chip; + + __error: + if (chip && !chip->num_interfaces) + snd_card_free(chip->card); + mutex_unlock(®ister_mutex); + __err_val: + return NULL; +} + +/* + * we need to take care of counter, since disconnection can be called also + * many times as well as usb_audio_probe(). + */ +static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) +{ + struct snd_usb_audio *chip; + struct snd_card *card; + struct list_head *p; + + if (ptr == (void *)-1L) + return; + + chip = ptr; + card = chip->card; + mutex_lock(®ister_mutex); + chip->shutdown = 1; + chip->num_interfaces--; + if (chip->num_interfaces <= 0) { + snd_card_disconnect(card); + /* release the pcm resources */ + list_for_each(p, &chip->pcm_list) { + snd_usb_stream_disconnect(p); + } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } + /* release mixer resources */ + list_for_each(p, &chip->mixer_list) { + snd_usb_mixer_disconnect(p); + } + usb_chip[chip->index] = NULL; + mutex_unlock(®ister_mutex); + snd_card_free_when_closed(card); + } else { + mutex_unlock(®ister_mutex); + } +} + +/* + * new 2.5 USB kernel API + */ +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + void *chip; + chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); + if (chip) { + usb_set_intfdata(intf, chip); + return 0; + } else + return -EIO; +} + +static void usb_audio_disconnect(struct usb_interface *intf) +{ + snd_usb_audio_disconnect(interface_to_usbdev(intf), + usb_get_intfdata(intf)); +} + +#ifdef CONFIG_PM +static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct snd_usb_audio *chip = usb_get_intfdata(intf); + struct list_head *p; + struct snd_usb_stream *as; + + if (chip == (void *)-1L) + return 0; + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + if (!chip->num_suspended_intf++) { + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, struct snd_usb_stream, list); + snd_pcm_suspend_all(as->pcm); + } + } + + return 0; +} + +static int usb_audio_resume(struct usb_interface *intf) +{ + struct snd_usb_audio *chip = usb_get_intfdata(intf); + + if (chip == (void *)-1L) + return 0; + if (--chip->num_suspended_intf) + return 0; + /* + * ALSA leaves material resumption to user space + * we just notify + */ + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif /* CONFIG_PM */ + +static struct usb_device_id usb_audio_ids [] = { +#include "quirks-table.h" + { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_audio_ids); + +/* + * entry point for linux usb interface + */ + +static struct usb_driver usb_audio_driver = { + .name = "snd-usb-audio", + .probe = usb_audio_probe, + .disconnect = usb_audio_disconnect, + .suspend = usb_audio_suspend, + .resume = usb_audio_resume, + .id_table = usb_audio_ids, +}; + +static int __init snd_usb_audio_init(void) +{ + if (nrpacks < 1 || nrpacks > MAX_PACKS) { + printk(KERN_WARNING "invalid nrpacks value.\n"); + return -EINVAL; + } + return usb_register(&usb_audio_driver); +} + +static void __exit snd_usb_audio_cleanup(void) +{ + usb_deregister(&usb_audio_driver); +} + +module_init(snd_usb_audio_init); +module_exit(snd_usb_audio_cleanup); diff --git a/sound/usb/card.h b/sound/usb/card.h new file mode 100644 index 0000000..71f03c1 --- /dev/null +++ b/sound/usb/card.h @@ -0,0 +1,105 @@ +#ifndef __USBAUDIO_CARD_H +#define __USBAUDIO_CARD_H + +#define MAX_PACKS 20 +#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ +#define MAX_URBS 8 +#define SYNC_URBS 4 /* always four urbs for sync */ +#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */ + +struct audioformat { + struct list_head list; + snd_pcm_format_t format; /* format type */ + unsigned int channels; /* # channels */ + unsigned int fmt_type; /* USB audio format type (1-3) */ + unsigned int frame_size; /* samples per frame for non-audio */ + int iface; /* interface number */ + unsigned char altsetting; /* corresponding alternate setting */ + unsigned char altset_idx; /* array index of altenate setting */ + unsigned char attributes; /* corresponding attributes of cs endpoint */ + unsigned char endpoint; /* endpoint */ + unsigned char ep_attr; /* endpoint attributes */ + unsigned char datainterval; /* log_2 of data packet interval */ + unsigned int maxpacksize; /* max. packet size */ + unsigned int rates; /* rate bitmasks */ + unsigned int rate_min, rate_max; /* min/max rates */ + unsigned int nr_rates; /* number of rate table entries */ + unsigned int *rate_table; /* rate table */ +}; + +struct snd_usb_substream; + +struct snd_urb_ctx { + struct urb *urb; + unsigned int buffer_size; /* size of data buffer, if data URB */ + struct snd_usb_substream *subs; + int index; /* index for urb array */ + int packets; /* number of packets per urb */ +}; + +struct snd_urb_ops { + int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); + int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); + int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); + int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); +}; + +struct snd_usb_substream { + struct snd_usb_stream *stream; + struct usb_device *dev; + struct snd_pcm_substream *pcm_substream; + int direction; /* playback or capture */ + int interface; /* current interface */ + int endpoint; /* assigned endpoint */ + struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ + unsigned int cur_rate; /* current rate (for hw_params callback) */ + unsigned int period_bytes; /* current period bytes (for hw_params callback) */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data i/o pipe */ + unsigned int syncpipe; /* 1 - async out or adaptive in */ + unsigned int datainterval; /* log_2 of data packet interval */ + unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ + unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int maxpacksize; /* max packet size in bytes */ + unsigned int maxframesize; /* max packet size in frames */ + unsigned int curpacksize; /* current packet size in bytes (for capture) */ + unsigned int curframesize; /* current packet size in frames (for capture) */ + unsigned int fill_max: 1; /* fill max packet size always */ + unsigned int txfr_quirk:1; /* allow sub-frame alignment */ + unsigned int fmt_type; /* USB audio format type (1-3) */ + + unsigned int running: 1; /* running status */ + + unsigned int hwptr_done; /* processed byte position in the buffer */ + unsigned int transfer_done; /* processed frames since last period update */ + unsigned long active_mask; /* bitmask of active urbs */ + unsigned long unlink_mask; /* bitmask of unlinked urbs */ + + unsigned int nurbs; /* # urbs */ + struct snd_urb_ctx dataurb[MAX_URBS]; /* data urb table */ + struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */ + char *syncbuf; /* sync buffer for all sync URBs */ + dma_addr_t sync_dma; /* DMA address of syncbuf */ + + u64 formats; /* format bitmasks (all or'ed) */ + unsigned int num_formats; /* number of supported audio formats (list) */ + struct list_head fmt_list; /* format list */ + struct snd_pcm_hw_constraint_list rate_list; /* limited rates */ + spinlock_t lock; + + struct snd_urb_ops ops; /* callbacks (must be filled at init) */ +}; + +struct snd_usb_stream { + struct snd_usb_audio *chip; + struct snd_pcm *pcm; + int pcm_index; + unsigned int fmt_type; /* USB audio format type (1-3) */ + struct snd_usb_substream substream[2]; + struct list_head list; +}; + +#endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/debug.h b/sound/usb/debug.h new file mode 100644 index 0000000..343ec2d --- /dev/null +++ b/sound/usb/debug.h @@ -0,0 +1,15 @@ +#ifndef __USBAUDIO_DEBUG_H +#define __USBAUDIO_DEBUG_H + +/* + * h/w constraints + */ + +#ifdef HW_CONST_DEBUG +#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) +#else +#define hwc_debug(fmt, args...) /**/ +#endif + +#endif /* __USBAUDIO_DEBUG_H */ + diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c new file mode 100644 index 0000000..3f53dee --- /dev/null +++ b/sound/usb/endpoint.c @@ -0,0 +1,358 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "proc.h" +#include "quirks.h" +#include "endpoint.h" +#include "urb.h" +#include "pcm.h" +#include "helper.h" +#include "format.h" + +/* + * free a substream + */ +static void free_substream(struct snd_usb_substream *subs) +{ + struct list_head *p, *n; + + if (!subs->num_formats) + return; /* not initialized */ + list_for_each_safe(p, n, &subs->fmt_list) { + struct audioformat *fp = list_entry(p, struct audioformat, list); + kfree(fp->rate_table); + kfree(fp); + } + kfree(subs->rate_list.list); +} + + +/* + * free a usb stream instance + */ +static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) +{ + free_substream(&stream->substream[0]); + free_substream(&stream->substream[1]); + list_del(&stream->list); + kfree(stream); +} + +static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) +{ + struct snd_usb_stream *stream = pcm->private_data; + if (stream) { + stream->pcm = NULL; + snd_usb_audio_stream_free(stream); + } +} + + +/* + * add this endpoint to the chip instance. + * if a stream with the same endpoint already exists, append to it. + * if not, create a new pcm stream. + */ +int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp) +{ + struct list_head *p; + struct snd_usb_stream *as; + struct snd_usb_substream *subs; + struct snd_pcm *pcm; + int err; + + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, struct snd_usb_stream, list); + if (as->fmt_type != fp->fmt_type) + continue; + subs = &as->substream[stream]; + if (!subs->endpoint) + continue; + if (subs->endpoint == fp->endpoint) { + list_add_tail(&fp->list, &subs->fmt_list); + subs->num_formats++; + subs->formats |= 1ULL << fp->format; + return 0; + } + } + /* look for an empty stream */ + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, struct snd_usb_stream, list); + if (as->fmt_type != fp->fmt_type) + continue; + subs = &as->substream[stream]; + if (subs->endpoint) + continue; + err = snd_pcm_new_stream(as->pcm, stream, 1); + if (err < 0) + return err; + snd_usb_init_substream(as, stream, fp); + return 0; + } + + /* create a new pcm */ + as = kzalloc(sizeof(*as), GFP_KERNEL); + if (!as) + return -ENOMEM; + as->pcm_index = chip->pcm_devs; + as->chip = chip; + as->fmt_type = fp->fmt_type; + err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, + stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, + stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, + &pcm); + if (err < 0) { + kfree(as); + return err; + } + as->pcm = pcm; + pcm->private_data = as; + pcm->private_free = snd_usb_audio_pcm_free; + pcm->info_flags = 0; + if (chip->pcm_devs > 0) + sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); + else + strcpy(pcm->name, "USB Audio"); + + snd_usb_init_substream(as, stream, fp); + + list_add(&as->list, &chip->pcm_list); + chip->pcm_devs++; + + snd_usb_proc_pcm_format_add(as); + + return 0; +} + +int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) +{ + struct usb_device *dev; + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + int i, altno, err, stream; + int format = 0, num_channels = 0; + struct audioformat *fp = NULL; + unsigned char *fmt, *csep; + int num, protocol; + + dev = chip->dev; + + /* parse the interface's altsettings */ + iface = usb_ifnum_to_if(dev, iface_no); + + num = iface->num_altsetting; + + /* + * Dallas DS4201 workaround: It presents 5 altsettings, but the last + * one misses syncpipe, and does not produce any sound. + */ + if (chip->usb_id == USB_ID(0x04fa, 0x4201)) + num = 4; + + for (i = 0; i < num; i++) { + alts = &iface->altsetting[i]; + altsd = get_iface_desc(alts); + protocol = altsd->bInterfaceProtocol; + /* skip invalid one */ + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING && + altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) || + altsd->bNumEndpoints < 1 || + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0) + continue; + /* must be isochronous */ + if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_ISOC) + continue; + /* check direction */ + stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + altno = altsd->bAlternateSetting; + + if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) + continue; + + /* get audio formats */ + switch (protocol) { + case UAC_VERSION_1: { + struct uac_as_header_descriptor_v1 *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); + + if (!as) { + snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", + dev->devnum, iface_no, altno); + continue; + } + + if (as->bLength < sizeof(*as)) { + snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n", + dev->devnum, iface_no, altno); + continue; + } + + format = le16_to_cpu(as->wFormatTag); /* remember the format value */ + break; + } + + case UAC_VERSION_2: { + struct uac_as_header_descriptor_v2 *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); + + if (!as) { + snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", + dev->devnum, iface_no, altno); + continue; + } + + if (as->bLength < sizeof(*as)) { + snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n", + dev->devnum, iface_no, altno); + continue; + } + + num_channels = as->bNrChannels; + format = le32_to_cpu(as->bmFormats); + + break; + } + + default: + snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n", + dev->devnum, iface_no, altno, protocol); + continue; + } + + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE); + if (!fmt) { + snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n", + dev->devnum, iface_no, altno); + continue; + } + if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) || + ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) { + snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", + dev->devnum, iface_no, altno); + continue; + } + + /* + * Blue Microphones workaround: The last altsetting is identical + * with the previous one, except for a larger packet size, but + * is actually a mislabeled two-channel setting; ignore it. + */ + if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 && + fp && fp->altsetting == 1 && fp->channels == 1 && + fp->format == SNDRV_PCM_FORMAT_S16_LE && + protocol == UAC_VERSION_1 && + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == + fp->maxpacksize * 2) + continue; + + csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); + /* Creamware Noah has this descriptor after the 2nd endpoint */ + if (!csep && altsd->bNumEndpoints >= 2) + csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); + if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) { + snd_printk(KERN_WARNING "%d:%u:%d : no or invalid" + " class specific endpoint descriptor\n", + dev->devnum, iface_no, altno); + csep = NULL; + } + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (! fp) { + snd_printk(KERN_ERR "cannot malloc\n"); + return -ENOMEM; + } + + fp->iface = iface_no; + fp->altsetting = altno; + fp->altset_idx = i; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + /* num_channels is only set for v2 interfaces */ + fp->channels = num_channels; + if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->attributes = csep ? csep[3] : 0; + + /* some quirks for attributes here */ + + switch (chip->usb_id) { + case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ + /* Optoplay sets the sample rate attribute although + * it seems not supporting it in fact. + */ + fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + /* doesn't set the sample rate attribute, but supports it */ + fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ + case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is + an older model 77d:223) */ + /* + * plantronics headset and Griffin iMic have set adaptive-in + * although it's really not... + */ + fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; + else + fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; + break; + } + + /* ok, let's parse further... */ + if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { + kfree(fp->rate_table); + kfree(fp); + continue; + } + + snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); + err = snd_usb_add_audio_endpoint(chip, stream, fp); + if (err < 0) { + kfree(fp->rate_table); + kfree(fp); + return err; + } + /* try to set the interface... */ + usb_set_interface(chip->dev, iface_no, altno); + snd_usb_init_pitch(chip->dev, iface_no, alts, fp); + snd_usb_init_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); + } + return 0; +} + diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h new file mode 100644 index 0000000..64dd0db --- /dev/null +++ b/sound/usb/endpoint.h @@ -0,0 +1,11 @@ +#ifndef __USBAUDIO_ENDPOINT_H +#define __USBAUDIO_ENDPOINT_H + +int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, + int iface_no); + +int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, + int stream, + struct audioformat *fp); + +#endif /* __USBAUDIO_ENDPOINT_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c new file mode 100644 index 0000000..cbfe0c2 --- /dev/null +++ b/sound/usb/format.c @@ -0,0 +1,445 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "quirks.h" +#include "helper.h" +#include "debug.h" + +/* + * parse the audio format type I descriptor + * and returns the corresponding pcm format + * + * @dev: usb device + * @fp: audioformat record + * @format: the format tag (wFormatTag) + * @fmt: the format type descriptor + */ +static int parse_audio_format_i_type(struct snd_usb_audio *chip, + struct audioformat *fp, + int format, void *_fmt, + int protocol) +{ + int pcm_format, i; + int sample_width, sample_bytes; + + switch (protocol) { + case UAC_VERSION_1: { + struct uac_format_type_i_discrete_descriptor *fmt = _fmt; + sample_width = fmt->bBitResolution; + sample_bytes = fmt->bSubframeSize; + break; + } + + case UAC_VERSION_2: { + struct uac_format_type_i_ext_descriptor *fmt = _fmt; + sample_width = fmt->bBitResolution; + sample_bytes = fmt->bSubslotSize; + + /* + * FIXME + * USB audio class v2 devices specify a bitmap of possible + * audio formats rather than one fix value. For now, we just + * pick one of them and report that as the only possible + * value for this setting. + * The bit allocation map is in fact compatible to the + * wFormatTag of the v1 AS streaming descriptors, which is why + * we can simply map the matrix. + */ + + for (i = 0; i < 5; i++) + if (format & (1UL << i)) { + format = i + 1; + break; + } + + break; + } + + default: + return -EINVAL; + } + + /* FIXME: correct endianess and sign? */ + pcm_format = -1; + + switch (format) { + case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */ + snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", + chip->dev->devnum, fp->iface, fp->altsetting); + /* fall-through */ + case UAC_FORMAT_TYPE_I_PCM: + if (sample_width > sample_bytes * 8) { + snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", + chip->dev->devnum, fp->iface, fp->altsetting, + sample_width, sample_bytes); + } + /* check the format byte size */ + switch (sample_bytes) { + case 1: + pcm_format = SNDRV_PCM_FORMAT_S8; + break; + case 2: + if (snd_usb_is_big_endian_format(chip, fp)) + pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ + else + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 3: + if (snd_usb_is_big_endian_format(chip, fp)) + pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ + else + pcm_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 4: + pcm_format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", + chip->dev->devnum, fp->iface, fp->altsetting, + sample_width, sample_bytes); + break; + } + break; + case UAC_FORMAT_TYPE_I_PCM8: + pcm_format = SNDRV_PCM_FORMAT_U8; + + /* Dallas DS4201 workaround: it advertises U8 format, but really + supports S8. */ + if (chip->usb_id == USB_ID(0x04fa, 0x4201)) + pcm_format = SNDRV_PCM_FORMAT_S8; + break; + case UAC_FORMAT_TYPE_I_IEEE_FLOAT: + pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; + break; + case UAC_FORMAT_TYPE_I_ALAW: + pcm_format = SNDRV_PCM_FORMAT_A_LAW; + break; + case UAC_FORMAT_TYPE_I_MULAW: + pcm_format = SNDRV_PCM_FORMAT_MU_LAW; + break; + default: + snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", + chip->dev->devnum, fp->iface, fp->altsetting, format); + break; + } + return pcm_format; +} + + +/* + * parse the format descriptor and stores the possible sample rates + * on the audioformat table (audio class v1). + * + * @dev: usb device + * @fp: audioformat record + * @fmt: the format descriptor + * @offset: the start offset of descriptor pointing the rate type + * (7 for type I and II, 8 for type II) + */ +static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp, + unsigned char *fmt, int offset) +{ + int nr_rates = fmt[offset]; + + if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { + snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", + chip->dev->devnum, fp->iface, fp->altsetting); + return -1; + } + + if (nr_rates) { + /* + * build the rate table and bitmap flags + */ + int r, idx; + + fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); + if (fp->rate_table == NULL) { + snd_printk(KERN_ERR "cannot malloc\n"); + return -1; + } + + fp->nr_rates = 0; + fp->rate_min = fp->rate_max = 0; + for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { + unsigned int rate = combine_triple(&fmt[idx]); + if (!rate) + continue; + /* C-Media CM6501 mislabels its 96 kHz altsetting */ + if (rate == 48000 && nr_rates == 1 && + (chip->usb_id == USB_ID(0x0d8c, 0x0201) || + chip->usb_id == USB_ID(0x0d8c, 0x0102)) && + fp->altsetting == 5 && fp->maxpacksize == 392) + rate = 96000; + /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */ + if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068)) + rate = 8000; + + fp->rate_table[fp->nr_rates] = rate; + if (!fp->rate_min || rate < fp->rate_min) + fp->rate_min = rate; + if (!fp->rate_max || rate > fp->rate_max) + fp->rate_max = rate; + fp->rates |= snd_pcm_rate_to_rate_bit(rate); + fp->nr_rates++; + } + if (!fp->nr_rates) { + hwc_debug("All rates were zero. Skipping format!\n"); + return -1; + } + } else { + /* continuous rates */ + fp->rates = SNDRV_PCM_RATE_CONTINUOUS; + fp->rate_min = combine_triple(&fmt[offset + 1]); + fp->rate_max = combine_triple(&fmt[offset + 4]); + } + return 0; +} + +/* + * parse the format descriptor and stores the possible sample rates + * on the audioformat table (audio class v2). + */ +static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, + struct audioformat *fp, + struct usb_host_interface *iface) +{ + struct usb_device *dev = chip->dev; + unsigned char tmp[2], *data; + int i, nr_rates, data_size, ret = 0; + + /* get the number of sample rates first by only fetching 2 bytes */ + ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000); + + if (ret < 0) { + snd_printk(KERN_ERR "unable to retrieve number of sample rates\n"); + goto err; + } + + nr_rates = (tmp[1] << 8) | tmp[0]; + data_size = 2 + 12 * nr_rates; + data = kzalloc(data_size, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + /* now get the full information */ + ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 0x0100, chip->clock_id << 8, data, data_size, 1000); + + if (ret < 0) { + snd_printk(KERN_ERR "unable to retrieve sample rate range\n"); + ret = -EINVAL; + goto err_free; + } + + fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); + if (!fp->rate_table) { + ret = -ENOMEM; + goto err_free; + } + + fp->nr_rates = 0; + fp->rate_min = fp->rate_max = 0; + + for (i = 0; i < nr_rates; i++) { + int rate = combine_quad(&data[2 + 12 * i]); + + fp->rate_table[fp->nr_rates] = rate; + if (!fp->rate_min || rate < fp->rate_min) + fp->rate_min = rate; + if (!fp->rate_max || rate > fp->rate_max) + fp->rate_max = rate; + fp->rates |= snd_pcm_rate_to_rate_bit(rate); + fp->nr_rates++; + } + +err_free: + kfree(data); +err: + return ret; +} + +/* + * parse the format type I and III descriptors + */ +static int parse_audio_format_i(struct snd_usb_audio *chip, + struct audioformat *fp, + int format, void *_fmt, + struct usb_host_interface *iface) +{ + struct usb_interface_descriptor *altsd = get_iface_desc(iface); + struct uac_format_type_i_discrete_descriptor *fmt = _fmt; + int protocol = altsd->bInterfaceProtocol; + int pcm_format, ret; + + if (fmt->bFormatType == UAC_FORMAT_TYPE_III) { + /* FIXME: the format type is really IECxxx + * but we give normal PCM format to get the existing + * apps working... + */ + switch (chip->usb_id) { + + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + if (chip->setup == 0x00 && + fp->altsetting == 6) + pcm_format = SNDRV_PCM_FORMAT_S16_BE; + else + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + } + } else { + pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol); + if (pcm_format < 0) + return -1; + } + + fp->format = pcm_format; + + /* gather possible sample rates */ + /* audio class v1 reports possible sample rates as part of the + * proprietary class specific descriptor. + * audio class v2 uses class specific EP0 range requests for that. + */ + switch (protocol) { + case UAC_VERSION_1: + fp->channels = fmt->bNrChannels; + ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7); + break; + case UAC_VERSION_2: + /* fp->channels is already set in this case */ + ret = parse_audio_format_rates_v2(chip, fp, iface); + break; + } + + if (fp->channels < 1) { + snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", + chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); + return -1; + } + + return ret; +} + +/* + * parse the format type II descriptor + */ +static int parse_audio_format_ii(struct snd_usb_audio *chip, + struct audioformat *fp, + int format, void *_fmt, + struct usb_host_interface *iface) +{ + int brate, framesize, ret; + struct usb_interface_descriptor *altsd = get_iface_desc(iface); + int protocol = altsd->bInterfaceProtocol; + + switch (format) { + case UAC_FORMAT_TYPE_II_AC3: + /* FIXME: there is no AC3 format defined yet */ + // fp->format = SNDRV_PCM_FORMAT_AC3; + fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */ + break; + case UAC_FORMAT_TYPE_II_MPEG: + fp->format = SNDRV_PCM_FORMAT_MPEG; + break; + default: + snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected. processed as MPEG.\n", + chip->dev->devnum, fp->iface, fp->altsetting, format); + fp->format = SNDRV_PCM_FORMAT_MPEG; + break; + } + + fp->channels = 1; + + switch (protocol) { + case UAC_VERSION_1: { + struct uac_format_type_ii_discrete_descriptor *fmt = _fmt; + brate = le16_to_cpu(fmt->wMaxBitRate); + framesize = le16_to_cpu(fmt->wSamplesPerFrame); + snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); + fp->frame_size = framesize; + ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */ + break; + } + case UAC_VERSION_2: { + struct uac_format_type_ii_ext_descriptor *fmt = _fmt; + brate = le16_to_cpu(fmt->wMaxBitRate); + framesize = le16_to_cpu(fmt->wSamplesPerFrame); + snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); + fp->frame_size = framesize; + ret = parse_audio_format_rates_v2(chip, fp, iface); + break; + } + } + + return ret; +} + +int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, + int format, unsigned char *fmt, int stream, + struct usb_host_interface *iface) +{ + int err; + + switch (fmt[3]) { + case UAC_FORMAT_TYPE_I: + case UAC_FORMAT_TYPE_III: + err = parse_audio_format_i(chip, fp, format, fmt, iface); + break; + case UAC_FORMAT_TYPE_II: + err = parse_audio_format_ii(chip, fp, format, fmt, iface); + break; + default: + snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", + chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]); + return -1; + } + fp->fmt_type = fmt[3]; + if (err < 0) + return err; +#if 1 + /* FIXME: temporary hack for extigy/audigy 2 nx/zs */ + /* extigy apparently supports sample rates other than 48k + * but not in ordinary way. so we enable only 48k atm. + */ + if (chip->usb_id == USB_ID(0x041e, 0x3000) || + chip->usb_id == USB_ID(0x041e, 0x3020) || + chip->usb_id == USB_ID(0x041e, 0x3061)) { + if (fmt[3] == UAC_FORMAT_TYPE_I && + fp->rates != SNDRV_PCM_RATE_48000 && + fp->rates != SNDRV_PCM_RATE_96000) + return -1; + } +#endif + return 0; +} + diff --git a/sound/usb/format.h b/sound/usb/format.h new file mode 100644 index 0000000..8298c4e --- /dev/null +++ b/sound/usb/format.h @@ -0,0 +1,8 @@ +#ifndef __USBAUDIO_FORMAT_H +#define __USBAUDIO_FORMAT_H + +int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, + int format, unsigned char *fmt, int stream, + struct usb_host_interface *iface); + +#endif /* __USBAUDIO_FORMAT_H */ diff --git a/sound/usb/helper.c b/sound/usb/helper.c new file mode 100644 index 0000000..ba7dba4 --- /dev/null +++ b/sound/usb/helper.c @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "usbaudio.h" +#include "helper.h" + +/* + * combine bytes and get an integer value + */ +unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size) +{ + switch (size) { + case 1: return *bytes; + case 2: return combine_word(bytes); + case 3: return combine_triple(bytes); + case 4: return combine_quad(bytes); + default: return 0; + } +} + +/* + * parse descriptor buffer and return the pointer starting the given + * descriptor type. + */ +void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype) +{ + u8 *p, *end, *next; + + p = descstart; + end = p + desclen; + for (; p < end;) { + if (p[0] < 2) + return NULL; + next = p + p[0]; + if (next > end) + return NULL; + if (p[1] == dtype && (!after || (void *)p > after)) { + return p; + } + p = next; + } + return NULL; +} + +/* + * find a class-specified interface descriptor with the given subtype. + */ +void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype) +{ + unsigned char *p = after; + + while ((p = snd_usb_find_desc(buffer, buflen, p, + USB_DT_CS_INTERFACE)) != NULL) { + if (p[0] >= 3 && p[2] == dsubtype) + return p; + } + return NULL; +} + +/* + * Wrapper for usb_control_msg(). + * Allocates a temp buffer to prevent dmaing from/to the stack. + */ +int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, + __u8 requesttype, __u16 value, __u16 index, void *data, + __u16 size, int timeout) +{ + int err; + void *buf = NULL; + + if (size > 0) { + buf = kmemdup(data, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + err = usb_control_msg(dev, pipe, request, requesttype, + value, index, buf, size, timeout); + if (size > 0) { + memcpy(data, buf, size); + kfree(buf); + } + return err; +} + +unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, + struct usb_host_interface *alts) +{ + if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH && + get_endpoint(alts, 0)->bInterval >= 1 && + get_endpoint(alts, 0)->bInterval <= 4) + return get_endpoint(alts, 0)->bInterval - 1; + else + return 0; +} + diff --git a/sound/usb/helper.h b/sound/usb/helper.h new file mode 100644 index 0000000..a6b0e51 --- /dev/null +++ b/sound/usb/helper.h @@ -0,0 +1,32 @@ +#ifndef __USBAUDIO_HELPER_H +#define __USBAUDIO_HELPER_H + +unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size); + +void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype); +void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype); + +int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, + __u8 request, __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout); + +unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, + struct usb_host_interface *alts); + +/* + * retrieve usb_interface descriptor from the host interface + * (conditional for compatibility with the older API) + */ +#ifndef get_iface_desc +#define get_iface_desc(iface) (&(iface)->desc) +#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc) +#define get_ep_desc(ep) (&(ep)->desc) +#define get_cfg_desc(cfg) (&(cfg)->desc) +#endif + +#ifndef snd_usb_get_speed +#define snd_usb_get_speed(dev) ((dev)->speed) +#endif + + +#endif /* __USBAUDIO_HELPER_H */ diff --git a/sound/usb/midi.c b/sound/usb/midi.c new file mode 100644 index 0000000..c6ee4a1 --- /dev/null +++ b/sound/usb/midi.c @@ -0,0 +1,2069 @@ +/* + * usbmidi.c - ALSA USB MIDI driver + * + * Copyright (c) 2002-2009 Clemens Ladisch + * All rights reserved. + * + * Based on the OSS usb-midi driver by NAGANO Daisuke, + * NetBSD's umidi driver by Takuya SHIOZAKI, + * the "USB Device Class Definition for MIDI Devices" by Roland + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed and/or modified under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "usbaudio.h" +#include "midi.h" +#include "helper.h" + +/* + * define this to log all USB packets + */ +/* #define DUMP_PACKETS */ + +/* + * how long to wait after some USB errors, so that khubd can disconnect() us + * without too many spurious errors + */ +#define ERROR_DELAY_JIFFIES (HZ / 10) + +#define OUTPUT_URBS 7 +#define INPUT_URBS 7 + + +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_DESCRIPTION("USB Audio/MIDI helper module"); +MODULE_LICENSE("Dual BSD/GPL"); + + +struct usb_ms_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bcdMSC[2]; + __le16 wTotalLength; +} __attribute__ ((packed)); + +struct usb_ms_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bNumEmbMIDIJack; + __u8 baAssocJackID[0]; +} __attribute__ ((packed)); + +struct snd_usb_midi_in_endpoint; +struct snd_usb_midi_out_endpoint; +struct snd_usb_midi_endpoint; + +struct usb_protocol_ops { + void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int); + void (*output)(struct snd_usb_midi_out_endpoint *ep, struct urb *urb); + void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t); + void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*); + void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*); +}; + +struct snd_usb_midi { + struct usb_device *dev; + struct snd_card *card; + struct usb_interface *iface; + const struct snd_usb_audio_quirk *quirk; + struct snd_rawmidi *rmidi; + struct usb_protocol_ops* usb_protocol_ops; + struct list_head list; + struct timer_list error_timer; + spinlock_t disc_lock; + struct mutex mutex; + u32 usb_id; + int next_midi_device; + + struct snd_usb_midi_endpoint { + struct snd_usb_midi_out_endpoint *out; + struct snd_usb_midi_in_endpoint *in; + } endpoints[MIDI_MAX_ENDPOINTS]; + unsigned long input_triggered; + unsigned int opened; + unsigned char disconnected; + + struct snd_kcontrol *roland_load_ctl; +}; + +struct snd_usb_midi_out_endpoint { + struct snd_usb_midi* umidi; + struct out_urb_context { + struct urb *urb; + struct snd_usb_midi_out_endpoint *ep; + } urbs[OUTPUT_URBS]; + unsigned int active_urbs; + unsigned int drain_urbs; + int max_transfer; /* size of urb buffer */ + struct tasklet_struct tasklet; + unsigned int next_urb; + spinlock_t buffer_lock; + + struct usbmidi_out_port { + struct snd_usb_midi_out_endpoint* ep; + struct snd_rawmidi_substream *substream; + int active; + uint8_t cable; /* cable number << 4 */ + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; + } ports[0x10]; + int current_port; + + wait_queue_head_t drain_wait; +}; + +struct snd_usb_midi_in_endpoint { + struct snd_usb_midi* umidi; + struct urb* urbs[INPUT_URBS]; + struct usbmidi_in_port { + struct snd_rawmidi_substream *substream; + u8 running_status_length; + } ports[0x10]; + u8 seen_f5; + u8 error_resubmit; + int current_port; +}; + +static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep); + +static const uint8_t snd_usbmidi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Submits the URB, with error handling. + */ +static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags) +{ + int err = usb_submit_urb(urb, flags); + if (err < 0 && err != -ENODEV) + snd_printk(KERN_ERR "usb_submit_urb: %d\n", err); + return err; +} + +/* + * Error handling for URB completion functions. + */ +static int snd_usbmidi_urb_error(int status) +{ + switch (status) { + /* manually unlinked, or device gone */ + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + case -ENODEV: + return -ENODEV; + /* errors that might occur during unplugging */ + case -EPROTO: + case -ETIME: + case -EILSEQ: + return -EIO; + default: + snd_printk(KERN_ERR "urb status %d\n", status); + return 0; /* continue */ + } +} + +/* + * Receives a chunk of MIDI data. + */ +static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint* ep, int portidx, + uint8_t* data, int length) +{ + struct usbmidi_in_port* port = &ep->ports[portidx]; + + if (!port->substream) { + snd_printd("unexpected port %d!\n", portidx); + return; + } + if (!test_bit(port->substream->number, &ep->umidi->input_triggered)) + return; + snd_rawmidi_receive(port->substream, data, length); +} + +#ifdef DUMP_PACKETS +static void dump_urb(const char *type, const u8 *data, int length) +{ + snd_printk(KERN_DEBUG "%s packet: [", type); + for (; length > 0; ++data, --length) + printk(" %02x", *data); + printk(" ]\n"); +} +#else +#define dump_urb(type, data, length) /* nothing */ +#endif + +/* + * Processes the data read from the device. + */ +static void snd_usbmidi_in_urb_complete(struct urb* urb) +{ + struct snd_usb_midi_in_endpoint* ep = urb->context; + + if (urb->status == 0) { + dump_urb("received", urb->transfer_buffer, urb->actual_length); + ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer, + urb->actual_length); + } else { + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) { + ep->error_resubmit = 1; + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); + } + return; + } + } + + urb->dev = ep->umidi->dev; + snd_usbmidi_submit_urb(urb, GFP_ATOMIC); +} + +static void snd_usbmidi_out_urb_complete(struct urb* urb) +{ + struct out_urb_context *context = urb->context; + struct snd_usb_midi_out_endpoint* ep = context->ep; + unsigned int urb_index; + + spin_lock(&ep->buffer_lock); + urb_index = context - ep->urbs; + ep->active_urbs &= ~(1 << urb_index); + if (unlikely(ep->drain_urbs)) { + ep->drain_urbs &= ~(1 << urb_index); + wake_up(&ep->drain_wait); + } + spin_unlock(&ep->buffer_lock); + if (urb->status < 0) { + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); + return; + } + } + snd_usbmidi_do_output(ep); +} + +/* + * This is called when some data should be transferred to the device + * (from one or more substreams). + */ +static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep) +{ + unsigned int urb_index; + struct urb* urb; + unsigned long flags; + + spin_lock_irqsave(&ep->buffer_lock, flags); + if (ep->umidi->disconnected) { + spin_unlock_irqrestore(&ep->buffer_lock, flags); + return; + } + + urb_index = ep->next_urb; + for (;;) { + if (!(ep->active_urbs & (1 << urb_index))) { + urb = ep->urbs[urb_index].urb; + urb->transfer_buffer_length = 0; + ep->umidi->usb_protocol_ops->output(ep, urb); + if (urb->transfer_buffer_length == 0) + break; + + dump_urb("sending", urb->transfer_buffer, + urb->transfer_buffer_length); + urb->dev = ep->umidi->dev; + if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0) + break; + ep->active_urbs |= 1 << urb_index; + } + if (++urb_index >= OUTPUT_URBS) + urb_index = 0; + if (urb_index == ep->next_urb) + break; + } + ep->next_urb = urb_index; + spin_unlock_irqrestore(&ep->buffer_lock, flags); +} + +static void snd_usbmidi_out_tasklet(unsigned long data) +{ + struct snd_usb_midi_out_endpoint* ep = (struct snd_usb_midi_out_endpoint *) data; + + snd_usbmidi_do_output(ep); +} + +/* called after transfers had been interrupted due to some USB error */ +static void snd_usbmidi_error_timer(unsigned long data) +{ + struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; + unsigned int i, j; + + spin_lock(&umidi->disc_lock); + if (umidi->disconnected) { + spin_unlock(&umidi->disc_lock); + return; + } + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in; + if (in && in->error_resubmit) { + in->error_resubmit = 0; + for (j = 0; j < INPUT_URBS; ++j) { + in->urbs[j]->dev = umidi->dev; + snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); + } + } + if (umidi->endpoints[i].out) + snd_usbmidi_do_output(umidi->endpoints[i].out); + } + spin_unlock(&umidi->disc_lock); +} + +/* helper function to send static data that may not DMA-able */ +static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep, + const void *data, int len) +{ + int err = 0; + void *buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + dump_urb("sending", buf, len); + if (ep->urbs[0].urb) + err = usb_bulk_msg(ep->umidi->dev, ep->urbs[0].urb->pipe, + buf, len, NULL, 250); + kfree(buf); + return err; +} + +/* + * Standard USB MIDI protocol: see the spec. + * Midiman protocol: like the standard protocol, but the control byte is the + * fourth byte in each packet, and uses length instead of CIN. + */ + +static void snd_usbmidi_standard_input(struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + int i; + + for (i = 0; i + 3 < buffer_length; i += 4) + if (buffer[i] != 0) { + int cable = buffer[i] >> 4; + int length = snd_usbmidi_cin_length[buffer[i] & 0x0f]; + snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); + } +} + +static void snd_usbmidi_midiman_input(struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + int i; + + for (i = 0; i + 3 < buffer_length; i += 4) + if (buffer[i + 3] != 0) { + int port = buffer[i + 3] >> 4; + int length = buffer[i + 3] & 3; + snd_usbmidi_input_data(ep, port, &buffer[i], length); + } +} + +/* + * Buggy M-Audio device: running status on input results in a packet that has + * the data bytes but not the status byte and that is marked with CIN 4. + */ +static void snd_usbmidi_maudio_broken_running_status_input( + struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + int i; + + for (i = 0; i + 3 < buffer_length; i += 4) + if (buffer[i] != 0) { + int cable = buffer[i] >> 4; + u8 cin = buffer[i] & 0x0f; + struct usbmidi_in_port *port = &ep->ports[cable]; + int length; + + length = snd_usbmidi_cin_length[cin]; + if (cin == 0xf && buffer[i + 1] >= 0xf8) + ; /* realtime msg: no running status change */ + else if (cin >= 0x8 && cin <= 0xe) + /* channel msg */ + port->running_status_length = length - 1; + else if (cin == 0x4 && + port->running_status_length != 0 && + buffer[i + 1] < 0x80) + /* CIN 4 that is not a SysEx */ + length = port->running_status_length; + else + /* + * All other msgs cannot begin running status. + * (A channel msg sent as two or three CIN 0xF + * packets could in theory, but this device + * doesn't use this format.) + */ + port->running_status_length = 0; + snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); + } +} + +/* + * CME protocol: like the standard protocol, but SysEx commands are sent as a + * single USB packet preceded by a 0x0F byte. + */ +static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f) + snd_usbmidi_standard_input(ep, buffer, buffer_length); + else + snd_usbmidi_input_data(ep, buffer[0] >> 4, + &buffer[1], buffer_length - 1); +} + +/* + * Adds one USB MIDI packet to the output buffer. + */ +static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + + uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length; + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + urb->transfer_buffer_length += 4; +} + +/* + * Adds one Midiman packet to the output buffer. + */ +static void snd_usbmidi_output_midiman_packet(struct urb* urb, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + + uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length; + buf[0] = p1; + buf[1] = p2; + buf[2] = p3; + buf[3] = (p0 & 0xf0) | snd_usbmidi_cin_length[p0 & 0x0f]; + urb->transfer_buffer_length += 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void snd_usbmidi_transmit_byte(struct usbmidi_out_port* port, + uint8_t b, struct urb* urb) +{ + uint8_t p0 = port->cable; + void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t) = + port->ep->umidi->usb_protocol_ops->output_packet; + + if (b >= 0xf8) { + output_packet(urb, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + output_packet(urb, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + output_packet(urb, p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + output_packet(urb, p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + output_packet(urb, p0 | 0x07, port->data[0], port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + output_packet(urb, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + output_packet(urb, p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + output_packet(urb, p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) +{ + int p; + + /* FIXME: lower-numbered ports can starve higher-numbered ports */ + for (p = 0; p < 0x10; ++p) { + struct usbmidi_out_port* port = &ep->ports[p]; + if (!port->active) + continue; + while (urb->transfer_buffer_length + 3 < ep->max_transfer) { + uint8_t b; + if (snd_rawmidi_transmit(port->substream, &b, 1) != 1) { + port->active = 0; + break; + } + snd_usbmidi_transmit_byte(port, b, urb); + } + } +} + +static struct usb_protocol_ops snd_usbmidi_standard_ops = { + .input = snd_usbmidi_standard_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + +static struct usb_protocol_ops snd_usbmidi_midiman_ops = { + .input = snd_usbmidi_midiman_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_midiman_packet, +}; + +static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = { + .input = snd_usbmidi_maudio_broken_running_status_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + +static struct usb_protocol_ops snd_usbmidi_cme_ops = { + .input = snd_usbmidi_cme_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + +/* + * Novation USB MIDI protocol: number of data bytes is in the first byte + * (when receiving) (+1!) or in the second byte (when sending); data begins + * at the third byte. + */ + +static void snd_usbmidi_novation_input(struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + if (buffer_length < 2 || !buffer[0] || buffer_length < buffer[0] + 1) + return; + snd_usbmidi_input_data(ep, 0, &buffer[2], buffer[0] - 1); +} + +static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) +{ + uint8_t* transfer_buffer; + int count; + + if (!ep->ports[0].active) + return; + transfer_buffer = urb->transfer_buffer; + count = snd_rawmidi_transmit(ep->ports[0].substream, + &transfer_buffer[2], + ep->max_transfer - 2); + if (count < 1) { + ep->ports[0].active = 0; + return; + } + transfer_buffer[0] = 0; + transfer_buffer[1] = count; + urb->transfer_buffer_length = 2 + count; +} + +static struct usb_protocol_ops snd_usbmidi_novation_ops = { + .input = snd_usbmidi_novation_input, + .output = snd_usbmidi_novation_output, +}; + +/* + * "raw" protocol: used by the MOTU FastLane. + */ + +static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + snd_usbmidi_input_data(ep, 0, buffer, buffer_length); +} + +static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) +{ + int count; + + if (!ep->ports[0].active) + return; + count = snd_rawmidi_transmit(ep->ports[0].substream, + urb->transfer_buffer, + ep->max_transfer); + if (count < 1) { + ep->ports[0].active = 0; + return; + } + urb->transfer_buffer_length = count; +} + +static struct usb_protocol_ops snd_usbmidi_raw_ops = { + .input = snd_usbmidi_raw_input, + .output = snd_usbmidi_raw_output, +}; + +static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + if (buffer_length != 9) + return; + buffer_length = 8; + while (buffer_length && buffer[buffer_length - 1] == 0xFD) + buffer_length--; + if (buffer_length) + snd_usbmidi_input_data(ep, 0, buffer, buffer_length); +} + +static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, + struct urb *urb) +{ + int count; + + if (!ep->ports[0].active) + return; + count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2; + count = snd_rawmidi_transmit(ep->ports[0].substream, + urb->transfer_buffer, + count); + if (count < 1) { + ep->ports[0].active = 0; + return; + } + + memset(urb->transfer_buffer + count, 0xFD, 9 - count); + urb->transfer_buffer_length = count; +} + +static struct usb_protocol_ops snd_usbmidi_122l_ops = { + .input = snd_usbmidi_us122l_input, + .output = snd_usbmidi_us122l_output, +}; + +/* + * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching. + */ + +static void snd_usbmidi_emagic_init_out(struct snd_usb_midi_out_endpoint* ep) +{ + static const u8 init_data[] = { + /* initialization magic: "get version" */ + 0xf0, + 0x00, 0x20, 0x31, /* Emagic */ + 0x64, /* Unitor8 */ + 0x0b, /* version number request */ + 0x00, /* command version */ + 0x00, /* EEPROM, box 0 */ + 0xf7 + }; + send_bulk_static_data(ep, init_data, sizeof(init_data)); + /* while we're at it, pour on more magic */ + send_bulk_static_data(ep, init_data, sizeof(init_data)); +} + +static void snd_usbmidi_emagic_finish_out(struct snd_usb_midi_out_endpoint* ep) +{ + static const u8 finish_data[] = { + /* switch to patch mode with last preset */ + 0xf0, + 0x00, 0x20, 0x31, /* Emagic */ + 0x64, /* Unitor8 */ + 0x10, /* patch switch command */ + 0x00, /* command version */ + 0x7f, /* to all boxes */ + 0x40, /* last preset in EEPROM */ + 0xf7 + }; + send_bulk_static_data(ep, finish_data, sizeof(finish_data)); +} + +static void snd_usbmidi_emagic_input(struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + int i; + + /* FF indicates end of valid data */ + for (i = 0; i < buffer_length; ++i) + if (buffer[i] == 0xff) { + buffer_length = i; + break; + } + + /* handle F5 at end of last buffer */ + if (ep->seen_f5) + goto switch_port; + + while (buffer_length > 0) { + /* determine size of data until next F5 */ + for (i = 0; i < buffer_length; ++i) + if (buffer[i] == 0xf5) + break; + snd_usbmidi_input_data(ep, ep->current_port, buffer, i); + buffer += i; + buffer_length -= i; + + if (buffer_length <= 0) + break; + /* assert(buffer[0] == 0xf5); */ + ep->seen_f5 = 1; + ++buffer; + --buffer_length; + + switch_port: + if (buffer_length <= 0) + break; + if (buffer[0] < 0x80) { + ep->current_port = (buffer[0] - 1) & 15; + ++buffer; + --buffer_length; + } + ep->seen_f5 = 0; + } +} + +static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) +{ + int port0 = ep->current_port; + uint8_t* buf = urb->transfer_buffer; + int buf_free = ep->max_transfer; + int length, i; + + for (i = 0; i < 0x10; ++i) { + /* round-robin, starting at the last current port */ + int portnum = (port0 + i) & 15; + struct usbmidi_out_port* port = &ep->ports[portnum]; + + if (!port->active) + continue; + if (snd_rawmidi_transmit_peek(port->substream, buf, 1) != 1) { + port->active = 0; + continue; + } + + if (portnum != ep->current_port) { + if (buf_free < 2) + break; + ep->current_port = portnum; + buf[0] = 0xf5; + buf[1] = (portnum + 1) & 15; + buf += 2; + buf_free -= 2; + } + + if (buf_free < 1) + break; + length = snd_rawmidi_transmit(port->substream, buf, buf_free); + if (length > 0) { + buf += length; + buf_free -= length; + if (buf_free < 1) + break; + } + } + if (buf_free < ep->max_transfer && buf_free > 0) { + *buf = 0xff; + --buf_free; + } + urb->transfer_buffer_length = ep->max_transfer - buf_free; +} + +static struct usb_protocol_ops snd_usbmidi_emagic_ops = { + .input = snd_usbmidi_emagic_input, + .output = snd_usbmidi_emagic_output, + .init_out_endpoint = snd_usbmidi_emagic_init_out, + .finish_out_endpoint = snd_usbmidi_emagic_finish_out, +}; + + +static void update_roland_altsetting(struct snd_usb_midi* umidi) +{ + struct usb_interface *intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor *intfd; + int is_light_load; + + intf = umidi->iface; + is_light_load = intf->cur_altsetting != intf->altsetting; + if (umidi->roland_load_ctl->private_value == is_light_load) + return; + hostif = &intf->altsetting[umidi->roland_load_ctl->private_value]; + intfd = get_iface_desc(hostif); + snd_usbmidi_input_stop(&umidi->list); + usb_set_interface(umidi->dev, intfd->bInterfaceNumber, + intfd->bAlternateSetting); + snd_usbmidi_input_start(&umidi->list); +} + +static void substream_open(struct snd_rawmidi_substream *substream, int open) +{ + struct snd_usb_midi* umidi = substream->rmidi->private_data; + struct snd_kcontrol *ctl; + + mutex_lock(&umidi->mutex); + if (open) { + if (umidi->opened++ == 0 && umidi->roland_load_ctl) { + ctl = umidi->roland_load_ctl; + ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(umidi->card, + SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + update_roland_altsetting(umidi); + } + } else { + if (--umidi->opened == 0 && umidi->roland_load_ctl) { + ctl = umidi->roland_load_ctl; + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(umidi->card, + SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + } + } + mutex_unlock(&umidi->mutex); +} + +static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) +{ + struct snd_usb_midi* umidi = substream->rmidi->private_data; + struct usbmidi_out_port* port = NULL; + int i, j; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + if (umidi->endpoints[i].out) + for (j = 0; j < 0x10; ++j) + if (umidi->endpoints[i].out->ports[j].substream == substream) { + port = &umidi->endpoints[i].out->ports[j]; + break; + } + if (!port) { + snd_BUG(); + return -ENXIO; + } + substream->runtime->private_data = port; + port->state = STATE_UNKNOWN; + substream_open(substream, 1); + return 0; +} + +static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) +{ + substream_open(substream, 0); + return 0; +} + +static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct usbmidi_out_port* port = (struct usbmidi_out_port*)substream->runtime->private_data; + + port->active = up; + if (up) { + if (port->ep->umidi->disconnected) { + /* gobble up remaining bytes to prevent wait in + * snd_rawmidi_drain_output */ + while (!snd_rawmidi_transmit_empty(substream)) + snd_rawmidi_transmit_ack(substream, 1); + return; + } + tasklet_schedule(&port->ep->tasklet); + } +} + +static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct usbmidi_out_port* port = substream->runtime->private_data; + struct snd_usb_midi_out_endpoint *ep = port->ep; + unsigned int drain_urbs; + DEFINE_WAIT(wait); + long timeout = msecs_to_jiffies(50); + + /* + * The substream buffer is empty, but some data might still be in the + * currently active URBs, so we have to wait for those to complete. + */ + spin_lock_irq(&ep->buffer_lock); + drain_urbs = ep->active_urbs; + if (drain_urbs) { + ep->drain_urbs |= drain_urbs; + do { + prepare_to_wait(&ep->drain_wait, &wait, + TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&ep->buffer_lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&ep->buffer_lock); + drain_urbs &= ep->drain_urbs; + } while (drain_urbs && timeout); + finish_wait(&ep->drain_wait, &wait); + } + spin_unlock_irq(&ep->buffer_lock); +} + +static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) +{ + substream_open(substream, 1); + return 0; +} + +static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) +{ + substream_open(substream, 0); + return 0; +} + +static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct snd_usb_midi* umidi = substream->rmidi->private_data; + + if (up) + set_bit(substream->number, &umidi->input_triggered); + else + clear_bit(substream->number, &umidi->input_triggered); +} + +static struct snd_rawmidi_ops snd_usbmidi_output_ops = { + .open = snd_usbmidi_output_open, + .close = snd_usbmidi_output_close, + .trigger = snd_usbmidi_output_trigger, + .drain = snd_usbmidi_output_drain, +}; + +static struct snd_rawmidi_ops snd_usbmidi_input_ops = { + .open = snd_usbmidi_input_open, + .close = snd_usbmidi_input_close, + .trigger = snd_usbmidi_input_trigger +}; + +static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb, + unsigned int buffer_length) +{ + usb_buffer_free(umidi->dev, buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +/* + * Frees an input endpoint. + * May be called when ep hasn't been initialized completely. + */ +static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep) +{ + unsigned int i; + + for (i = 0; i < INPUT_URBS; ++i) + if (ep->urbs[i]) + free_urb_and_buffer(ep->umidi, ep->urbs[i], + ep->urbs[i]->transfer_buffer_length); + kfree(ep); +} + +/* + * Creates an input endpoint. + */ +static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* ep_info, + struct snd_usb_midi_endpoint* rep) +{ + struct snd_usb_midi_in_endpoint* ep; + void* buffer; + unsigned int pipe; + int length; + unsigned int i; + + rep->in = NULL; + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + ep->umidi = umidi; + + for (i = 0; i < INPUT_URBS; ++i) { + ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urbs[i]) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } + } + if (ep_info->in_interval) + pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep); + else + pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep); + length = usb_maxpacket(umidi->dev, pipe, 0); + for (i = 0; i < INPUT_URBS; ++i) { + buffer = usb_buffer_alloc(umidi->dev, length, GFP_KERNEL, + &ep->urbs[i]->transfer_dma); + if (!buffer) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } + if (ep_info->in_interval) + usb_fill_int_urb(ep->urbs[i], umidi->dev, + pipe, buffer, length, + snd_usbmidi_in_urb_complete, + ep, ep_info->in_interval); + else + usb_fill_bulk_urb(ep->urbs[i], umidi->dev, + pipe, buffer, length, + snd_usbmidi_in_urb_complete, ep); + ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + rep->in = ep; + return 0; +} + +/* + * Frees an output endpoint. + * May be called when ep hasn't been initialized completely. + */ +static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep) +{ + unsigned int i; + + for (i = 0; i < OUTPUT_URBS; ++i) + if (ep->urbs[i].urb) + free_urb_and_buffer(ep->umidi, ep->urbs[i].urb, + ep->max_transfer); + kfree(ep); +} + +/* + * Creates an output endpoint, and initializes output ports. + */ +static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* ep_info, + struct snd_usb_midi_endpoint* rep) +{ + struct snd_usb_midi_out_endpoint* ep; + unsigned int i; + unsigned int pipe; + void* buffer; + + rep->out = NULL; + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + ep->umidi = umidi; + + for (i = 0; i < OUTPUT_URBS; ++i) { + ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urbs[i].urb) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + ep->urbs[i].ep = ep; + } + if (ep_info->out_interval) + pipe = usb_sndintpipe(umidi->dev, ep_info->out_ep); + else + pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep); + switch (umidi->usb_id) { + default: + ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1); + break; + /* + * Various chips declare a packet size larger than 4 bytes, but + * do not actually work with larger packets: + */ + case USB_ID(0x0a92, 0x1020): /* ESI M4U */ + case USB_ID(0x1430, 0x474b): /* RedOctane GH MIDI INTERFACE */ + case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */ + case USB_ID(0x15ca, 0x1806): /* Textech USB Midi Cable */ + case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */ + ep->max_transfer = 4; + break; + } + for (i = 0; i < OUTPUT_URBS; ++i) { + buffer = usb_buffer_alloc(umidi->dev, + ep->max_transfer, GFP_KERNEL, + &ep->urbs[i].urb->transfer_dma); + if (!buffer) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + if (ep_info->out_interval) + usb_fill_int_urb(ep->urbs[i].urb, umidi->dev, + pipe, buffer, ep->max_transfer, + snd_usbmidi_out_urb_complete, + &ep->urbs[i], ep_info->out_interval); + else + usb_fill_bulk_urb(ep->urbs[i].urb, umidi->dev, + pipe, buffer, ep->max_transfer, + snd_usbmidi_out_urb_complete, + &ep->urbs[i]); + ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + spin_lock_init(&ep->buffer_lock); + tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); + init_waitqueue_head(&ep->drain_wait); + + for (i = 0; i < 0x10; ++i) + if (ep_info->out_cables & (1 << i)) { + ep->ports[i].ep = ep; + ep->ports[i].cable = i << 4; + } + + if (umidi->usb_protocol_ops->init_out_endpoint) + umidi->usb_protocol_ops->init_out_endpoint(ep); + + rep->out = ep; + return 0; +} + +/* + * Frees everything. + */ +static void snd_usbmidi_free(struct snd_usb_midi* umidi) +{ + int i; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; + if (ep->out) + snd_usbmidi_out_endpoint_delete(ep->out); + if (ep->in) + snd_usbmidi_in_endpoint_delete(ep->in); + } + mutex_destroy(&umidi->mutex); + kfree(umidi); +} + +/* + * Unlinks all URBs (must be done before the usb_device is deleted). + */ +void snd_usbmidi_disconnect(struct list_head* p) +{ + struct snd_usb_midi* umidi; + unsigned int i, j; + + umidi = list_entry(p, struct snd_usb_midi, list); + /* + * an URB's completion handler may start the timer and + * a timer may submit an URB. To reliably break the cycle + * a flag under lock must be used + */ + spin_lock_irq(&umidi->disc_lock); + umidi->disconnected = 1; + spin_unlock_irq(&umidi->disc_lock); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; + if (ep->out) + tasklet_kill(&ep->out->tasklet); + if (ep->out) { + for (j = 0; j < OUTPUT_URBS; ++j) + usb_kill_urb(ep->out->urbs[j].urb); + if (umidi->usb_protocol_ops->finish_out_endpoint) + umidi->usb_protocol_ops->finish_out_endpoint(ep->out); + } + if (ep->in) + for (j = 0; j < INPUT_URBS; ++j) + usb_kill_urb(ep->in->urbs[j]); + /* free endpoints here; later call can result in Oops */ + if (ep->out) { + snd_usbmidi_out_endpoint_delete(ep->out); + ep->out = NULL; + } + if (ep->in) { + snd_usbmidi_in_endpoint_delete(ep->in); + ep->in = NULL; + } + } + del_timer_sync(&umidi->error_timer); +} + +static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi) +{ + struct snd_usb_midi* umidi = rmidi->private_data; + snd_usbmidi_free(umidi); +} + +static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi, + int stream, int number) +{ + struct list_head* list; + + list_for_each(list, &umidi->rmidi->streams[stream].substreams) { + struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list); + if (substream->number == number) + return substream; + } + return NULL; +} + +/* + * This list specifies names for ports that do not fit into the standard + * "(product) MIDI (n)" schema because they aren't external MIDI ports, + * such as internal control or synthesizer ports. + */ +static struct port_info { + u32 id; + short int port; + short int voices; + const char *name; + unsigned int seq_flags; +} snd_usbmidi_port_info[] = { +#define PORT_INFO(vendor, product, num, name_, voices_, flags) \ + { .id = USB_ID(vendor, product), \ + .port = num, .voices = voices_, \ + .name = name_, .seq_flags = flags } +#define EXTERNAL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_PORT) +#define CONTROL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE) +#define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) +#define SOUNDCANVAS_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) + /* Roland UA-100 */ + CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"), + /* Roland SC-8850 */ + SOUNDCANVAS_PORT(0x0582, 0x0003, 0, "%s Part A", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 1, "%s Part B", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 2, "%s Part C", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 3, "%s Part D", 128), + EXTERNAL_PORT(0x0582, 0x0003, 4, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0003, 5, "%s MIDI 2"), + /* Roland U-8 */ + EXTERNAL_PORT(0x0582, 0x0004, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0004, 1, "%s Control"), + /* Roland SC-8820 */ + SOUNDCANVAS_PORT(0x0582, 0x0007, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x0007, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0007, 2, "%s MIDI"), + /* Roland SK-500 */ + SOUNDCANVAS_PORT(0x0582, 0x000b, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000b, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000b, 2, "%s MIDI"), + /* Roland SC-D70 */ + SOUNDCANVAS_PORT(0x0582, 0x000c, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000c, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000c, 2, "%s MIDI"), + /* Edirol UM-880 */ + CONTROL_PORT(0x0582, 0x0014, 8, "%s Control"), + /* Edirol SD-90 */ + ROLAND_SYNTH_PORT(0x0582, 0x0016, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0016, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0016, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0016, 3, "%s MIDI 2"), + /* Edirol UM-550 */ + CONTROL_PORT(0x0582, 0x0023, 5, "%s Control"), + /* Edirol SD-20 */ + ROLAND_SYNTH_PORT(0x0582, 0x0027, 0, "%s Part A", 64), + ROLAND_SYNTH_PORT(0x0582, 0x0027, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0027, 2, "%s MIDI"), + /* Edirol SD-80 */ + ROLAND_SYNTH_PORT(0x0582, 0x0029, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0029, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0029, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0029, 3, "%s MIDI 2"), + /* Edirol UA-700 */ + EXTERNAL_PORT(0x0582, 0x002b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x002b, 1, "%s Control"), + /* Roland VariOS */ + EXTERNAL_PORT(0x0582, 0x002f, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 1, "%s External MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 2, "%s Sync"), + /* Edirol PCR */ + EXTERNAL_PORT(0x0582, 0x0033, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0033, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0033, 2, "%s 2"), + /* BOSS GS-10 */ + EXTERNAL_PORT(0x0582, 0x003b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x003b, 1, "%s Control"), + /* Edirol UA-1000 */ + EXTERNAL_PORT(0x0582, 0x0044, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0044, 1, "%s Control"), + /* Edirol UR-80 */ + EXTERNAL_PORT(0x0582, 0x0048, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0048, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0048, 2, "%s 2"), + /* Edirol PCR-A */ + EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"), + /* Edirol UM-3EX */ + CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"), + /* M-Audio MidiSport 8x8 */ + CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"), + CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"), + /* MOTU Fastlane */ + EXTERNAL_PORT(0x07fd, 0x0001, 0, "%s MIDI A"), + EXTERNAL_PORT(0x07fd, 0x0001, 1, "%s MIDI B"), + /* Emagic Unitor8/AMT8/MT4 */ + EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"), + /* Access Music Virus TI */ + EXTERNAL_PORT(0x133e, 0x0815, 0, "%s MIDI"), + PORT_INFO(0x133e, 0x0815, 1, "%s Synth", 0, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER), +}; + +static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) { + if (snd_usbmidi_port_info[i].id == umidi->usb_id && + snd_usbmidi_port_info[i].port == number) + return &snd_usbmidi_port_info[i]; + } + return NULL; +} + +static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number, + struct snd_seq_port_info *seq_port_info) +{ + struct snd_usb_midi *umidi = rmidi->private_data; + struct port_info *port_info; + + /* TODO: read port flags from descriptors */ + port_info = find_port_info(umidi, number); + if (port_info) { + seq_port_info->type = port_info->seq_flags; + seq_port_info->midi_voices = port_info->voices; + } +} + +static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi, + int stream, int number, + struct snd_rawmidi_substream ** rsubstream) +{ + struct port_info *port_info; + const char *name_format; + + struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number); + if (!substream) { + snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); + return; + } + + /* TODO: read port name from jack descriptor */ + port_info = find_port_info(umidi, number); + name_format = port_info ? port_info->name : "%s MIDI %d"; + snprintf(substream->name, sizeof(substream->name), + name_format, umidi->card->shortname, number + 1); + + *rsubstream = substream; +} + +/* + * Creates the endpoints and their ports. + */ +static int snd_usbmidi_create_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) +{ + int i, j, err; + int out_ports = 0, in_ports = 0; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + if (endpoints[i].out_cables) { + err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i], + &umidi->endpoints[i]); + if (err < 0) + return err; + } + if (endpoints[i].in_cables) { + err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i], + &umidi->endpoints[i]); + if (err < 0) + return err; + } + + for (j = 0; j < 0x10; ++j) { + if (endpoints[i].out_cables & (1 << j)) { + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, out_ports, + &umidi->endpoints[i].out->ports[j].substream); + ++out_ports; + } + if (endpoints[i].in_cables & (1 << j)) { + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, in_ports, + &umidi->endpoints[i].in->ports[j].substream); + ++in_ports; + } + } + } + snd_printdd(KERN_INFO "created %d output and %d input ports\n", + out_ports, in_ports); + return 0; +} + +/* + * Returns MIDIStreaming device capabilities. + */ +static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_ms_header_descriptor* ms_header; + struct usb_host_endpoint *hostep; + struct usb_endpoint_descriptor* ep; + struct usb_ms_endpoint_descriptor* ms_ep; + int i, epidx; + + intf = umidi->iface; + if (!intf) + return -ENXIO; + hostif = &intf->altsetting[0]; + intfd = get_iface_desc(hostif); + ms_header = (struct usb_ms_header_descriptor*)hostif->extra; + if (hostif->extralen >= 7 && + ms_header->bLength >= 7 && + ms_header->bDescriptorType == USB_DT_CS_INTERFACE && + ms_header->bDescriptorSubtype == UAC_HEADER) + snd_printdd(KERN_INFO "MIDIStreaming version %02x.%02x\n", + ms_header->bcdMSC[1], ms_header->bcdMSC[0]); + else + snd_printk(KERN_WARNING "MIDIStreaming interface descriptor not found\n"); + + epidx = 0; + for (i = 0; i < intfd->bNumEndpoints; ++i) { + hostep = &hostif->endpoint[i]; + ep = get_ep_desc(hostep); + if (!usb_endpoint_xfer_bulk(ep) && !usb_endpoint_xfer_int(ep)) + continue; + ms_ep = (struct usb_ms_endpoint_descriptor*)hostep->extra; + if (hostep->extralen < 4 || + ms_ep->bLength < 4 || + ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || + ms_ep->bDescriptorSubtype != UAC_MS_GENERAL) + continue; + if (usb_endpoint_dir_out(ep)) { + if (endpoints[epidx].out_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + snd_printk(KERN_WARNING "too many endpoints\n"); + break; + } + } + endpoints[epidx].out_ep = usb_endpoint_num(ep); + if (usb_endpoint_xfer_int(ep)) + endpoints[epidx].out_interval = ep->bInterval; + else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW) + /* + * Low speed bulk transfers don't exist, so + * force interrupt transfers for devices like + * ESI MIDI Mate that try to use them anyway. + */ + endpoints[epidx].out_interval = 1; + endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); + } else { + if (endpoints[epidx].in_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + snd_printk(KERN_WARNING "too many endpoints\n"); + break; + } + } + endpoints[epidx].in_ep = usb_endpoint_num(ep); + if (usb_endpoint_xfer_int(ep)) + endpoints[epidx].in_interval = ep->bInterval; + else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW) + endpoints[epidx].in_interval = 1; + endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); + } + } + return 0; +} + +static int roland_load_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[] = { "High Load", "Light Load" }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item > 1) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int roland_load_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + value->value.enumerated.item[0] = kcontrol->private_value; + return 0; +} + +static int roland_load_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_usb_midi* umidi = kcontrol->private_data; + int changed; + + if (value->value.enumerated.item[0] > 1) + return -EINVAL; + mutex_lock(&umidi->mutex); + changed = value->value.enumerated.item[0] != kcontrol->private_value; + if (changed) + kcontrol->private_value = value->value.enumerated.item[0]; + mutex_unlock(&umidi->mutex); + return changed; +} + +static struct snd_kcontrol_new roland_load_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "MIDI Input Mode", + .info = roland_load_info, + .get = roland_load_get, + .put = roland_load_put, + .private_value = 1, +}; + +/* + * On Roland devices, use the second alternate setting to be able to use + * the interrupt input endpoint. + */ +static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + + intf = umidi->iface; + if (!intf || intf->num_altsetting != 2) + return; + + hostif = &intf->altsetting[1]; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints != 2 || + (get_endpoint(hostif, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK || + (get_endpoint(hostif, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + return; + + snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n", + intfd->bAlternateSetting); + usb_set_interface(umidi->dev, intfd->bInterfaceNumber, + intfd->bAlternateSetting); + + umidi->roland_load_ctl = snd_ctl_new1(&roland_load_ctl, umidi); + if (snd_ctl_add(umidi->card, umidi->roland_load_ctl) < 0) + umidi->roland_load_ctl = NULL; +} + +/* + * Try to find any usable endpoints in the interface. + */ +static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint, + int max_endpoints) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_endpoint_descriptor* epd; + int i, out_eps = 0, in_eps = 0; + + if (USB_ID_VENDOR(umidi->usb_id) == 0x0582) + snd_usbmidi_switch_roland_altsetting(umidi); + + if (endpoint[0].out_ep || endpoint[0].in_ep) + return 0; + + intf = umidi->iface; + if (!intf || intf->num_altsetting < 1) + return -ENOENT; + hostif = intf->cur_altsetting; + intfd = get_iface_desc(hostif); + + for (i = 0; i < intfd->bNumEndpoints; ++i) { + epd = get_endpoint(hostif, i); + if (!usb_endpoint_xfer_bulk(epd) && + !usb_endpoint_xfer_int(epd)) + continue; + if (out_eps < max_endpoints && + usb_endpoint_dir_out(epd)) { + endpoint[out_eps].out_ep = usb_endpoint_num(epd); + if (usb_endpoint_xfer_int(epd)) + endpoint[out_eps].out_interval = epd->bInterval; + ++out_eps; + } + if (in_eps < max_endpoints && + usb_endpoint_dir_in(epd)) { + endpoint[in_eps].in_ep = usb_endpoint_num(epd); + if (usb_endpoint_xfer_int(epd)) + endpoint[in_eps].in_interval = epd->bInterval; + ++in_eps; + } + } + return (out_eps || in_eps) ? 0 : -ENOENT; +} + +/* + * Detects the endpoints for one-port-per-endpoint protocols. + */ +static int snd_usbmidi_detect_per_port_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) +{ + int err, i; + + err = snd_usbmidi_detect_endpoints(umidi, endpoints, MIDI_MAX_ENDPOINTS); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + if (endpoints[i].out_ep) + endpoints[i].out_cables = 0x0001; + if (endpoints[i].in_ep) + endpoints[i].in_cables = 0x0001; + } + return err; +} + +/* + * Detects the endpoints and ports of Yamaha devices. + */ +static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + uint8_t* cs_desc; + + intf = umidi->iface; + if (!intf) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < 1) + return -ENOENT; + + /* + * For each port there is one MIDI_IN/OUT_JACK descriptor, not + * necessarily with any useful contents. So simply count 'em. + */ + for (cs_desc = hostif->extra; + cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; + cs_desc += cs_desc[0]) { + if (cs_desc[1] == USB_DT_CS_INTERFACE) { + if (cs_desc[2] == UAC_MIDI_IN_JACK) + endpoint->in_cables = (endpoint->in_cables << 1) | 1; + else if (cs_desc[2] == UAC_MIDI_OUT_JACK) + endpoint->out_cables = (endpoint->out_cables << 1) | 1; + } + } + if (!endpoint->in_cables && !endpoint->out_cables) + return -ENOENT; + + return snd_usbmidi_detect_endpoints(umidi, endpoint, 1); +} + +/* + * Creates the endpoints and their ports for Midiman devices. + */ +static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint) +{ + struct snd_usb_midi_endpoint_info ep_info; + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_endpoint_descriptor* epd; + int cable, err; + + intf = umidi->iface; + if (!intf) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + /* + * The various MidiSport devices have more or less random endpoint + * numbers, so we have to identify the endpoints by their index in + * the descriptor array, like the driver for that other OS does. + * + * There is one interrupt input endpoint for all input ports, one + * bulk output endpoint for even-numbered ports, and one for odd- + * numbered ports. Both bulk output endpoints have corresponding + * input bulk endpoints (at indices 1 and 3) which aren't used. + */ + if (intfd->bNumEndpoints < (endpoint->out_cables > 0x0001 ? 5 : 3)) { + snd_printdd(KERN_ERR "not enough endpoints\n"); + return -ENOENT; + } + + epd = get_endpoint(hostif, 0); + if (!usb_endpoint_dir_in(epd) || !usb_endpoint_xfer_int(epd)) { + snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n"); + return -ENXIO; + } + epd = get_endpoint(hostif, 2); + if (!usb_endpoint_dir_out(epd) || !usb_endpoint_xfer_bulk(epd)) { + snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n"); + return -ENXIO; + } + if (endpoint->out_cables > 0x0001) { + epd = get_endpoint(hostif, 4); + if (!usb_endpoint_dir_out(epd) || + !usb_endpoint_xfer_bulk(epd)) { + snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n"); + return -ENXIO; + } + } + + ep_info.out_ep = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_interval = 0; + ep_info.out_cables = endpoint->out_cables & 0x5555; + err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); + if (err < 0) + return err; + + ep_info.in_ep = get_endpoint(hostif, 0)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.in_interval = get_endpoint(hostif, 0)->bInterval; + ep_info.in_cables = endpoint->in_cables; + err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); + if (err < 0) + return err; + + if (endpoint->out_cables > 0x0001) { + ep_info.out_ep = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_cables = endpoint->out_cables & 0xaaaa; + err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]); + if (err < 0) + return err; + } + + for (cable = 0; cable < 0x10; ++cable) { + if (endpoint->out_cables & (1 << cable)) + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, cable, + &umidi->endpoints[cable & 1].out->ports[cable].substream); + if (endpoint->in_cables & (1 << cable)) + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, cable, + &umidi->endpoints[0].in->ports[cable].substream); + } + return 0; +} + +static struct snd_rawmidi_global_ops snd_usbmidi_ops = { + .get_port_info = snd_usbmidi_get_port_info, +}; + +static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi, + int out_ports, int in_ports) +{ + struct snd_rawmidi *rmidi; + int err; + + err = snd_rawmidi_new(umidi->card, "USB MIDI", + umidi->next_midi_device++, + out_ports, in_ports, &rmidi); + if (err < 0) + return err; + strcpy(rmidi->name, umidi->card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->ops = &snd_usbmidi_ops; + rmidi->private_data = umidi; + rmidi->private_free = snd_usbmidi_rawmidi_free; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input_ops); + + umidi->rmidi = rmidi; + return 0; +} + +/* + * Temporarily stop input. + */ +void snd_usbmidi_input_stop(struct list_head* p) +{ + struct snd_usb_midi* umidi; + unsigned int i, j; + + umidi = list_entry(p, struct snd_usb_midi, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; + if (ep->in) + for (j = 0; j < INPUT_URBS; ++j) + usb_kill_urb(ep->in->urbs[j]); + } +} + +static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep) +{ + unsigned int i; + + if (!ep) + return; + for (i = 0; i < INPUT_URBS; ++i) { + struct urb* urb = ep->urbs[i]; + urb->dev = ep->umidi->dev; + snd_usbmidi_submit_urb(urb, GFP_KERNEL); + } +} + +/* + * Resume input after a call to snd_usbmidi_input_stop(). + */ +void snd_usbmidi_input_start(struct list_head* p) +{ + struct snd_usb_midi* umidi; + int i; + + umidi = list_entry(p, struct snd_usb_midi, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + snd_usbmidi_input_start_ep(umidi->endpoints[i].in); +} + +/* + * Creates and registers everything needed for a MIDI streaming interface. + */ +int snd_usbmidi_create(struct snd_card *card, + struct usb_interface* iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk* quirk) +{ + struct snd_usb_midi* umidi; + struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; + int out_ports, in_ports; + int i, err; + + umidi = kzalloc(sizeof(*umidi), GFP_KERNEL); + if (!umidi) + return -ENOMEM; + umidi->dev = interface_to_usbdev(iface); + umidi->card = card; + umidi->iface = iface; + umidi->quirk = quirk; + umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; + init_timer(&umidi->error_timer); + spin_lock_init(&umidi->disc_lock); + mutex_init(&umidi->mutex); + umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), + le16_to_cpu(umidi->dev->descriptor.idProduct)); + umidi->error_timer.function = snd_usbmidi_error_timer; + umidi->error_timer.data = (unsigned long)umidi; + + /* detect the endpoint(s) to use */ + memset(endpoints, 0, sizeof(endpoints)); + switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) { + case QUIRK_MIDI_STANDARD_INTERFACE: + err = snd_usbmidi_get_ms_info(umidi, endpoints); + if (umidi->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */ + umidi->usb_protocol_ops = + &snd_usbmidi_maudio_broken_running_status_ops; + break; + case QUIRK_MIDI_US122L: + umidi->usb_protocol_ops = &snd_usbmidi_122l_ops; + /* fall through */ + case QUIRK_MIDI_FIXED_ENDPOINT: + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); + break; + case QUIRK_MIDI_YAMAHA: + err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); + break; + case QUIRK_MIDI_MIDIMAN: + umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops; + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = 0; + break; + case QUIRK_MIDI_NOVATION: + umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + case QUIRK_MIDI_FASTLANE: + umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; + /* + * Interface 1 contains isochronous endpoints, but with the same + * numbers as in interface 0. Since it is interface 1 that the + * USB core has most recently seen, these descriptors are now + * associated with the endpoint numbers. This will foul up our + * attempts to submit bulk/interrupt URBs to the endpoints in + * interface 0, so we have to make sure that the USB core looks + * again at interface 0 by calling usb_set_interface() on it. + */ + usb_set_interface(umidi->dev, 0, 0); + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + case QUIRK_MIDI_EMAGIC: + umidi->usb_protocol_ops = &snd_usbmidi_emagic_ops; + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); + break; + case QUIRK_MIDI_CME: + umidi->usb_protocol_ops = &snd_usbmidi_cme_ops; + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + default: + snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); + err = -ENXIO; + break; + } + if (err < 0) { + kfree(umidi); + return err; + } + + /* create rawmidi device */ + out_ports = 0; + in_ports = 0; + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + out_ports += hweight16(endpoints[i].out_cables); + in_ports += hweight16(endpoints[i].in_cables); + } + err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports); + if (err < 0) { + kfree(umidi); + return err; + } + + /* create endpoint/port structures */ + if (quirk && quirk->type == QUIRK_MIDI_MIDIMAN) + err = snd_usbmidi_create_endpoints_midiman(umidi, &endpoints[0]); + else + err = snd_usbmidi_create_endpoints(umidi, endpoints); + if (err < 0) { + snd_usbmidi_free(umidi); + return err; + } + + list_add_tail(&umidi->list, midi_list); + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + snd_usbmidi_input_start_ep(umidi->endpoints[i].in); + return 0; +} + +EXPORT_SYMBOL(snd_usbmidi_create); +EXPORT_SYMBOL(snd_usbmidi_input_stop); +EXPORT_SYMBOL(snd_usbmidi_input_start); +EXPORT_SYMBOL(snd_usbmidi_disconnect); diff --git a/sound/usb/midi.h b/sound/usb/midi.h new file mode 100644 index 0000000..2089ec9 --- /dev/null +++ b/sound/usb/midi.h @@ -0,0 +1,48 @@ +#ifndef __USBMIDI_H +#define __USBMIDI_H + +/* maximum number of endpoints per interface */ +#define MIDI_MAX_ENDPOINTS 2 + +/* data for QUIRK_MIDI_FIXED_ENDPOINT */ +struct snd_usb_midi_endpoint_info { + int8_t out_ep; /* ep number, 0 autodetect */ + uint8_t out_interval; /* interval for interrupt endpoints */ + int8_t in_ep; + uint8_t in_interval; + uint16_t out_cables; /* bitmask */ + uint16_t in_cables; /* bitmask */ +}; + +/* for QUIRK_MIDI_YAMAHA, data is NULL */ + +/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info + * structure (out_cables and in_cables only) */ + +/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk + * structures, terminated with .ifnum = -1 */ + +/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ + +/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */ + +/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */ + +/* for QUIRK_IGNORE_INTERFACE, data is NULL */ + +/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */ + +/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info + * structure (out_cables and in_cables only) */ + +/* for QUIRK_MIDI_CME, data is NULL */ + +int snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk); +void snd_usbmidi_input_stop(struct list_head* p); +void snd_usbmidi_input_start(struct list_head* p); +void snd_usbmidi_disconnect(struct list_head *p); + +#endif /* __USBMIDI_H */ diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index b4a4cb4..796d8b2 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -24,7 +24,7 @@ #include #include #include "../usbaudio.h" -#include "../usbmidi.h" +#include "../midi.h" MODULE_DESCRIPTION("Edirol UA-101/1000 driver"); MODULE_AUTHOR("Clemens Ladisch "); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c new file mode 100644 index 0000000..87863cc --- /dev/null +++ b/sound/usb/pcm.c @@ -0,0 +1,845 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "quirks.h" +#include "debug.h" +#include "urb.h" +#include "helper.h" +#include "pcm.h" + +/* + * return the current pcm pointer. just based on the hwptr_done value. + */ +static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs; + unsigned int hwptr_done; + + subs = (struct snd_usb_substream *)substream->runtime->private_data; + spin_lock(&subs->lock); + hwptr_done = subs->hwptr_done; + spin_unlock(&subs->lock); + return hwptr_done / (substream->runtime->frame_bits >> 3); +} + +/* + * find a matching audio format + */ +static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format, + unsigned int rate, unsigned int channels) +{ + struct list_head *p; + struct audioformat *found = NULL; + int cur_attr = 0, attr; + + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (fp->format != format || fp->channels != channels) + continue; + if (rate < fp->rate_min || rate > fp->rate_max) + continue; + if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { + unsigned int i; + for (i = 0; i < fp->nr_rates; i++) + if (fp->rate_table[i] == rate) + break; + if (i >= fp->nr_rates) + continue; + } + attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; + if (! found) { + found = fp; + cur_attr = attr; + continue; + } + /* avoid async out and adaptive in if the other method + * supports the same format. + * this is a workaround for the case like + * M-audio audiophile USB. + */ + if (attr != cur_attr) { + if ((attr == USB_ENDPOINT_SYNC_ASYNC && + subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || + (attr == USB_ENDPOINT_SYNC_ADAPTIVE && + subs->direction == SNDRV_PCM_STREAM_CAPTURE)) + continue; + if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC && + subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || + (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE && + subs->direction == SNDRV_PCM_STREAM_CAPTURE)) { + found = fp; + cur_attr = attr; + continue; + } + } + /* find the format with the largest max. packet size */ + if (fp->maxpacksize > found->maxpacksize) { + found = fp; + cur_attr = attr; + } + } + return found; +} + + +/* + * initialize the picth control and sample rate + */ +int snd_usb_init_pitch(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt) +{ + unsigned int ep; + unsigned char data[1]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) { + data[0] = 1; + if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", + dev->devnum, iface, ep); + return err; + } + } + return 0; +} + +int snd_usb_init_sample_rate(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + unsigned int ep; + unsigned char data[3]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) { + int crate; + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", + dev->devnum, iface, fmt->altsetting, rate, ep); + return err; + } + if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { + snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", + dev->devnum, iface, fmt->altsetting, ep); + return 0; /* some devices don't support reading */ + } + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + // runtime->rate = crate; + } + } + return 0; +} + +/* + * find a matching format and set up the interface + */ +static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_interface *iface; + unsigned int ep, attr; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + int err; + + iface = usb_ifnum_to_if(dev, fmt->iface); + if (WARN_ON(!iface)) + return -EINVAL; + alts = &iface->altsetting[fmt->altset_idx]; + altsd = get_iface_desc(alts); + if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) + return -EINVAL; + + if (fmt == subs->cur_audiofmt) + return 0; + + /* close the old interface */ + if (subs->interface >= 0 && subs->interface != fmt->iface) { + if (usb_set_interface(subs->dev, subs->interface, 0) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EIO; + } + subs->interface = -1; + subs->format = 0; + } + + /* set interface */ + if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { + if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EIO; + } + snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); + subs->interface = fmt->iface; + subs->format = fmt->altset_idx; + } + + /* create a data pipe */ + ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK; + if (is_playback) + subs->datapipe = usb_sndisocpipe(dev, ep); + else + subs->datapipe = usb_rcvisocpipe(dev, ep); + subs->datainterval = fmt->datainterval; + subs->syncpipe = subs->syncinterval = 0; + subs->maxpacksize = fmt->maxpacksize; + subs->fill_max = 0; + + /* we need a sync pipe in async OUT or adaptive IN mode */ + /* check the number of EP, since some devices have broken + * descriptors which fool us. if it has only one EP, + * assume it as adaptive-out or sync-in. + */ + attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || + (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && + altsd->bNumEndpoints >= 2) { + /* check sync-pipe endpoint */ + /* ... and check descriptor size before accessing bSynchAddress + because there is a version of the SB Audigy 2 NX firmware lacking + the audio fields in the endpoint descriptors */ + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || + (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bSynchAddress != 0)) { + snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EINVAL; + } + ep = get_endpoint(alts, 1)->bEndpointAddress; + if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { + snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EINVAL; + } + ep &= USB_ENDPOINT_NUMBER_MASK; + if (is_playback) + subs->syncpipe = usb_rcvisocpipe(dev, ep); + else + subs->syncpipe = usb_sndisocpipe(dev, ep); + if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bRefresh >= 1 && + get_endpoint(alts, 1)->bRefresh <= 9) + subs->syncinterval = get_endpoint(alts, 1)->bRefresh; + else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) + subs->syncinterval = 1; + else if (get_endpoint(alts, 1)->bInterval >= 1 && + get_endpoint(alts, 1)->bInterval <= 16) + subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1; + else + subs->syncinterval = 3; + } + + /* always fill max packet size */ + if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) + subs->fill_max = 1; + + if ((err = snd_usb_init_pitch(dev, subs->interface, alts, fmt)) < 0) + return err; + + subs->cur_audiofmt = fmt; + + snd_usb_set_format_quirk(subs, fmt); + +#if 0 + printk(KERN_DEBUG + "setting done: format = %d, rate = %d..%d, channels = %d\n", + fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels); + printk(KERN_DEBUG + " datapipe = 0x%0x, syncpipe = 0x%0x\n", + subs->datapipe, subs->syncpipe); +#endif + + return 0; +} + +/* + * hw_params callback + * + * allocate a buffer and set the given audio format. + * + * so far we use a physically linear buffer although packetize transfer + * doesn't need a continuous area. + * if sg buffer is supported on the later version of alsa, we'll follow + * that. + */ +static int snd_usb_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + struct audioformat *fmt; + unsigned int channels, rate, format; + int ret, changed; + + ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) + return ret; + + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); + fmt = find_format(subs, format, rate, channels); + if (!fmt) { + snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n", + format, rate, channels); + return -EINVAL; + } + + changed = subs->cur_audiofmt != fmt || + subs->period_bytes != params_period_bytes(hw_params) || + subs->cur_rate != rate; + if ((ret = set_format(subs, fmt)) < 0) + return ret; + + if (subs->cur_rate != rate) { + struct usb_host_interface *alts; + struct usb_interface *iface; + iface = usb_ifnum_to_if(subs->dev, fmt->iface); + alts = &iface->altsetting[fmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->dev, subs->interface, alts, fmt, rate); + if (ret < 0) + return ret; + subs->cur_rate = rate; + } + + if (changed) { + /* format changed */ + snd_usb_release_substream_urbs(subs, 0); + /* influenced: period_bytes, channels, rate, format, */ + ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params), + params_rate(hw_params), + snd_pcm_format_physical_width(params_format(hw_params)) * + params_channels(hw_params)); + } + + return ret; +} + +/* + * hw_free callback + * + * reset the audio format and release the buffer + */ +static int snd_usb_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + + subs->cur_audiofmt = NULL; + subs->cur_rate = 0; + subs->period_bytes = 0; + if (!subs->stream->chip->shutdown) + snd_usb_release_substream_urbs(subs, 0); + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +/* + * prepare callback + * + * only a few subtle things... + */ +static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_usb_substream *subs = runtime->private_data; + + if (! subs->cur_audiofmt) { + snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); + return -ENXIO; + } + + /* some unit conversions in runtime */ + subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); + subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); + + /* reset the pointer */ + subs->hwptr_done = 0; + subs->transfer_done = 0; + subs->phase = 0; + runtime->delay = 0; + + return snd_usb_substream_prepare(subs, runtime); +} + +static struct snd_pcm_hardware snd_usb_hardware = +{ + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE, + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +static int hw_check_valid_format(struct snd_usb_substream *subs, + struct snd_pcm_hw_params *params, + struct audioformat *fp) +{ + struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); + unsigned int ptime; + + /* check the format */ + if (!snd_mask_test(fmts, fp->format)) { + hwc_debug(" > check: no supported format %d\n", fp->format); + return 0; + } + /* check the channels */ + if (fp->channels < ct->min || fp->channels > ct->max) { + hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max); + return 0; + } + /* check the rate is within the range */ + if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) { + hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max); + return 0; + } + if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) { + hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); + return 0; + } + /* check whether the period time is >= the data packet interval */ + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) { + ptime = 125 * (1 << fp->datainterval); + if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { + hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); + return 0; + } + } + return 1; +} + +static int hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + struct list_head *p; + struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + unsigned int rmin, rmax; + int changed; + + hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (!hw_check_valid_format(subs, params, fp)) + continue; + if (changed++) { + if (rmin > fp->rate_min) + rmin = fp->rate_min; + if (rmax < fp->rate_max) + rmax = fp->rate_max; + } else { + rmin = fp->rate_min; + rmax = fp->rate_max; + } + } + + if (!changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + + +static int hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + struct list_head *p; + struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int rmin, rmax; + int changed; + + hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (!hw_check_valid_format(subs, params, fp)) + continue; + if (changed++) { + if (rmin > fp->channels) + rmin = fp->channels; + if (rmax < fp->channels) + rmax = fp->channels; + } else { + rmin = fp->channels; + rmax = fp->channels; + } + } + + if (!changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + +static int hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + struct list_head *p; + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + u64 fbits; + u32 oldbits[2]; + int changed; + + hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); + fbits = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (!hw_check_valid_format(subs, params, fp)) + continue; + fbits |= (1ULL << fp->format); + } + + oldbits[0] = fmt->bits[0]; + oldbits[1] = fmt->bits[1]; + fmt->bits[0] &= (u32)fbits; + fmt->bits[1] &= (u32)(fbits >> 32); + if (!fmt->bits[0] && !fmt->bits[1]) { + hwc_debug(" --> get empty\n"); + return -EINVAL; + } + changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); + hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); + return changed; +} + +static int hw_rule_period_time(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + struct audioformat *fp; + struct snd_interval *it; + unsigned char min_datainterval; + unsigned int pmin; + int changed; + + it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); + hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max); + min_datainterval = 0xff; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (!hw_check_valid_format(subs, params, fp)) + continue; + min_datainterval = min(min_datainterval, fp->datainterval); + } + if (min_datainterval == 0xff) { + hwc_debug(" --> get emtpy\n"); + it->empty = 1; + return -EINVAL; + } + pmin = 125 * (1 << min_datainterval); + changed = 0; + if (it->min < pmin) { + it->min = pmin; + it->openmin = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + +/* + * If the device supports unusual bit rates, does the request meet these? + */ +static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) +{ + struct audioformat *fp; + int count = 0, needs_knot = 0; + int err; + + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) + return 0; + count += fp->nr_rates; + if (fp->rates & SNDRV_PCM_RATE_KNOT) + needs_knot = 1; + } + if (!needs_knot) + return 0; + + subs->rate_list.count = count; + subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); + subs->rate_list.mask = 0; + count = 0; + list_for_each_entry(fp, &subs->fmt_list, list) { + int i; + for (i = 0; i < fp->nr_rates; i++) + subs->rate_list.list[count++] = fp->rate_table[i]; + } + err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &subs->rate_list); + if (err < 0) + return err; + + return 0; +} + + +/* + * set up the runtime hardware information. + */ + +static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) +{ + struct list_head *p; + unsigned int pt, ptmin; + int param_period_time_if_needed; + int err; + + runtime->hw.formats = subs->formats; + + runtime->hw.rate_min = 0x7fffffff; + runtime->hw.rate_max = 0; + runtime->hw.channels_min = 256; + runtime->hw.channels_max = 0; + runtime->hw.rates = 0; + ptmin = UINT_MAX; + /* check min/max rates and channels */ + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + runtime->hw.rates |= fp->rates; + if (runtime->hw.rate_min > fp->rate_min) + runtime->hw.rate_min = fp->rate_min; + if (runtime->hw.rate_max < fp->rate_max) + runtime->hw.rate_max = fp->rate_max; + if (runtime->hw.channels_min > fp->channels) + runtime->hw.channels_min = fp->channels; + if (runtime->hw.channels_max < fp->channels) + runtime->hw.channels_max = fp->channels; + if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) { + /* FIXME: there might be more than one audio formats... */ + runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = + fp->frame_size; + } + pt = 125 * (1 << fp->datainterval); + ptmin = min(ptmin, pt); + } + + param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; + if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH) + /* full speed devices have fixed data packet interval */ + ptmin = 1000; + if (ptmin == 1000) + /* if period time doesn't go below 1 ms, no rules needed */ + param_period_time_if_needed = -1; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + ptmin, UINT_MAX); + + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + param_period_time_if_needed, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1)) < 0) + return err; + if (param_period_time_if_needed >= 0) { + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + hw_rule_period_time, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + -1); + if (err < 0) + return err; + } + if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) + return err; + return 0; +} + +static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) +{ + struct snd_usb_stream *as = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_usb_substream *subs = &as->substream[direction]; + + subs->interface = -1; + subs->format = 0; + runtime->hw = snd_usb_hardware; + runtime->private_data = subs; + subs->pcm_substream = substream; + return setup_hw_info(runtime, subs); +} + +static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) +{ + struct snd_usb_stream *as = snd_pcm_substream_chip(substream); + struct snd_usb_substream *subs = &as->substream[direction]; + + if (!as->chip->shutdown && subs->interface >= 0) { + usb_set_interface(subs->dev, subs->interface, 0); + subs->interface = -1; + } + subs->pcm_substream = NULL; + return 0; +} + +static int snd_usb_playback_open(struct snd_pcm_substream *substream) +{ + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK); +} + +static int snd_usb_playback_close(struct snd_pcm_substream *substream) +{ + return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK); +} + +static int snd_usb_capture_open(struct snd_pcm_substream *substream) +{ + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE); +} + +static int snd_usb_capture_close(struct snd_pcm_substream *substream) +{ + return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); +} + +static struct snd_pcm_ops snd_usb_playback_ops = { + .open = snd_usb_playback_open, + .close = snd_usb_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_substream_playback_trigger, + .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +static struct snd_pcm_ops snd_usb_capture_ops = { + .open = snd_usb_capture_open, + .close = snd_usb_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_substream_capture_trigger, + .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) +{ + snd_pcm_set_ops(pcm, stream, + stream == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_usb_playback_ops : &snd_usb_capture_ops); +} diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h new file mode 100644 index 0000000..8585601 --- /dev/null +++ b/sound/usb/pcm.h @@ -0,0 +1,14 @@ +#ifndef __USBAUDIO_PCM_H +#define __USBAUDIO_PCM_H + +void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); + +int snd_usb_init_pitch(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt); + +int snd_usb_init_sample_rate(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate); + +#endif /* __USBAUDIO_PCM_H */ diff --git a/sound/usb/proc.c b/sound/usb/proc.c new file mode 100644 index 0000000..be3065e --- /dev/null +++ b/sound/usb/proc.c @@ -0,0 +1,163 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "helper.h" +#include "card.h" +#include "proc.h" + +/* convert our full speed USB rate into sampling rate in Hz */ +static inline unsigned get_full_speed_hz(unsigned int usb_rate) +{ + return (usb_rate * 125 + (1 << 12)) >> 13; +} + +/* convert our high speed USB rate into sampling rate in Hz */ +static inline unsigned get_high_speed_hz(unsigned int usb_rate) +{ + return (usb_rate * 125 + (1 << 9)) >> 10; +} + +/* + * common proc files to show the usb device info + */ +static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + struct snd_usb_audio *chip = entry->private_data; + if (!chip->shutdown) + snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum); +} + +static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + struct snd_usb_audio *chip = entry->private_data; + if (!chip->shutdown) + snd_iprintf(buffer, "%04x:%04x\n", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); +} + +void snd_usb_audio_create_proc(struct snd_usb_audio *chip) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(chip->card, "usbbus", &entry)) + snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read); + if (!snd_card_proc_new(chip->card, "usbid", &entry)) + snd_info_set_text_ops(entry, chip, proc_audio_usbid_read); +} + +/* + * proc interface for list the supported pcm formats + */ +static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) +{ + struct list_head *p; + static char *sync_types[4] = { + "NONE", "ASYNC", "ADAPTIVE", "SYNC" + }; + + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + snd_iprintf(buffer, " Interface %d\n", fp->iface); + snd_iprintf(buffer, " Altset %d\n", fp->altsetting); + snd_iprintf(buffer, " Format: %s\n", + snd_pcm_format_name(fp->format)); + snd_iprintf(buffer, " Channels: %d\n", fp->channels); + snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", + fp->endpoint & USB_ENDPOINT_NUMBER_MASK, + fp->endpoint & USB_DIR_IN ? "IN" : "OUT", + sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]); + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { + snd_iprintf(buffer, " Rates: %d - %d (continuous)\n", + fp->rate_min, fp->rate_max); + } else { + unsigned int i; + snd_iprintf(buffer, " Rates: "); + for (i = 0; i < fp->nr_rates; i++) { + if (i > 0) + snd_iprintf(buffer, ", "); + snd_iprintf(buffer, "%d", fp->rate_table[i]); + } + snd_iprintf(buffer, "\n"); + } + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) + snd_iprintf(buffer, " Data packet interval: %d us\n", + 125 * (1 << fp->datainterval)); + // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); + // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); + } +} + +static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) +{ + if (subs->running) { + unsigned int i; + snd_iprintf(buffer, " Status: Running\n"); + snd_iprintf(buffer, " Interface = %d\n", subs->interface); + snd_iprintf(buffer, " Altset = %d\n", subs->format); + snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs); + for (i = 0; i < subs->nurbs; i++) + snd_iprintf(buffer, "%d ", subs->dataurb[i].packets); + snd_iprintf(buffer, "]\n"); + snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize); + snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", + snd_usb_get_speed(subs->dev) == USB_SPEED_FULL + ? get_full_speed_hz(subs->freqm) + : get_high_speed_hz(subs->freqm), + subs->freqm >> 16, subs->freqm & 0xffff); + } else { + snd_iprintf(buffer, " Status: Stop\n"); + } +} + +static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + struct snd_usb_stream *stream = entry->private_data; + + snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); + + if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { + snd_iprintf(buffer, "\nPlayback:\n"); + proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); + proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); + } + if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) { + snd_iprintf(buffer, "\nCapture:\n"); + proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); + proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); + } +} + +void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream) +{ + struct snd_info_entry *entry; + char name[32]; + struct snd_card *card = stream->chip->card; + + sprintf(name, "stream%d", stream->pcm_index); + if (!snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, stream, proc_pcm_format_read); +} + diff --git a/sound/usb/proc.h b/sound/usb/proc.h new file mode 100644 index 0000000..a45b765 --- /dev/null +++ b/sound/usb/proc.h @@ -0,0 +1,8 @@ +#ifndef __USBAUDIO_PROC_H +#define __USBAUDIO_PROC_H + +void snd_usb_audio_create_proc(struct snd_usb_audio *chip); +void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream); + +#endif /* __USBAUDIO_PROC_H */ + diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h new file mode 100644 index 0000000..2b426c1 --- /dev/null +++ b/sound/usb/quirks-table.h @@ -0,0 +1,2248 @@ +/* + * ALSA USB Audio Driver + * + * Copyright (c) 2002 by Takashi Iwai , + * Clemens Ladisch + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The contents of this file are part of the driver's id_table. + * + * In a perfect world, this file would be empty. + */ + +/* + * Use this for devices where other interfaces are standard compliant, + * to prevent the quirk being applied to those interfaces. (To work with + * hotplugging, bDeviceClass must be set to USB_CLASS_PER_INTERFACE.) + */ +#define USB_DEVICE_VENDOR_SPEC(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \ + USB_DEVICE_ID_MATCH_PRODUCT | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = vend, \ + .idProduct = prod, \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC + +/* Creative/Toshiba Multimedia Center SB-0500 */ +{ + USB_DEVICE(0x041e, 0x3048), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Toshiba", + .product_name = "SB-0500", + .ifnum = QUIRK_NO_INTERFACE + } +}, + +/* Creative/E-Mu devices */ +{ + USB_DEVICE(0x041e, 0x3010), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Creative Labs", + .product_name = "Sound Blaster MP3+", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + /* E-Mu 0202 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f02, + .bInterfaceClass = USB_CLASS_AUDIO, +}, +{ + /* E-Mu 0404 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f04, + .bInterfaceClass = USB_CLASS_AUDIO, +}, +{ + /* E-Mu Tracker Pre */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f0a, + .bInterfaceClass = USB_CLASS_AUDIO, +}, + +/* + * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface + * class matches do not take effect without an explicit ID match. + */ +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x0850, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08ae, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08f0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08f5, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08f6, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +}, +{ + USB_DEVICE(0x046d, 0x0990), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Logitech, Inc.", + .product_name = "QuickCam Pro 9000", + .ifnum = QUIRK_NO_INTERFACE + } +}, + +/* + * Yamaha devices + */ + +#define YAMAHA_DEVICE(id, name) { \ + USB_DEVICE(0x0499, id), \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = "Yamaha", \ + .product_name = name, \ + .ifnum = QUIRK_ANY_INTERFACE, \ + .type = QUIRK_MIDI_YAMAHA \ + } \ +} +#define YAMAHA_INTERFACE(id, intf, name) { \ + USB_DEVICE_VENDOR_SPEC(0x0499, id), \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = "Yamaha", \ + .product_name = name, \ + .ifnum = intf, \ + .type = QUIRK_MIDI_YAMAHA \ + } \ +} +YAMAHA_DEVICE(0x1000, "UX256"), +YAMAHA_DEVICE(0x1001, "MU1000"), +YAMAHA_DEVICE(0x1002, "MU2000"), +YAMAHA_DEVICE(0x1003, "MU500"), +YAMAHA_INTERFACE(0x1004, 3, "UW500"), +YAMAHA_DEVICE(0x1005, "MOTIF6"), +YAMAHA_DEVICE(0x1006, "MOTIF7"), +YAMAHA_DEVICE(0x1007, "MOTIF8"), +YAMAHA_DEVICE(0x1008, "UX96"), +YAMAHA_DEVICE(0x1009, "UX16"), +YAMAHA_INTERFACE(0x100a, 3, "EOS BX"), +YAMAHA_DEVICE(0x100c, "UC-MX"), +YAMAHA_DEVICE(0x100d, "UC-KX"), +YAMAHA_DEVICE(0x100e, "S08"), +YAMAHA_DEVICE(0x100f, "CLP-150"), +YAMAHA_DEVICE(0x1010, "CLP-170"), +YAMAHA_DEVICE(0x1011, "P-250"), +YAMAHA_DEVICE(0x1012, "TYROS"), +YAMAHA_DEVICE(0x1013, "PF-500"), +YAMAHA_DEVICE(0x1014, "S90"), +YAMAHA_DEVICE(0x1015, "MOTIF-R"), +YAMAHA_DEVICE(0x1016, "MDP-5"), +YAMAHA_DEVICE(0x1017, "CVP-204"), +YAMAHA_DEVICE(0x1018, "CVP-206"), +YAMAHA_DEVICE(0x1019, "CVP-208"), +YAMAHA_DEVICE(0x101a, "CVP-210"), +YAMAHA_DEVICE(0x101b, "PSR-1100"), +YAMAHA_DEVICE(0x101c, "PSR-2100"), +YAMAHA_DEVICE(0x101d, "CLP-175"), +YAMAHA_DEVICE(0x101e, "PSR-K1"), +YAMAHA_DEVICE(0x101f, "EZ-J24"), +YAMAHA_DEVICE(0x1020, "EZ-250i"), +YAMAHA_DEVICE(0x1021, "MOTIF ES 6"), +YAMAHA_DEVICE(0x1022, "MOTIF ES 7"), +YAMAHA_DEVICE(0x1023, "MOTIF ES 8"), +YAMAHA_DEVICE(0x1024, "CVP-301"), +YAMAHA_DEVICE(0x1025, "CVP-303"), +YAMAHA_DEVICE(0x1026, "CVP-305"), +YAMAHA_DEVICE(0x1027, "CVP-307"), +YAMAHA_DEVICE(0x1028, "CVP-309"), +YAMAHA_DEVICE(0x1029, "CVP-309GP"), +YAMAHA_DEVICE(0x102a, "PSR-1500"), +YAMAHA_DEVICE(0x102b, "PSR-3000"), +YAMAHA_DEVICE(0x102e, "ELS-01/01C"), +YAMAHA_DEVICE(0x1030, "PSR-295/293"), +YAMAHA_DEVICE(0x1031, "DGX-205/203"), +YAMAHA_DEVICE(0x1032, "DGX-305"), +YAMAHA_DEVICE(0x1033, "DGX-505"), +YAMAHA_DEVICE(0x1034, NULL), +YAMAHA_DEVICE(0x1035, NULL), +YAMAHA_DEVICE(0x1036, NULL), +YAMAHA_DEVICE(0x1037, NULL), +YAMAHA_DEVICE(0x1038, NULL), +YAMAHA_DEVICE(0x1039, NULL), +YAMAHA_DEVICE(0x103a, NULL), +YAMAHA_DEVICE(0x103b, NULL), +YAMAHA_DEVICE(0x103c, NULL), +YAMAHA_DEVICE(0x103d, NULL), +YAMAHA_DEVICE(0x103e, NULL), +YAMAHA_DEVICE(0x103f, NULL), +YAMAHA_DEVICE(0x1040, NULL), +YAMAHA_DEVICE(0x1041, NULL), +YAMAHA_DEVICE(0x1042, NULL), +YAMAHA_DEVICE(0x1043, NULL), +YAMAHA_DEVICE(0x1044, NULL), +YAMAHA_DEVICE(0x1045, NULL), +YAMAHA_INTERFACE(0x104e, 0, NULL), +YAMAHA_DEVICE(0x104f, NULL), +YAMAHA_DEVICE(0x1050, NULL), +YAMAHA_DEVICE(0x1051, NULL), +YAMAHA_DEVICE(0x1052, NULL), +YAMAHA_DEVICE(0x2000, "DGP-7"), +YAMAHA_DEVICE(0x2001, "DGP-5"), +YAMAHA_DEVICE(0x2002, NULL), +YAMAHA_DEVICE(0x5000, "CS1D"), +YAMAHA_DEVICE(0x5001, "DSP1D"), +YAMAHA_DEVICE(0x5002, "DME32"), +YAMAHA_DEVICE(0x5003, "DM2000"), +YAMAHA_DEVICE(0x5004, "02R96"), +YAMAHA_DEVICE(0x5005, "ACU16-C"), +YAMAHA_DEVICE(0x5006, "NHB32-C"), +YAMAHA_DEVICE(0x5007, "DM1000"), +YAMAHA_DEVICE(0x5008, "01V96"), +YAMAHA_DEVICE(0x5009, "SPX2000"), +YAMAHA_DEVICE(0x500a, "PM5D"), +YAMAHA_DEVICE(0x500b, "DME64N"), +YAMAHA_DEVICE(0x500c, "DME24N"), +YAMAHA_DEVICE(0x500d, NULL), +YAMAHA_DEVICE(0x500e, NULL), +YAMAHA_DEVICE(0x500f, NULL), +YAMAHA_DEVICE(0x7000, "DTX"), +YAMAHA_DEVICE(0x7010, "UB99"), +#undef YAMAHA_DEVICE +#undef YAMAHA_INTERFACE + +/* + * Roland/RolandED/Edirol/BOSS devices + */ +{ + USB_DEVICE(0x0582, 0x0000), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "UA-100", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_FILL_MAX, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0002), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-4", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0003), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SC-8850", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x003f, + .in_cables = 0x003f + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0004), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "U-8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0005, + .in_cables = 0x0005 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* Has ID 0x0099 when not in "Advanced Driver" mode. + * The UM-2EX has only one input, but we cannot detect this. */ + USB_DEVICE(0x0582, 0x0005), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-2", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0007), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SC-8820", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0008), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "PC-300", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x009d when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0009), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-1", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x000b), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SK-500", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* thanks to Emiliano Grilli + * for helping researching this data */ + USB_DEVICE(0x0582, 0x000c), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SC-D70", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x81, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ /* + * This quirk is for the "Advanced Driver" mode of the Edirol UA-5. + * If the advanced mode switch at the back of the unit is off, the + * UA-5 has ID 0x0582/0x0011 and is standard compliant (no quirks), + * but offers only 16-bit PCM. + * In advanced mode, the UA-5 will output S24_3LE samples (two + * channels) at the rate indicated on the front switch, including + * the 96kHz sample rate. + */ + USB_DEVICE(0x0582, 0x0010), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-5", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x0013 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0012), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "XV-5050", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x0015 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0014), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-880", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + /* has ID 0x0017 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0016), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "SD-90", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x001c when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x001b), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "MMP-2", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x001e when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x001d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "V-SYNTH", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x0024 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0023), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-550", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x003f, + .in_cables = 0x003f + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-20 + * has ID 0x0026 and is standard compliant, but has only 16-bit PCM + * and no MIDI. + */ + USB_DEVICE(0x0582, 0x0025), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-20", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x82, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x0028 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0027), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "SD-20", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + /* has ID 0x002a when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0029), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "SD-80", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ /* + * This quirk is for the "Advanced" modes of the Edirol UA-700. + * If the sample format switch is not in an advanced setting, the + * UA-700 has ID 0x0582/0x002c and is standard compliant (no quirks), + * but offers only 16-bit PCM and no MIDI. + */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-700", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 3, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x002e when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x002d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "XV-2020", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x0030 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x002f), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "VariOS", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + } +}, +{ + /* has ID 0x0034 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0033), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "PCR", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, + /* TODO: add Roland M-1000 support */ +{ + /* + * Has ID 0x0038 when not in "Advanced Driver" mode; + * later revisions use IDs 0x0054 and 0x00a2. + */ + USB_DEVICE(0x0582, 0x0037), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "Digital Piano", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the GS-10 + * has ID 0x003c and is standard compliant, but has only 16-bit PCM + * and no MIDI. + */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "BOSS", + .product_name = "GS-10", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x0041 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0040), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "GI-20", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x0043 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0042), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "RS-70", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x0049 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0047), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "EDIROL", */ + /* .product_name = "UR-80", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + /* in the 96 kHz modes, only interface 1 is there */ + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x004a when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0048), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "EDIROL", */ + /* .product_name = "UR-80", */ + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, + /* TODO: add Edirol M-100FX support */ +{ + /* has ID 0x004e when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x004c), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "PCR-A", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x004f when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x004d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "PCR-A", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-3FX + * is standard compliant, but has only 16-bit PCM. + */ + USB_DEVICE(0x0582, 0x0050), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-3FX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0052), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-1SX", + .ifnum = 0, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ + USB_DEVICE(0x0582, 0x0060), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "EXR Series", + .ifnum = 0, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ + /* has ID 0x0067 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0065), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "PCR-1", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0003 + } + } +}, +{ + /* has ID 0x006b when not in "Advanced Driver" mode */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x006a), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SP-606", + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x006e when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x006d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "FANTOM-X", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ /* + * This quirk is for the "Advanced" modes of the Edirol UA-25. + * If the switch is not in an advanced setting, the UA-25 has + * ID 0x0582/0x0073 and is standard compliant (no quirks), but + * offers only 16-bit PCM at 44.1 kHz and no MIDI. + */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x0074), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-25", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x0076 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0075), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "BOSS", + .product_name = "DR-880", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* has ID 0x007b when not in "Advanced Driver" mode */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x007a), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + /* "RD" or "RD-700SX"? */ + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + /* has ID 0x0081 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0080), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "G-70", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, + /* TODO: add Roland V-SYNTH XT support */ + /* TODO: add BOSS GT-PRO support */ +{ + /* has ID 0x008c when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x008b), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "PC-50", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, + /* TODO: add Edirol PC-80 support */ +{ + USB_DEVICE(0x0582, 0x0096), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-1EX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x009a), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UM-3EX", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX + * is standard compliant, but has only 16-bit PCM and no MIDI. + */ + USB_DEVICE(0x0582, 0x00a3), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-4FX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = -1 + } + } + } +}, + /* TODO: add Edirol MD-P1 support */ +{ + USB_DEVICE(0x582, 0x00a6), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "Juno-G", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + /* Roland SH-201 */ + USB_DEVICE(0x0582, 0x00ad), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SH-201", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* Roland SonicCell */ + USB_DEVICE(0x0582, 0x00c2), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SonicCell", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* Edirol M-16DX */ + /* FIXME: This quirk gives a good-working capture stream but the + * playback seems problematic because of lacking of sync + * with capture stream. It needs to sync with the capture + * clock. As now, you'll get frequent sound distortions + * via the playback. + */ + USB_DEVICE(0x0582, 0x00c4), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* BOSS GT-10 */ + USB_DEVICE(0x0582, 0x00da), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* Advanced modes of the Edirol UA-25EX. + * For the standard mode, UA-25EX has ID 0582:00e7, which + * offers only 16-bit PCM at 44.1 kHz and no MIDI. + */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e6), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-25EX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_EDIROL_UAXX + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* has ID 0x00ea when not in Advanced Driver mode */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Roland", */ + /* .product_name = "UA-1G", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, + +/* Guillemot devices */ +{ + /* + * This is for the "Windows Edition" where the external MIDI ports are + * the only MIDI ports; the control data is reported through HID + * interfaces. The "Macintosh Edition" has ID 0xd002 and uses standard + * compliant USB MIDI ports for external MIDI and controls. + */ + USB_DEVICE_VENDOR_SPEC(0x06f8, 0xb000), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Hercules", + .product_name = "DJ Console (WE)", + .ifnum = 4, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, + +/* Midiman/M-Audio devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 2x2", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 1x1", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "Keystation", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 4x4", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + /* + * For hardware revision 1.05; in the later revisions (1.10 and + * 1.21), 0x1031 is the ID for the device without firmware. + * Thanks to Olaf Giesbrecht + */ + USB_DEVICE_VER(0x0763, 0x1031, 0x0100, 0x0109), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 8x8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 8x8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1041), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 2x4", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x000f, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "Quattro", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + /* + * Interfaces 0-2 are "Windows-compatible", 16-bit only, + * and share endpoints with the other interfaces. + * Ignore them. The other interfaces can do 24 bits, + * but captured samples are big-endian (see usbaudio.c). + */ + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 5, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 6, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 7, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 8, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "AudioPhile", + .ifnum = 6, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2008), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "Ozone", + .ifnum = 3, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x200d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "M-Audio", + .product_name = "OmniStudio", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 5, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 6, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 7, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 8, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0763, 0x2019), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Ozone Academic", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, + +/* Casio devices */ +{ + USB_DEVICE(0x07cf, 0x6801), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Casio", + .product_name = "PL-40R", + .ifnum = 0, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + /* this ID is used by several devices without a product ID */ + USB_DEVICE(0x07cf, 0x6802), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Casio", + .product_name = "Keyboard", + .ifnum = 0, + .type = QUIRK_MIDI_YAMAHA + } +}, + +/* Mark of the Unicorn devices */ +{ + /* thanks to Robert A. Lerche */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_PRODUCT | + USB_DEVICE_ID_MATCH_DEV_SUBCLASS, + .idVendor = 0x07fd, + .idProduct = 0x0001, + .bDeviceSubClass = 2, + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "MOTU", + .product_name = "Fastlane", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_MIDI_FASTLANE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, + +/* Emagic devices */ +{ + USB_DEVICE(0x086a, 0x0001), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Emagic", + /* .product_name = "Unitor8", */ + .ifnum = 2, + .type = QUIRK_MIDI_EMAGIC, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x80ff, + .in_cables = 0x80ff + } + } +}, +{ + USB_DEVICE(0x086a, 0x0002), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Emagic", + /* .product_name = "AMT8", */ + .ifnum = 2, + .type = QUIRK_MIDI_EMAGIC, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x80ff, + .in_cables = 0x80ff + } + } +}, +{ + USB_DEVICE(0x086a, 0x0003), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Emagic", + /* .product_name = "MT4", */ + .ifnum = 2, + .type = QUIRK_MIDI_EMAGIC, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x800f, + .in_cables = 0x8003 + } + } +}, + +/* TerraTec devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "TerraTec", + .product_name = "PHASE 26", + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "TerraTec", + .product_name = "PHASE 26", + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0014), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "TerraTec", + .product_name = "PHASE 26", + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ + USB_DEVICE(0x0ccd, 0x0028), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "TerraTec", + .product_name = "Aureon5.1MkII", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + USB_DEVICE(0x0ccd, 0x0035), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Miditech", + .product_name = "Play'n Roll", + .ifnum = 0, + .type = QUIRK_MIDI_CME + } +}, + +/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ +{ + USB_DEVICE(0x103d, 0x0100), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Stanton", + .product_name = "ScratchAmp", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + USB_DEVICE(0x103d, 0x0101), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Stanton", + .product_name = "ScratchAmp", + .ifnum = QUIRK_NO_INTERFACE + } +}, + +/* Novation EMS devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x1235, 0x0001), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Novation", + .product_name = "ReMOTE Audio/XStation", + .ifnum = 4, + .type = QUIRK_MIDI_NOVATION + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x1235, 0x0002), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Novation", + .product_name = "Speedio", + .ifnum = 3, + .type = QUIRK_MIDI_NOVATION + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Novation", + .product_name = "ReMOTE25", + .ifnum = 0, + .type = QUIRK_MIDI_NOVATION + } +}, + +/* Access Music devices */ +{ + /* VirusTI Desktop */ + USB_DEVICE_VENDOR_SPEC(0x133e, 0x0815), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &(const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + }, + { + .ifnum = 4, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, + +/* */ +{ + /* aka. Serato Scratch Live DJ Box */ + USB_DEVICE(0x13e5, 0x0001), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Rane", + .product_name = "SL-1", + .ifnum = QUIRK_NO_INTERFACE + } +}, + +/* Miditech devices */ +{ + USB_DEVICE(0x4752, 0x0011), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Miditech", + .product_name = "Midistart-2", + .ifnum = 0, + .type = QUIRK_MIDI_CME + } +}, + +/* Central Music devices */ +{ + /* this ID used by both Miditech MidiStudio-2 and CME UF-x */ + USB_DEVICE(0x7104, 0x2202), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = 0, + .type = QUIRK_MIDI_CME + } +}, + +/* Hauppauge HVR-950Q and HVR-850 */ +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7200), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7201), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7202), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7203), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7204), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7205), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7250), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-950Q", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x2040, 0x7230), + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Hauppauge", + .product_name = "HVR-850", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + } +}, + +/* Digidesign Mbox */ +{ + /* Thanks to Clemens Ladisch */ + USB_DEVICE(0x0dba, 0x1000), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Digidesign", + .product_name = "MBox", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]){ + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3BE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x02, + .ep_attr = 0x01, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { + .ifnum = -1 + } + } + + } +}, + +{ + /* + * Some USB MIDI devices don't have an audio control interface, + * so we have to grab MIDI streaming interfaces here. + */ + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, + +#undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c new file mode 100644 index 0000000..4c16920 --- /dev/null +++ b/sound/usb/quirks.c @@ -0,0 +1,592 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "usbmixer.h" +#include "midi.h" +#include "quirks.h" +#include "helper.h" +#include "endpoint.h" +#include "pcm.h" + +/* + * handle the quirks for the contained interfaces + */ +static int create_composite_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + int err; + + for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); + if (!iface) + continue; + if (quirk->ifnum != probed_ifnum && + usb_interface_claimed(iface)) + continue; + err = snd_usb_create_quirk(chip, iface, driver, quirk); + if (err < 0) + return err; + if (quirk->ifnum != probed_ifnum) + usb_driver_claim_interface(driver, iface, (void *)-1L); + } + return 0; +} + +static int ignore_interface_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + return 0; +} + + +/* + * Allow alignment on audio sub-slot (channel samples) rather than + * on audio slots (audio frames) + */ +static int create_align_transfer_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + chip->txfr_quirk = 1; + return 1; /* Continue with creating streams and mixer */ +} + +static int create_any_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *intf, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk); +} + +/* + * create a stream for an interface with proper descriptors + */ +static int create_standard_audio_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + int err; + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber); + if (err < 0) { + snd_printk(KERN_ERR "cannot setup if %d: error %d\n", + altsd->bInterfaceNumber, err); + return err; + } + /* reset the current interface */ + usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0); + return 0; +} + +/* + * create a stream for an endpoint/altsetting without proper descriptors + */ +static int create_fixed_stream_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + struct audioformat *fp; + struct usb_host_interface *alts; + int stream, err; + unsigned *rate_table = NULL; + + fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL); + if (! fp) { + snd_printk(KERN_ERR "cannot memdup\n"); + return -ENOMEM; + } + if (fp->nr_rates > 0) { + rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL); + if (!rate_table) { + kfree(fp); + return -ENOMEM; + } + memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates); + fp->rate_table = rate_table; + } + + stream = (fp->endpoint & USB_DIR_IN) + ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + err = snd_usb_add_audio_endpoint(chip, stream, fp); + if (err < 0) { + kfree(fp); + kfree(rate_table); + return err; + } + if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || + fp->altset_idx >= iface->num_altsetting) { + kfree(fp); + kfree(rate_table); + return -EINVAL; + } + alts = &iface->altsetting[fp->altset_idx]; + fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + usb_set_interface(chip->dev, fp->iface, 0); + snd_usb_init_pitch(chip->dev, fp->iface, alts, fp); + snd_usb_init_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max); + return 0; +} + +/* + * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface. + * The only way to detect the sample rate is by looking at wMaxPacketSize. + */ +static int create_uaxx_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + static const struct audioformat ua_format = { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .fmt_type = UAC_FORMAT_TYPE_I, + .altsetting = 1, + .altset_idx = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + }; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct audioformat *fp; + int stream, err; + + /* both PCM and MIDI interfaces have 2 or more altsettings */ + if (iface->num_altsetting < 2) + return -ENXIO; + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + + if (altsd->bNumEndpoints == 2) { + static const struct snd_usb_midi_endpoint_info ua700_ep = { + .out_cables = 0x0003, + .in_cables = 0x0003 + }; + static const struct snd_usb_audio_quirk ua700_quirk = { + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &ua700_ep + }; + static const struct snd_usb_midi_endpoint_info uaxx_ep = { + .out_cables = 0x0001, + .in_cables = 0x0001 + }; + static const struct snd_usb_audio_quirk uaxx_quirk = { + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &uaxx_ep + }; + const struct snd_usb_audio_quirk *quirk = + chip->usb_id == USB_ID(0x0582, 0x002b) + ? &ua700_quirk : &uaxx_quirk; + return snd_usbmidi_create(chip->card, iface, + &chip->midi_list, quirk); + } + + if (altsd->bNumEndpoints != 1) + return -ENXIO; + + fp = kmalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return -ENOMEM; + memcpy(fp, &ua_format, sizeof(*fp)); + + fp->iface = altsd->bInterfaceNumber; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = 0; + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + + switch (fp->maxpacksize) { + case 0x120: + fp->rate_max = fp->rate_min = 44100; + break; + case 0x138: + case 0x140: + fp->rate_max = fp->rate_min = 48000; + break; + case 0x258: + case 0x260: + fp->rate_max = fp->rate_min = 96000; + break; + default: + snd_printk(KERN_ERR "unknown sample rate\n"); + kfree(fp); + return -ENXIO; + } + + stream = (fp->endpoint & USB_DIR_IN) + ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + err = snd_usb_add_audio_endpoint(chip, stream, fp); + if (err < 0) { + kfree(fp); + return err; + } + usb_set_interface(chip->dev, fp->iface, 0); + return 0; +} + +/* + * audio-interface quirks + * + * returns zero if no standard audio/MIDI parsing is needed. + * returns a postive value if standard audio/midi interfaces are parsed + * after this. + * returns a negative value at error. + */ +int snd_usb_create_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + typedef int (*quirk_func_t)(struct snd_usb_audio *, + struct usb_interface *, + struct usb_driver *, + const struct snd_usb_audio_quirk *); + static const quirk_func_t quirk_funcs[] = { + [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, + [QUIRK_COMPOSITE] = create_composite_quirk, + [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk, + [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk, + [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk, + [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk, + [QUIRK_MIDI_NOVATION] = create_any_midi_quirk, + [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk, + [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk, + [QUIRK_MIDI_CME] = create_any_midi_quirk, + [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, + [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, + [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, + [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk + }; + + if (quirk->type < QUIRK_TYPE_COUNT) { + return quirk_funcs[quirk->type](chip, iface, driver, quirk); + } else { + snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); + return -ENXIO; + } +} + +/* + * boot quirks + */ + +#define EXTIGY_FIRMWARE_SIZE_OLD 794 +#define EXTIGY_FIRMWARE_SIZE_NEW 483 + +static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf) +{ + struct usb_host_config *config = dev->actconfig; + int err; + + if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD || + le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) { + snd_printdd("sending Extigy boot sequence...\n"); + /* Send message to force it to reconnect with full interface. */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0), + 0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000); + if (err < 0) snd_printdd("error sending boot message: %d\n", err); + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, + &dev->descriptor, sizeof(dev->descriptor)); + config = dev->actconfig; + if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err); + err = usb_reset_configuration(dev); + if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err); + snd_printdd("extigy_boot: new boot length = %d\n", + le16_to_cpu(get_cfg_desc(config)->wTotalLength)); + return -ENODEV; /* quit this anyway */ + } + return 0; +} + +static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) +{ + u8 buf = 1; + + snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 0, 0, &buf, 1, 1000); + if (buf == 0) { + snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 1, 2000, NULL, 0, 1000); + return -ENODEV; + } + return 0; +} + +/* + * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely + * documented in the device's data sheet. + */ +static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value) +{ + u8 buf[4]; + buf[0] = 0x20; + buf[1] = value & 0xff; + buf[2] = (value >> 8) & 0xff; + buf[3] = reg; + return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, 0, &buf, 4, 1000); +} + +static int snd_usb_cm106_boot_quirk(struct usb_device *dev) +{ + /* + * Enable line-out driver mode, set headphone source to front + * channels, enable stereo mic. + */ + return snd_usb_cm106_write_int_reg(dev, 2, 0x8004); +} + +/* + * C-Media CM6206 is based on CM106 with two additional + * registers that are not documented in the data sheet. + * Values here are chosen based on sniffing USB traffic + * under Windows. + */ +static int snd_usb_cm6206_boot_quirk(struct usb_device *dev) +{ + int err, reg; + int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000}; + + for (reg = 0; reg < ARRAY_SIZE(val); reg++) { + err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]); + if (err < 0) + return err; + } + + return err; +} + +/* + * This call will put the synth in "USB send" mode, i.e it will send MIDI + * messages through USB (this is disabled at startup). The synth will + * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB + * sign on its LCD. Values here are chosen based on sniffing USB traffic + * under Windows. + */ +static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) +{ + int err, actual_length; + + /* "midi send" enable */ + static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 }; + + void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); + if (!buf) + return -ENOMEM; + err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf, + ARRAY_SIZE(seq), &actual_length, 1000); + kfree(buf); + if (err < 0) + return err; + + return 0; +} + +/* + * Setup quirks + */ +#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */ +#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */ +#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */ +#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */ +#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */ +#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */ +#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */ +#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */ +#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */ +#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */ + +static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, + int iface, + int altno) +{ + /* Reset ALL ifaces to 0 altsetting. + * Call it for every possible altsetting of every interface. + */ + usb_set_interface(chip->dev, iface, 0); + + if (chip->setup & AUDIOPHILE_SET) { + if ((chip->setup & AUDIOPHILE_SET_DTS) + && altno != 6) + return 1; /* skip this altsetting */ + if ((chip->setup & AUDIOPHILE_SET_96K) + && altno != 1) + return 1; /* skip this altsetting */ + if ((chip->setup & AUDIOPHILE_SET_MASK) == + AUDIOPHILE_SET_24B_48K_DI && altno != 2) + return 1; /* skip this altsetting */ + if ((chip->setup & AUDIOPHILE_SET_MASK) == + AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3) + return 1; /* skip this altsetting */ + if ((chip->setup & AUDIOPHILE_SET_MASK) == + AUDIOPHILE_SET_16B_48K_DI && altno != 4) + return 1; /* skip this altsetting */ + if ((chip->setup & AUDIOPHILE_SET_MASK) == + AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5) + return 1; /* skip this altsetting */ + } + + return 0; /* keep this altsetting */ +} + +int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, + int iface, + int altno) +{ + /* audiophile usb: skip altsets incompatible with device_setup */ + if (chip->usb_id == USB_ID(0x0763, 0x2003)) + return audiophile_skip_setting_quirk(chip, iface, altno); + + return 0; +} + +int snd_usb_apply_boot_quirk(struct usb_device *dev, + struct usb_interface *intf, + const struct snd_usb_audio_quirk *quirk) +{ + u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* SB Extigy needs special boot-up sequence */ + /* if more models come, this will go to the quirk list. */ + if (id == USB_ID(0x041e, 0x3000)) + return snd_usb_extigy_boot_quirk(dev, intf); + + /* SB Audigy 2 NX needs its own boot-up magic, too */ + if (id == USB_ID(0x041e, 0x3020)) + return snd_usb_audigy2nx_boot_quirk(dev); + + /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */ + if (id == USB_ID(0x10f5, 0x0200)) + return snd_usb_cm106_boot_quirk(dev); + + /* C-Media CM6206 / CM106-Like Sound Device */ + if (id == USB_ID(0x0d8c, 0x0102)) + return snd_usb_cm6206_boot_quirk(dev); + + /* Access Music VirusTI Desktop */ + if (id == USB_ID(0x133e, 0x0815)) + return snd_usb_accessmusic_boot_quirk(dev); + + return 0; +} + +/* + * check if the device uses big-endian samples + */ +int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp) +{ + switch (chip->usb_id) { + case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */ + if (fp->endpoint & USB_DIR_IN) + return 1; + break; + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + if (chip->setup == 0x00 || + fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3) + return 1; + } + return 0; +} + +/* + * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device, + * not for interface. + */ + +enum { + EMU_QUIRK_SR_44100HZ = 0, + EMU_QUIRK_SR_48000HZ, + EMU_QUIRK_SR_88200HZ, + EMU_QUIRK_SR_96000HZ, + EMU_QUIRK_SR_176400HZ, + EMU_QUIRK_SR_192000HZ +}; + +static void set_format_emu_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + unsigned char emu_samplerate_id = 0; + + /* When capture is active + * sample rate shouldn't be changed + * by playback substream + */ + if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1) + return; + } + + switch (fmt->rate_min) { + case 48000: + emu_samplerate_id = EMU_QUIRK_SR_48000HZ; + break; + case 88200: + emu_samplerate_id = EMU_QUIRK_SR_88200HZ; + break; + case 96000: + emu_samplerate_id = EMU_QUIRK_SR_96000HZ; + break; + case 176400: + emu_samplerate_id = EMU_QUIRK_SR_176400HZ; + break; + case 192000: + emu_samplerate_id = EMU_QUIRK_SR_192000HZ; + break; + default: + emu_samplerate_id = EMU_QUIRK_SR_44100HZ; + break; + } + snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id); +} + +void snd_usb_set_format_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + switch (subs->stream->chip->usb_id) { + case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ + case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ + case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ + set_format_emu_quirk(subs, fmt); + break; + } +} + diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h new file mode 100644 index 0000000..03e5e94 --- /dev/null +++ b/sound/usb/quirks.h @@ -0,0 +1,23 @@ +#ifndef __USBAUDIO_QUIRKS_H +#define __USBAUDIO_QUIRKS_H + +int snd_usb_create_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk); + +int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, + int iface, + int altno); + +int snd_usb_apply_boot_quirk(struct usb_device *dev, + struct usb_interface *intf, + const struct snd_usb_audio_quirk *quirk); + +void snd_usb_set_format_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt); + +int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, + struct audioformat *fp); + +#endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/urb.c b/sound/usb/urb.c new file mode 100644 index 0000000..e9c339f --- /dev/null +++ b/sound/usb/urb.c @@ -0,0 +1,989 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include + +#include "usbaudio.h" +#include "helper.h" +#include "card.h" +#include "urb.h" +#include "pcm.h" + +/* + * convert a sampling rate into our full speed format (fs/1000 in Q16.16) + * this will overflow at approx 524 kHz + */ +static inline unsigned get_usb_full_speed_rate(unsigned int rate) +{ + return ((rate << 13) + 62) / 125; +} + +/* + * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) + * this will overflow at approx 4 MHz + */ +static inline unsigned get_usb_high_speed_rate(unsigned int rate) +{ + return ((rate << 10) + 62) / 125; +} + +/* + * unlink active urbs. + */ +static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep) +{ + struct snd_usb_audio *chip = subs->stream->chip; + unsigned int i; + int async; + + subs->running = 0; + + if (!force && subs->stream->chip->shutdown) /* to be sure... */ + return -EBADFD; + + async = !can_sleep && chip->async_unlink; + + if (!async && in_interrupt()) + return 0; + + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) { + if (!test_and_set_bit(i, &subs->unlink_mask)) { + struct urb *u = subs->dataurb[i].urb; + if (async) + usb_unlink_urb(u); + else + usb_kill_urb(u); + } + } + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i+16, &subs->active_mask)) { + if (!test_and_set_bit(i+16, &subs->unlink_mask)) { + struct urb *u = subs->syncurb[i].urb; + if (async) + usb_unlink_urb(u); + else + usb_kill_urb(u); + } + } + } + } + return 0; +} + + +/* + * release a urb data + */ +static void release_urb_ctx(struct snd_urb_ctx *u) +{ + if (u->urb) { + if (u->buffer_size) + usb_buffer_free(u->subs->dev, u->buffer_size, + u->urb->transfer_buffer, + u->urb->transfer_dma); + usb_free_urb(u->urb); + u->urb = NULL; + } +} + +/* + * wait until all urbs are processed. + */ +static int wait_clear_urbs(struct snd_usb_substream *subs) +{ + unsigned long end_time = jiffies + msecs_to_jiffies(1000); + unsigned int i; + int alive; + + do { + alive = 0; + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) + alive++; + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i + 16, &subs->active_mask)) + alive++; + } + } + if (! alive) + break; + schedule_timeout_uninterruptible(1); + } while (time_before(jiffies, end_time)); + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); + return 0; +} + +/* + * release a substream + */ +void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force) +{ + int i; + + /* stop urbs (to be sure) */ + deactivate_urbs(subs, force, 1); + wait_clear_urbs(subs); + + for (i = 0; i < MAX_URBS; i++) + release_urb_ctx(&subs->dataurb[i]); + for (i = 0; i < SYNC_URBS; i++) + release_urb_ctx(&subs->syncurb[i]); + usb_buffer_free(subs->dev, SYNC_URBS * 4, + subs->syncbuf, subs->sync_dma); + subs->syncbuf = NULL; + subs->nurbs = 0; +} + +/* + * complete callback from data urb + */ +static void snd_complete_urb(struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_substream *subs = ctx->subs; + struct snd_pcm_substream *substream = ctx->subs->pcm_substream; + int err = 0; + + if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || + !subs->running || /* can be stopped during retire callback */ + (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + clear_bit(ctx->index, &subs->active_mask); + if (err < 0) { + snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + } + } +} + + +/* + * complete callback from sync urb + */ +static void snd_complete_sync_urb(struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_substream *subs = ctx->subs; + struct snd_pcm_substream *substream = ctx->subs->pcm_substream; + int err = 0; + + if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) || + !subs->running || /* can be stopped during retire callback */ + (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + clear_bit(ctx->index + 16, &subs->active_mask); + if (err < 0) { + snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + } + } +} + + +/* + * initialize a substream for plaback/capture + */ +int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, + unsigned int period_bytes, + unsigned int rate, + unsigned int frame_bits) +{ + unsigned int maxsize, i; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int urb_packs, total_packs, packs_per_ms; + struct snd_usb_audio *chip = subs->stream->chip; + + /* calculate the frequency in 16.16 format */ + if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) + subs->freqn = get_usb_full_speed_rate(rate); + else + subs->freqn = get_usb_high_speed_rate(rate); + subs->freqm = subs->freqn; + /* calculate max. frequency */ + if (subs->maxpacksize) { + /* whatever fits into a max. size packet */ + maxsize = subs->maxpacksize; + subs->freqmax = (maxsize / (frame_bits >> 3)) + << (16 - subs->datainterval); + } else { + /* no max. packet size: just take 25% higher than nominal */ + subs->freqmax = subs->freqn + (subs->freqn >> 2); + maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) + >> (16 - subs->datainterval); + } + subs->phase = 0; + + if (subs->fill_max) + subs->curpacksize = subs->maxpacksize; + else + subs->curpacksize = maxsize; + + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) + packs_per_ms = 8 >> subs->datainterval; + else + packs_per_ms = 1; + + if (is_playback) { + urb_packs = max(chip->nrpacks, 1); + urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); + } else + urb_packs = 1; + urb_packs *= packs_per_ms; + if (subs->syncpipe) + urb_packs = min(urb_packs, 1U << subs->syncinterval); + + /* decide how many packets to be used */ + if (is_playback) { + unsigned int minsize, maxpacks; + /* determine how small a packet can be */ + minsize = (subs->freqn >> (16 - subs->datainterval)) + * (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (subs->syncpipe) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + total_packs = (period_bytes + minsize - 1) / minsize; + /* we need at least two URBs for queueing */ + if (total_packs < 2) { + total_packs = 2; + } else { + /* and we don't want too long a queue either */ + maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); + total_packs = min(total_packs, maxpacks); + } + } else { + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + total_packs = MAX_URBS * urb_packs; + } + subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; + if (subs->nurbs > MAX_URBS) { + /* too much... */ + subs->nurbs = MAX_URBS; + total_packs = MAX_URBS * urb_packs; + } else if (subs->nurbs < 2) { + /* too little - we need at least two packets + * to ensure contiguous playback/capture + */ + subs->nurbs = 2; + } + + /* allocate and initialize data urbs */ + for (i = 0; i < subs->nurbs; i++) { + struct snd_urb_ctx *u = &subs->dataurb[i]; + u->index = i; + u->subs = subs; + u->packets = (i + 1) * total_packs / subs->nurbs + - i * total_packs / subs->nurbs; + u->buffer_size = maxsize * u->packets; + if (subs->fmt_type == UAC_FORMAT_TYPE_II) + u->packets++; /* for transfer delimiter */ + u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); + if (!u->urb) + goto out_of_memory; + u->urb->transfer_buffer = + usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL, + &u->urb->transfer_dma); + if (!u->urb->transfer_buffer) + goto out_of_memory; + u->urb->pipe = subs->datapipe; + u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + u->urb->interval = 1 << subs->datainterval; + u->urb->context = u; + u->urb->complete = snd_complete_urb; + } + + if (subs->syncpipe) { + /* allocate and initialize sync urbs */ + subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4, + GFP_KERNEL, &subs->sync_dma); + if (!subs->syncbuf) + goto out_of_memory; + for (i = 0; i < SYNC_URBS; i++) { + struct snd_urb_ctx *u = &subs->syncurb[i]; + u->index = i; + u->subs = subs; + u->packets = 1; + u->urb = usb_alloc_urb(1, GFP_KERNEL); + if (!u->urb) + goto out_of_memory; + u->urb->transfer_buffer = subs->syncbuf + i * 4; + u->urb->transfer_dma = subs->sync_dma + i * 4; + u->urb->transfer_buffer_length = 4; + u->urb->pipe = subs->syncpipe; + u->urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + u->urb->number_of_packets = 1; + u->urb->interval = 1 << subs->syncinterval; + u->urb->context = u; + u->urb->complete = snd_complete_sync_urb; + } + } + return 0; + +out_of_memory: + snd_usb_release_substream_urbs(subs, 0); + return -ENOMEM; +} + +/* + * prepare urb for full speed capture sync pipe + * + * fill the length and offset of each urb descriptor. + * the fixed 10.14 frequency is passed through the pipe. + */ +static int prepare_capture_sync_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned char *cp = urb->transfer_buffer; + struct snd_urb_ctx *ctx = urb->context; + + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].offset = 0; + cp[0] = subs->freqn >> 2; + cp[1] = subs->freqn >> 10; + cp[2] = subs->freqn >> 18; + return 0; +} + +/* + * prepare urb for high speed capture sync pipe + * + * fill the length and offset of each urb descriptor. + * the fixed 12.13 frequency is passed as 16.16 through the pipe. + */ +static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned char *cp = urb->transfer_buffer; + struct snd_urb_ctx *ctx = urb->context; + + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->iso_frame_desc[0].length = 4; + urb->iso_frame_desc[0].offset = 0; + cp[0] = subs->freqn; + cp[1] = subs->freqn >> 8; + cp[2] = subs->freqn >> 16; + cp[3] = subs->freqn >> 24; + return 0; +} + +/* + * process after capture sync complete + * - nothing to do + */ +static int retire_capture_sync_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + return 0; +} + +/* + * prepare urb for capture data pipe + * + * fill the offset and length of each descriptor. + * + * we use a temporary buffer to write the captured data. + * since the length of written data is determined by host, we cannot + * write onto the pcm buffer directly... the data is thus copied + * later at complete callback to the global buffer. + */ +static int prepare_capture_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + int i, offs; + struct snd_urb_ctx *ctx = urb->context; + + offs = 0; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + for (i = 0; i < ctx->packets; i++) { + urb->iso_frame_desc[i].offset = offs; + urb->iso_frame_desc[i].length = subs->curpacksize; + offs += subs->curpacksize; + } + urb->transfer_buffer_length = offs; + urb->number_of_packets = ctx->packets; + return 0; +} + +/* + * process after capture complete + * + * copy the data from each desctiptor to the pcm buffer, and + * update the current position. + */ +static int retire_capture_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned long flags; + unsigned char *cp; + int i; + unsigned int stride, frames, bytes, oldptr; + int period_elapsed = 0; + + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { + snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status); + // continue; + } + bytes = urb->iso_frame_desc[i].actual_length; + frames = bytes / stride; + if (!subs->txfr_quirk) + bytes = frames * stride; + if (bytes % (runtime->sample_bits >> 3) != 0) { +#ifdef CONFIG_SND_DEBUG_VERBOSE + int oldbytes = bytes; +#endif + bytes = frames * stride; + snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", + oldbytes, bytes); + } + /* update the current pointer */ + spin_lock_irqsave(&subs->lock, flags); + oldptr = subs->hwptr_done; + subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; + frames = (bytes + (oldptr % stride)) / stride; + subs->transfer_done += frames; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + period_elapsed = 1; + } + spin_unlock_irqrestore(&subs->lock, flags); + /* copy a data chunk */ + if (oldptr + bytes > runtime->buffer_size * stride) { + unsigned int bytes1 = + runtime->buffer_size * stride - oldptr; + memcpy(runtime->dma_area + oldptr, cp, bytes1); + memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1); + } else { + memcpy(runtime->dma_area + oldptr, cp, bytes); + } + } + if (period_elapsed) + snd_pcm_period_elapsed(subs->pcm_substream); + return 0; +} + +/* + * Process after capture complete when paused. Nothing to do. + */ +static int retire_paused_capture_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + return 0; +} + + +/* + * prepare urb for full speed playback sync pipe + * + * set up the offset and length to receive the current frequency. + */ + +static int prepare_playback_sync_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].offset = 0; + return 0; +} + +/* + * prepare urb for high speed playback sync pipe + * + * set up the offset and length to receive the current frequency. + */ + +static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->iso_frame_desc[0].length = 4; + urb->iso_frame_desc[0].offset = 0; + return 0; +} + +/* + * process after full speed playback sync complete + * + * retrieve the current 10.14 frequency from pipe, and set it. + * the value is referred in prepare_playback_urb(). + */ +static int retire_playback_sync_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned int f; + unsigned long flags; + + if (urb->iso_frame_desc[0].status == 0 && + urb->iso_frame_desc[0].actual_length == 3) { + f = combine_triple((u8*)urb->transfer_buffer) << 2; + if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } + } + + return 0; +} + +/* + * process after high speed playback sync complete + * + * retrieve the current 12.13 frequency from pipe, and set it. + * the value is referred in prepare_playback_urb(). + */ +static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned int f; + unsigned long flags; + + if (urb->iso_frame_desc[0].status == 0 && + urb->iso_frame_desc[0].actual_length == 4) { + f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; + if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } + } + + return 0; +} + +/* + * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete + * + * These devices return the number of samples per packet instead of the number + * of samples per microframe. + */ +static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned int f; + unsigned long flags; + + if (urb->iso_frame_desc[0].status == 0 && + urb->iso_frame_desc[0].actual_length == 4) { + f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; + f >>= subs->datainterval; + if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } + } + + return 0; +} + +/* determine the number of frames in the next packet */ +static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) +{ + if (subs->fill_max) + return subs->maxframesize; + else { + subs->phase = (subs->phase & 0xffff) + + (subs->freqm << subs->datainterval); + return min(subs->phase >> 16, subs->maxframesize); + } +} + +/* + * Prepare urb for streaming before playback starts or when paused. + * + * We don't have any data, so we send silence. + */ +static int prepare_nodata_playback_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned int i, offs, counts; + struct snd_urb_ctx *ctx = urb->context; + int stride = runtime->frame_bits >> 3; + + offs = 0; + urb->dev = ctx->subs->dev; + for (i = 0; i < ctx->packets; ++i) { + counts = snd_usb_audio_next_packet_size(subs); + urb->iso_frame_desc[i].offset = offs * stride; + urb->iso_frame_desc[i].length = counts * stride; + offs += counts; + } + urb->number_of_packets = ctx->packets; + urb->transfer_buffer_length = offs * stride; + memset(urb->transfer_buffer, + subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0, + offs * stride); + return 0; +} + +/* + * prepare urb for playback data pipe + * + * Since a URB can handle only a single linear buffer, we must use double + * buffering when the data to be transferred overflows the buffer boundary. + * To avoid inconsistencies when updating hwptr_done, we use double buffering + * for all URBs. + */ +static int prepare_playback_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + int i, stride; + unsigned int counts, frames, bytes; + unsigned long flags; + int period_elapsed = 0; + struct snd_urb_ctx *ctx = urb->context; + + stride = runtime->frame_bits >> 3; + + frames = 0; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); + for (i = 0; i < ctx->packets; i++) { + counts = snd_usb_audio_next_packet_size(subs); + /* set up descriptor */ + urb->iso_frame_desc[i].offset = frames * stride; + urb->iso_frame_desc[i].length = counts * stride; + frames += counts; + urb->number_of_packets++; + subs->transfer_done += counts; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + period_elapsed = 1; + if (subs->fmt_type == UAC_FORMAT_TYPE_II) { + if (subs->transfer_done > 0) { + /* FIXME: fill-max mode is not + * supported yet */ + frames -= subs->transfer_done; + counts -= subs->transfer_done; + urb->iso_frame_desc[i].length = + counts * stride; + subs->transfer_done = 0; + } + i++; + if (i < ctx->packets) { + /* add a transfer delimiter */ + urb->iso_frame_desc[i].offset = + frames * stride; + urb->iso_frame_desc[i].length = 0; + urb->number_of_packets++; + } + break; + } + } + if (period_elapsed) /* finish at the period boundary */ + break; + } + bytes = frames * stride; + if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { + /* err, the transferred area goes over buffer boundary. */ + unsigned int bytes1 = + runtime->buffer_size * stride - subs->hwptr_done; + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes1); + memcpy(urb->transfer_buffer + bytes1, + runtime->dma_area, bytes - bytes1); + } else { + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes); + } + subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; + runtime->delay += frames; + spin_unlock_irqrestore(&subs->lock, flags); + urb->transfer_buffer_length = bytes; + if (period_elapsed) + snd_pcm_period_elapsed(subs->pcm_substream); + return 0; +} + +/* + * process after playback data complete + * - decrease the delay count again + */ +static int retire_playback_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned long flags; + int stride = runtime->frame_bits >> 3; + int processed = urb->transfer_buffer_length / stride; + + spin_lock_irqsave(&subs->lock, flags); + if (processed > runtime->delay) + runtime->delay = 0; + else + runtime->delay -= processed; + spin_unlock_irqrestore(&subs->lock, flags); + return 0; +} + +static const char *usb_error_string(int err) +{ + switch (err) { + case -ENODEV: + return "no device"; + case -ENOENT: + return "endpoint not enabled"; + case -EPIPE: + return "endpoint stalled"; + case -ENOSPC: + return "not enough bandwidth"; + case -ESHUTDOWN: + return "device disabled"; + case -EHOSTUNREACH: + return "device suspended"; + case -EINVAL: + case -EAGAIN: + case -EFBIG: + case -EMSGSIZE: + return "internal error"; + default: + return "unknown error"; + } +} + +/* + * set up and start data/sync urbs + */ +static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime) +{ + unsigned int i; + int err; + + if (subs->stream->chip->shutdown) + return -EBADFD; + + for (i = 0; i < subs->nurbs; i++) { + if (snd_BUG_ON(!subs->dataurb[i].urb)) + return -EINVAL; + if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) { + snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i); + goto __error; + } + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (snd_BUG_ON(!subs->syncurb[i].urb)) + return -EINVAL; + if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) { + snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i); + goto __error; + } + } + } + + subs->active_mask = 0; + subs->unlink_mask = 0; + subs->running = 1; + for (i = 0; i < subs->nurbs; i++) { + err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC); + if (err < 0) { + snd_printk(KERN_ERR "cannot submit datapipe " + "for urb %d, error %d: %s\n", + i, err, usb_error_string(err)); + goto __error; + } + set_bit(i, &subs->active_mask); + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC); + if (err < 0) { + snd_printk(KERN_ERR "cannot submit syncpipe " + "for urb %d, error %d: %s\n", + i, err, usb_error_string(err)); + goto __error; + } + set_bit(i + 16, &subs->active_mask); + } + } + return 0; + + __error: + // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); + deactivate_urbs(subs, 0, 0); + return -EPIPE; +} + + +/* + */ +static struct snd_urb_ops audio_urb_ops[2] = { + { + .prepare = prepare_nodata_playback_urb, + .retire = retire_playback_urb, + .prepare_sync = prepare_playback_sync_urb, + .retire_sync = retire_playback_sync_urb, + }, + { + .prepare = prepare_capture_urb, + .retire = retire_capture_urb, + .prepare_sync = prepare_capture_sync_urb, + .retire_sync = retire_capture_sync_urb, + }, +}; + +static struct snd_urb_ops audio_urb_ops_high_speed[2] = { + { + .prepare = prepare_nodata_playback_urb, + .retire = retire_playback_urb, + .prepare_sync = prepare_playback_sync_urb_hs, + .retire_sync = retire_playback_sync_urb_hs, + }, + { + .prepare = prepare_capture_urb, + .retire = retire_capture_urb, + .prepare_sync = prepare_capture_sync_urb_hs, + .retire_sync = retire_capture_sync_urb, + }, +}; + +/* + * initialize the substream instance. + */ + +void snd_usb_init_substream(struct snd_usb_stream *as, + int stream, struct audioformat *fp) +{ + struct snd_usb_substream *subs = &as->substream[stream]; + + INIT_LIST_HEAD(&subs->fmt_list); + spin_lock_init(&subs->lock); + + subs->stream = as; + subs->direction = stream; + subs->dev = as->chip->dev; + subs->txfr_quirk = as->chip->txfr_quirk; + if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) { + subs->ops = audio_urb_ops[stream]; + } else { + subs->ops = audio_urb_ops_high_speed[stream]; + switch (as->chip->usb_id) { + case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ + case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ + case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ + subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; + break; + } + } + + snd_usb_set_pcm_ops(as->pcm, stream); + + list_add_tail(&fp->list, &subs->fmt_list); + subs->formats |= 1ULL << fp->format; + subs->endpoint = fp->endpoint; + subs->num_formats++; + subs->fmt_type = fp->fmt_type; +} + +int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + subs->ops.prepare = prepare_playback_urb; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + return deactivate_urbs(subs, 0, 0); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->ops.prepare = prepare_nodata_playback_urb; + return 0; + } + + return -EINVAL; +} + +int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + subs->ops.retire = retire_capture_urb; + return start_urbs(subs, substream->runtime); + case SNDRV_PCM_TRIGGER_STOP: + return deactivate_urbs(subs, 0, 0); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->ops.retire = retire_paused_capture_urb; + return 0; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + subs->ops.retire = retire_capture_urb; + return 0; + } + + return -EINVAL; +} + +int snd_usb_substream_prepare(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime) +{ + /* clear urbs (to be sure) */ + deactivate_urbs(subs, 0, 1); + wait_clear_urbs(subs); + + /* for playback, submit the URBs now; otherwise, the first hwptr_done + * updates for all URBs would happen at the same time when starting */ + if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { + subs->ops.prepare = prepare_nodata_playback_urb; + return start_urbs(subs, runtime); + } + + return 0; +} + diff --git a/sound/usb/urb.h b/sound/usb/urb.h new file mode 100644 index 0000000..888da38 --- /dev/null +++ b/sound/usb/urb.h @@ -0,0 +1,21 @@ +#ifndef __USBAUDIO_URB_H +#define __USBAUDIO_URB_H + +void snd_usb_init_substream(struct snd_usb_stream *as, + int stream, + struct audioformat *fp); + +int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, + unsigned int period_bytes, + unsigned int rate, + unsigned int frame_bits); + +void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force); + +int snd_usb_substream_prepare(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime); + +int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd); +int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd); + +#endif /* __USBAUDIO_URB_H */ diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c deleted file mode 100644 index 5b91aa0..0000000 --- a/sound/usb/usbaudio.c +++ /dev/null @@ -1,4051 +0,0 @@ -/* - * (Tentative) USB Audio Driver for ALSA - * - * Main and PCM part - * - * Copyright (c) 2002 by Takashi Iwai - * - * Many codes borrowed from audio.c by - * Alan Cox (alan@lxorguk.ukuu.org.uk) - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * NOTES: - * - * - async unlink should be used for avoiding the sleep inside lock. - * 2.4.22 usb-uhci seems buggy for async unlinking and results in - * oops. in such a cse, pass async_unlink=0 option. - * - the linked URBs would be preferred but not used so far because of - * the instability of unlinking. - * - type II is not supported properly. there is no device which supports - * this type *correctly*. SB extigy looks as if it supports, but it's - * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream). - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "usbaudio.h" -#include "usbmidi.h" -#include "usbmixer.h" - -MODULE_AUTHOR("Takashi Iwai "); -MODULE_DESCRIPTION("USB Audio"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); - - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ -/* Vendor/product IDs for this card */ -static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; -static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; -static int nrpacks = 8; /* max. number of packets per urb */ -static int async_unlink = 1; -static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/ -static int ignore_ctl_error; - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for the USB audio adapter."); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable USB audio adapter."); -module_param_array(vid, int, NULL, 0444); -MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); -module_param_array(pid, int, NULL, 0444); -MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); -module_param(nrpacks, int, 0644); -MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); -module_param(async_unlink, bool, 0444); -MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); -module_param_array(device_setup, int, NULL, 0444); -MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); -module_param(ignore_ctl_error, bool, 0444); -MODULE_PARM_DESC(ignore_ctl_error, - "Ignore errors from USB controller for mixer interfaces."); - -/* - * debug the h/w constraints - */ -/* #define HW_CONST_DEBUG */ - - -/* - * - */ - -#define MAX_PACKS 20 -#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ -#define MAX_URBS 8 -#define SYNC_URBS 4 /* always four urbs for sync */ -#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */ - -struct audioformat { - struct list_head list; - snd_pcm_format_t format; /* format type */ - unsigned int channels; /* # channels */ - unsigned int fmt_type; /* USB audio format type (1-3) */ - unsigned int frame_size; /* samples per frame for non-audio */ - int iface; /* interface number */ - unsigned char altsetting; /* corresponding alternate setting */ - unsigned char altset_idx; /* array index of altenate setting */ - unsigned char attributes; /* corresponding attributes of cs endpoint */ - unsigned char endpoint; /* endpoint */ - unsigned char ep_attr; /* endpoint attributes */ - unsigned char datainterval; /* log_2 of data packet interval */ - unsigned int maxpacksize; /* max. packet size */ - unsigned int rates; /* rate bitmasks */ - unsigned int rate_min, rate_max; /* min/max rates */ - unsigned int nr_rates; /* number of rate table entries */ - unsigned int *rate_table; /* rate table */ -}; - -struct snd_usb_substream; - -struct snd_urb_ctx { - struct urb *urb; - unsigned int buffer_size; /* size of data buffer, if data URB */ - struct snd_usb_substream *subs; - int index; /* index for urb array */ - int packets; /* number of packets per urb */ -}; - -struct snd_urb_ops { - int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); - int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); - int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); - int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); -}; - -struct snd_usb_substream { - struct snd_usb_stream *stream; - struct usb_device *dev; - struct snd_pcm_substream *pcm_substream; - int direction; /* playback or capture */ - int interface; /* current interface */ - int endpoint; /* assigned endpoint */ - struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ - unsigned int cur_rate; /* current rate (for hw_params callback) */ - unsigned int period_bytes; /* current period bytes (for hw_params callback) */ - unsigned int format; /* USB data format */ - unsigned int datapipe; /* the data i/o pipe */ - unsigned int syncpipe; /* 1 - async out or adaptive in */ - unsigned int datainterval; /* log_2 of data packet interval */ - unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ - unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ - unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ - unsigned int freqmax; /* maximum sampling rate, used for buffer management */ - unsigned int phase; /* phase accumulator */ - unsigned int maxpacksize; /* max packet size in bytes */ - unsigned int maxframesize; /* max packet size in frames */ - unsigned int curpacksize; /* current packet size in bytes (for capture) */ - unsigned int curframesize; /* current packet size in frames (for capture) */ - unsigned int fill_max: 1; /* fill max packet size always */ - unsigned int txfr_quirk:1; /* allow sub-frame alignment */ - unsigned int fmt_type; /* USB audio format type (1-3) */ - - unsigned int running: 1; /* running status */ - - unsigned int hwptr_done; /* processed byte position in the buffer */ - unsigned int transfer_done; /* processed frames since last period update */ - unsigned long active_mask; /* bitmask of active urbs */ - unsigned long unlink_mask; /* bitmask of unlinked urbs */ - - unsigned int nurbs; /* # urbs */ - struct snd_urb_ctx dataurb[MAX_URBS]; /* data urb table */ - struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */ - char *syncbuf; /* sync buffer for all sync URBs */ - dma_addr_t sync_dma; /* DMA address of syncbuf */ - - u64 formats; /* format bitmasks (all or'ed) */ - unsigned int num_formats; /* number of supported audio formats (list) */ - struct list_head fmt_list; /* format list */ - struct snd_pcm_hw_constraint_list rate_list; /* limited rates */ - spinlock_t lock; - - struct snd_urb_ops ops; /* callbacks (must be filled at init) */ -}; - - -struct snd_usb_stream { - struct snd_usb_audio *chip; - struct snd_pcm *pcm; - int pcm_index; - unsigned int fmt_type; /* USB audio format type (1-3) */ - struct snd_usb_substream substream[2]; - struct list_head list; -}; - - -/* - * we keep the snd_usb_audio_t instances by ourselves for merging - * the all interfaces on the same card as one sound device. - */ - -static DEFINE_MUTEX(register_mutex); -static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; - - -/* - * convert a sampling rate into our full speed format (fs/1000 in Q16.16) - * this will overflow at approx 524 kHz - */ -static inline unsigned get_usb_full_speed_rate(unsigned int rate) -{ - return ((rate << 13) + 62) / 125; -} - -/* - * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) - * this will overflow at approx 4 MHz - */ -static inline unsigned get_usb_high_speed_rate(unsigned int rate) -{ - return ((rate << 10) + 62) / 125; -} - -/* convert our full speed USB rate into sampling rate in Hz */ -static inline unsigned get_full_speed_hz(unsigned int usb_rate) -{ - return (usb_rate * 125 + (1 << 12)) >> 13; -} - -/* convert our high speed USB rate into sampling rate in Hz */ -static inline unsigned get_high_speed_hz(unsigned int usb_rate) -{ - return (usb_rate * 125 + (1 << 9)) >> 10; -} - - -/* - * prepare urb for full speed capture sync pipe - * - * fill the length and offset of each urb descriptor. - * the fixed 10.14 frequency is passed through the pipe. - */ -static int prepare_capture_sync_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned char *cp = urb->transfer_buffer; - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 3; - urb->iso_frame_desc[0].offset = 0; - cp[0] = subs->freqn >> 2; - cp[1] = subs->freqn >> 10; - cp[2] = subs->freqn >> 18; - return 0; -} - -/* - * prepare urb for high speed capture sync pipe - * - * fill the length and offset of each urb descriptor. - * the fixed 12.13 frequency is passed as 16.16 through the pipe. - */ -static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned char *cp = urb->transfer_buffer; - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 4; - urb->iso_frame_desc[0].offset = 0; - cp[0] = subs->freqn; - cp[1] = subs->freqn >> 8; - cp[2] = subs->freqn >> 16; - cp[3] = subs->freqn >> 24; - return 0; -} - -/* - * process after capture sync complete - * - nothing to do - */ -static int retire_capture_sync_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - return 0; -} - -/* - * prepare urb for capture data pipe - * - * fill the offset and length of each descriptor. - * - * we use a temporary buffer to write the captured data. - * since the length of written data is determined by host, we cannot - * write onto the pcm buffer directly... the data is thus copied - * later at complete callback to the global buffer. - */ -static int prepare_capture_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - int i, offs; - struct snd_urb_ctx *ctx = urb->context; - - offs = 0; - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - for (i = 0; i < ctx->packets; i++) { - urb->iso_frame_desc[i].offset = offs; - urb->iso_frame_desc[i].length = subs->curpacksize; - offs += subs->curpacksize; - } - urb->transfer_buffer_length = offs; - urb->number_of_packets = ctx->packets; - return 0; -} - -/* - * process after capture complete - * - * copy the data from each desctiptor to the pcm buffer, and - * update the current position. - */ -static int retire_capture_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned long flags; - unsigned char *cp; - int i; - unsigned int stride, frames, bytes, oldptr; - int period_elapsed = 0; - - stride = runtime->frame_bits >> 3; - - for (i = 0; i < urb->number_of_packets; i++) { - cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (urb->iso_frame_desc[i].status) { - snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status); - // continue; - } - bytes = urb->iso_frame_desc[i].actual_length; - frames = bytes / stride; - if (!subs->txfr_quirk) - bytes = frames * stride; - if (bytes % (runtime->sample_bits >> 3) != 0) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - int oldbytes = bytes; -#endif - bytes = frames * stride; - snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", - oldbytes, bytes); - } - /* update the current pointer */ - spin_lock_irqsave(&subs->lock, flags); - oldptr = subs->hwptr_done; - subs->hwptr_done += bytes; - if (subs->hwptr_done >= runtime->buffer_size * stride) - subs->hwptr_done -= runtime->buffer_size * stride; - frames = (bytes + (oldptr % stride)) / stride; - subs->transfer_done += frames; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - period_elapsed = 1; - } - spin_unlock_irqrestore(&subs->lock, flags); - /* copy a data chunk */ - if (oldptr + bytes > runtime->buffer_size * stride) { - unsigned int bytes1 = - runtime->buffer_size * stride - oldptr; - memcpy(runtime->dma_area + oldptr, cp, bytes1); - memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1); - } else { - memcpy(runtime->dma_area + oldptr, cp, bytes); - } - } - if (period_elapsed) - snd_pcm_period_elapsed(subs->pcm_substream); - return 0; -} - -/* - * Process after capture complete when paused. Nothing to do. - */ -static int retire_paused_capture_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - return 0; -} - - -/* - * prepare urb for full speed playback sync pipe - * - * set up the offset and length to receive the current frequency. - */ - -static int prepare_playback_sync_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 3; - urb->iso_frame_desc[0].offset = 0; - return 0; -} - -/* - * prepare urb for high speed playback sync pipe - * - * set up the offset and length to receive the current frequency. - */ - -static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 4; - urb->iso_frame_desc[0].offset = 0; - return 0; -} - -/* - * process after full speed playback sync complete - * - * retrieve the current 10.14 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). - */ -static int retire_playback_sync_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 3) { - f = combine_triple((u8*)urb->transfer_buffer) << 2; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } - } - - return 0; -} - -/* - * process after high speed playback sync complete - * - * retrieve the current 12.13 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). - */ -static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } - } - - return 0; -} - -/* - * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete - * - * These devices return the number of samples per packet instead of the number - * of samples per microframe. - */ -static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - f >>= subs->datainterval; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } - } - - return 0; -} - -/* determine the number of frames in the next packet */ -static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) -{ - if (subs->fill_max) - return subs->maxframesize; - else { - subs->phase = (subs->phase & 0xffff) - + (subs->freqm << subs->datainterval); - return min(subs->phase >> 16, subs->maxframesize); - } -} - -/* - * Prepare urb for streaming before playback starts or when paused. - * - * We don't have any data, so we send silence. - */ -static int prepare_nodata_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int i, offs, counts; - struct snd_urb_ctx *ctx = urb->context; - int stride = runtime->frame_bits >> 3; - - offs = 0; - urb->dev = ctx->subs->dev; - for (i = 0; i < ctx->packets; ++i) { - counts = snd_usb_audio_next_packet_size(subs); - urb->iso_frame_desc[i].offset = offs * stride; - urb->iso_frame_desc[i].length = counts * stride; - offs += counts; - } - urb->number_of_packets = ctx->packets; - urb->transfer_buffer_length = offs * stride; - memset(urb->transfer_buffer, - subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0, - offs * stride); - return 0; -} - -/* - * prepare urb for playback data pipe - * - * Since a URB can handle only a single linear buffer, we must use double - * buffering when the data to be transferred overflows the buffer boundary. - * To avoid inconsistencies when updating hwptr_done, we use double buffering - * for all URBs. - */ -static int prepare_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - int i, stride; - unsigned int counts, frames, bytes; - unsigned long flags; - int period_elapsed = 0; - struct snd_urb_ctx *ctx = urb->context; - - stride = runtime->frame_bits >> 3; - - frames = 0; - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->number_of_packets = 0; - spin_lock_irqsave(&subs->lock, flags); - for (i = 0; i < ctx->packets; i++) { - counts = snd_usb_audio_next_packet_size(subs); - /* set up descriptor */ - urb->iso_frame_desc[i].offset = frames * stride; - urb->iso_frame_desc[i].length = counts * stride; - frames += counts; - urb->number_of_packets++; - subs->transfer_done += counts; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - period_elapsed = 1; - if (subs->fmt_type == UAC_FORMAT_TYPE_II) { - if (subs->transfer_done > 0) { - /* FIXME: fill-max mode is not - * supported yet */ - frames -= subs->transfer_done; - counts -= subs->transfer_done; - urb->iso_frame_desc[i].length = - counts * stride; - subs->transfer_done = 0; - } - i++; - if (i < ctx->packets) { - /* add a transfer delimiter */ - urb->iso_frame_desc[i].offset = - frames * stride; - urb->iso_frame_desc[i].length = 0; - urb->number_of_packets++; - } - break; - } - } - if (period_elapsed) /* finish at the period boundary */ - break; - } - bytes = frames * stride; - if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { - /* err, the transferred area goes over buffer boundary. */ - unsigned int bytes1 = - runtime->buffer_size * stride - subs->hwptr_done; - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes1); - memcpy(urb->transfer_buffer + bytes1, - runtime->dma_area, bytes - bytes1); - } else { - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes); - } - subs->hwptr_done += bytes; - if (subs->hwptr_done >= runtime->buffer_size * stride) - subs->hwptr_done -= runtime->buffer_size * stride; - runtime->delay += frames; - spin_unlock_irqrestore(&subs->lock, flags); - urb->transfer_buffer_length = bytes; - if (period_elapsed) - snd_pcm_period_elapsed(subs->pcm_substream); - return 0; -} - -/* - * process after playback data complete - * - decrease the delay count again - */ -static int retire_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned long flags; - int stride = runtime->frame_bits >> 3; - int processed = urb->transfer_buffer_length / stride; - - spin_lock_irqsave(&subs->lock, flags); - if (processed > runtime->delay) - runtime->delay = 0; - else - runtime->delay -= processed; - spin_unlock_irqrestore(&subs->lock, flags); - return 0; -} - - -/* - */ -static struct snd_urb_ops audio_urb_ops[2] = { - { - .prepare = prepare_nodata_playback_urb, - .retire = retire_playback_urb, - .prepare_sync = prepare_playback_sync_urb, - .retire_sync = retire_playback_sync_urb, - }, - { - .prepare = prepare_capture_urb, - .retire = retire_capture_urb, - .prepare_sync = prepare_capture_sync_urb, - .retire_sync = retire_capture_sync_urb, - }, -}; - -static struct snd_urb_ops audio_urb_ops_high_speed[2] = { - { - .prepare = prepare_nodata_playback_urb, - .retire = retire_playback_urb, - .prepare_sync = prepare_playback_sync_urb_hs, - .retire_sync = retire_playback_sync_urb_hs, - }, - { - .prepare = prepare_capture_urb, - .retire = retire_capture_urb, - .prepare_sync = prepare_capture_sync_urb_hs, - .retire_sync = retire_capture_sync_urb, - }, -}; - -/* - * complete callback from data urb - */ -static void snd_complete_urb(struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - struct snd_usb_substream *subs = ctx->subs; - struct snd_pcm_substream *substream = ctx->subs->pcm_substream; - int err = 0; - - if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || - !subs->running || /* can be stopped during retire callback */ - (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || - (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - clear_bit(ctx->index, &subs->active_mask); - if (err < 0) { - snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - } - } -} - - -/* - * complete callback from sync urb - */ -static void snd_complete_sync_urb(struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - struct snd_usb_substream *subs = ctx->subs; - struct snd_pcm_substream *substream = ctx->subs->pcm_substream; - int err = 0; - - if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) || - !subs->running || /* can be stopped during retire callback */ - (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || - (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - clear_bit(ctx->index + 16, &subs->active_mask); - if (err < 0) { - snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - } - } -} - - -/* - * unlink active urbs. - */ -static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep) -{ - unsigned int i; - int async; - - subs->running = 0; - - if (!force && subs->stream->chip->shutdown) /* to be sure... */ - return -EBADFD; - - async = !can_sleep && async_unlink; - - if (!async && in_interrupt()) - return 0; - - for (i = 0; i < subs->nurbs; i++) { - if (test_bit(i, &subs->active_mask)) { - if (!test_and_set_bit(i, &subs->unlink_mask)) { - struct urb *u = subs->dataurb[i].urb; - if (async) - usb_unlink_urb(u); - else - usb_kill_urb(u); - } - } - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (test_bit(i+16, &subs->active_mask)) { - if (!test_and_set_bit(i+16, &subs->unlink_mask)) { - struct urb *u = subs->syncurb[i].urb; - if (async) - usb_unlink_urb(u); - else - usb_kill_urb(u); - } - } - } - } - return 0; -} - - -static const char *usb_error_string(int err) -{ - switch (err) { - case -ENODEV: - return "no device"; - case -ENOENT: - return "endpoint not enabled"; - case -EPIPE: - return "endpoint stalled"; - case -ENOSPC: - return "not enough bandwidth"; - case -ESHUTDOWN: - return "device disabled"; - case -EHOSTUNREACH: - return "device suspended"; - case -EINVAL: - case -EAGAIN: - case -EFBIG: - case -EMSGSIZE: - return "internal error"; - default: - return "unknown error"; - } -} - -/* - * set up and start data/sync urbs - */ -static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime) -{ - unsigned int i; - int err; - - if (subs->stream->chip->shutdown) - return -EBADFD; - - for (i = 0; i < subs->nurbs; i++) { - if (snd_BUG_ON(!subs->dataurb[i].urb)) - return -EINVAL; - if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) { - snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i); - goto __error; - } - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (snd_BUG_ON(!subs->syncurb[i].urb)) - return -EINVAL; - if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) { - snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i); - goto __error; - } - } - } - - subs->active_mask = 0; - subs->unlink_mask = 0; - subs->running = 1; - for (i = 0; i < subs->nurbs; i++) { - err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "cannot submit datapipe " - "for urb %d, error %d: %s\n", - i, err, usb_error_string(err)); - goto __error; - } - set_bit(i, &subs->active_mask); - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "cannot submit syncpipe " - "for urb %d, error %d: %s\n", - i, err, usb_error_string(err)); - goto __error; - } - set_bit(i + 16, &subs->active_mask); - } - } - return 0; - - __error: - // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - deactivate_urbs(subs, 0, 0); - return -EPIPE; -} - - -/* - * wait until all urbs are processed. - */ -static int wait_clear_urbs(struct snd_usb_substream *subs) -{ - unsigned long end_time = jiffies + msecs_to_jiffies(1000); - unsigned int i; - int alive; - - do { - alive = 0; - for (i = 0; i < subs->nurbs; i++) { - if (test_bit(i, &subs->active_mask)) - alive++; - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (test_bit(i + 16, &subs->active_mask)) - alive++; - } - } - if (! alive) - break; - schedule_timeout_uninterruptible(1); - } while (time_before(jiffies, end_time)); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - return 0; -} - - -/* - * return the current pcm pointer. just based on the hwptr_done value. - */ -static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_usb_substream *subs; - unsigned int hwptr_done; - - subs = (struct snd_usb_substream *)substream->runtime->private_data; - spin_lock(&subs->lock); - hwptr_done = subs->hwptr_done; - spin_unlock(&subs->lock); - return hwptr_done / (substream->runtime->frame_bits >> 3); -} - - -/* - * start/stop playback substream - */ -static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct snd_usb_substream *subs = substream->runtime->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - subs->ops.prepare = prepare_playback_urb; - return 0; - case SNDRV_PCM_TRIGGER_STOP: - return deactivate_urbs(subs, 0, 0); - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - subs->ops.prepare = prepare_nodata_playback_urb; - return 0; - default: - return -EINVAL; - } -} - -/* - * start/stop capture substream - */ -static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct snd_usb_substream *subs = substream->runtime->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - subs->ops.retire = retire_capture_urb; - return start_urbs(subs, substream->runtime); - case SNDRV_PCM_TRIGGER_STOP: - return deactivate_urbs(subs, 0, 0); - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - subs->ops.retire = retire_paused_capture_urb; - return 0; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - subs->ops.retire = retire_capture_urb; - return 0; - default: - return -EINVAL; - } -} - - -/* - * release a urb data - */ -static void release_urb_ctx(struct snd_urb_ctx *u) -{ - if (u->urb) { - if (u->buffer_size) - usb_buffer_free(u->subs->dev, u->buffer_size, - u->urb->transfer_buffer, - u->urb->transfer_dma); - usb_free_urb(u->urb); - u->urb = NULL; - } -} - -/* - * release a substream - */ -static void release_substream_urbs(struct snd_usb_substream *subs, int force) -{ - int i; - - /* stop urbs (to be sure) */ - deactivate_urbs(subs, force, 1); - wait_clear_urbs(subs); - - for (i = 0; i < MAX_URBS; i++) - release_urb_ctx(&subs->dataurb[i]); - for (i = 0; i < SYNC_URBS; i++) - release_urb_ctx(&subs->syncurb[i]); - usb_buffer_free(subs->dev, SYNC_URBS * 4, - subs->syncbuf, subs->sync_dma); - subs->syncbuf = NULL; - subs->nurbs = 0; -} - -/* - * initialize a substream for plaback/capture - */ -static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes, - unsigned int rate, unsigned int frame_bits) -{ - unsigned int maxsize, i; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int urb_packs, total_packs, packs_per_ms; - - /* calculate the frequency in 16.16 format */ - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) - subs->freqn = get_usb_full_speed_rate(rate); - else - subs->freqn = get_usb_high_speed_rate(rate); - subs->freqm = subs->freqn; - /* calculate max. frequency */ - if (subs->maxpacksize) { - /* whatever fits into a max. size packet */ - maxsize = subs->maxpacksize; - subs->freqmax = (maxsize / (frame_bits >> 3)) - << (16 - subs->datainterval); - } else { - /* no max. packet size: just take 25% higher than nominal */ - subs->freqmax = subs->freqn + (subs->freqn >> 2); - maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) - >> (16 - subs->datainterval); - } - subs->phase = 0; - - if (subs->fill_max) - subs->curpacksize = subs->maxpacksize; - else - subs->curpacksize = maxsize; - - if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) - packs_per_ms = 8 >> subs->datainterval; - else - packs_per_ms = 1; - - if (is_playback) { - urb_packs = max(nrpacks, 1); - urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); - } else - urb_packs = 1; - urb_packs *= packs_per_ms; - if (subs->syncpipe) - urb_packs = min(urb_packs, 1U << subs->syncinterval); - - /* decide how many packets to be used */ - if (is_playback) { - unsigned int minsize, maxpacks; - /* determine how small a packet can be */ - minsize = (subs->freqn >> (16 - subs->datainterval)) - * (frame_bits >> 3); - /* with sync from device, assume it can be 12% lower */ - if (subs->syncpipe) - minsize -= minsize >> 3; - minsize = max(minsize, 1u); - total_packs = (period_bytes + minsize - 1) / minsize; - /* we need at least two URBs for queueing */ - if (total_packs < 2) { - total_packs = 2; - } else { - /* and we don't want too long a queue either */ - maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); - total_packs = min(total_packs, maxpacks); - } - } else { - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - total_packs = MAX_URBS * urb_packs; - } - subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; - if (subs->nurbs > MAX_URBS) { - /* too much... */ - subs->nurbs = MAX_URBS; - total_packs = MAX_URBS * urb_packs; - } else if (subs->nurbs < 2) { - /* too little - we need at least two packets - * to ensure contiguous playback/capture - */ - subs->nurbs = 2; - } - - /* allocate and initialize data urbs */ - for (i = 0; i < subs->nurbs; i++) { - struct snd_urb_ctx *u = &subs->dataurb[i]; - u->index = i; - u->subs = subs; - u->packets = (i + 1) * total_packs / subs->nurbs - - i * total_packs / subs->nurbs; - u->buffer_size = maxsize * u->packets; - if (subs->fmt_type == UAC_FORMAT_TYPE_II) - u->packets++; /* for transfer delimiter */ - u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); - if (!u->urb) - goto out_of_memory; - u->urb->transfer_buffer = - usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL, - &u->urb->transfer_dma); - if (!u->urb->transfer_buffer) - goto out_of_memory; - u->urb->pipe = subs->datapipe; - u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - u->urb->interval = 1 << subs->datainterval; - u->urb->context = u; - u->urb->complete = snd_complete_urb; - } - - if (subs->syncpipe) { - /* allocate and initialize sync urbs */ - subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4, - GFP_KERNEL, &subs->sync_dma); - if (!subs->syncbuf) - goto out_of_memory; - for (i = 0; i < SYNC_URBS; i++) { - struct snd_urb_ctx *u = &subs->syncurb[i]; - u->index = i; - u->subs = subs; - u->packets = 1; - u->urb = usb_alloc_urb(1, GFP_KERNEL); - if (!u->urb) - goto out_of_memory; - u->urb->transfer_buffer = subs->syncbuf + i * 4; - u->urb->transfer_dma = subs->sync_dma + i * 4; - u->urb->transfer_buffer_length = 4; - u->urb->pipe = subs->syncpipe; - u->urb->transfer_flags = URB_ISO_ASAP | - URB_NO_TRANSFER_DMA_MAP; - u->urb->number_of_packets = 1; - u->urb->interval = 1 << subs->syncinterval; - u->urb->context = u; - u->urb->complete = snd_complete_sync_urb; - } - } - return 0; - -out_of_memory: - release_substream_urbs(subs, 0); - return -ENOMEM; -} - - -/* - * find a matching audio format - */ -static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format, - unsigned int rate, unsigned int channels) -{ - struct list_head *p; - struct audioformat *found = NULL; - int cur_attr = 0, attr; - - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - if (fp->format != format || fp->channels != channels) - continue; - if (rate < fp->rate_min || rate > fp->rate_max) - continue; - if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { - unsigned int i; - for (i = 0; i < fp->nr_rates; i++) - if (fp->rate_table[i] == rate) - break; - if (i >= fp->nr_rates) - continue; - } - attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; - if (! found) { - found = fp; - cur_attr = attr; - continue; - } - /* avoid async out and adaptive in if the other method - * supports the same format. - * this is a workaround for the case like - * M-audio audiophile USB. - */ - if (attr != cur_attr) { - if ((attr == USB_ENDPOINT_SYNC_ASYNC && - subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || - (attr == USB_ENDPOINT_SYNC_ADAPTIVE && - subs->direction == SNDRV_PCM_STREAM_CAPTURE)) - continue; - if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC && - subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || - (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE && - subs->direction == SNDRV_PCM_STREAM_CAPTURE)) { - found = fp; - cur_attr = attr; - continue; - } - } - /* find the format with the largest max. packet size */ - if (fp->maxpacksize > found->maxpacksize) { - found = fp; - cur_attr = attr; - } - } - return found; -} - - -/* - * initialize the picth control and sample rate - */ -static int init_usb_pitch(struct usb_device *dev, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt) -{ - unsigned int ep; - unsigned char data[1]; - int err; - - ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) { - data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", - dev->devnum, iface, ep); - return err; - } - } - return 0; -} - -static int init_usb_sample_rate(struct usb_device *dev, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate) -{ - unsigned int ep; - unsigned char data[3]; - int err; - - ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) { - int crate; - data[0] = rate; - data[1] = rate >> 8; - data[2] = rate >> 16; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", - dev->devnum, iface, fmt->altsetting, rate, ep); - return err; - } - if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { - snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", - dev->devnum, iface, fmt->altsetting, ep); - return 0; /* some devices don't support reading */ - } - crate = data[0] | (data[1] << 8) | (data[2] << 16); - if (crate != rate) { - snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); - // runtime->rate = crate; - } - } - return 0; -} - -/* - * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device, - * not for interface. - */ -static void set_format_emu_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt) -{ - unsigned char emu_samplerate_id = 0; - - /* When capture is active - * sample rate shouldn't be changed - * by playback substream - */ - if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { - if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1) - return; - } - - switch (fmt->rate_min) { - case 48000: - emu_samplerate_id = EMU_QUIRK_SR_48000HZ; - break; - case 88200: - emu_samplerate_id = EMU_QUIRK_SR_88200HZ; - break; - case 96000: - emu_samplerate_id = EMU_QUIRK_SR_96000HZ; - break; - case 176400: - emu_samplerate_id = EMU_QUIRK_SR_176400HZ; - break; - case 192000: - emu_samplerate_id = EMU_QUIRK_SR_192000HZ; - break; - default: - emu_samplerate_id = EMU_QUIRK_SR_44100HZ; - break; - } - snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id); -} - -/* - * find a matching format and set up the interface - */ -static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) -{ - struct usb_device *dev = subs->dev; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_interface *iface; - unsigned int ep, attr; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - int err; - - iface = usb_ifnum_to_if(dev, fmt->iface); - if (WARN_ON(!iface)) - return -EINVAL; - alts = &iface->altsetting[fmt->altset_idx]; - altsd = get_iface_desc(alts); - if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) - return -EINVAL; - - if (fmt == subs->cur_audiofmt) - return 0; - - /* close the old interface */ - if (subs->interface >= 0 && subs->interface != fmt->iface) { - if (usb_set_interface(subs->dev, subs->interface, 0) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n", - dev->devnum, fmt->iface, fmt->altsetting); - return -EIO; - } - subs->interface = -1; - subs->format = 0; - } - - /* set interface */ - if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { - if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", - dev->devnum, fmt->iface, fmt->altsetting); - return -EIO; - } - snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); - subs->interface = fmt->iface; - subs->format = fmt->altset_idx; - } - - /* create a data pipe */ - ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK; - if (is_playback) - subs->datapipe = usb_sndisocpipe(dev, ep); - else - subs->datapipe = usb_rcvisocpipe(dev, ep); - subs->datainterval = fmt->datainterval; - subs->syncpipe = subs->syncinterval = 0; - subs->maxpacksize = fmt->maxpacksize; - subs->fill_max = 0; - - /* we need a sync pipe in async OUT or adaptive IN mode */ - /* check the number of EP, since some devices have broken - * descriptors which fool us. if it has only one EP, - * assume it as adaptive-out or sync-in. - */ - attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || - (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && - altsd->bNumEndpoints >= 2) { - /* check sync-pipe endpoint */ - /* ... and check descriptor size before accessing bSynchAddress - because there is a version of the SB Audigy 2 NX firmware lacking - the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || - (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bSynchAddress != 0)) { - snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", - dev->devnum, fmt->iface, fmt->altsetting); - return -EINVAL; - } - ep = get_endpoint(alts, 1)->bEndpointAddress; - if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || - (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { - snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", - dev->devnum, fmt->iface, fmt->altsetting); - return -EINVAL; - } - ep &= USB_ENDPOINT_NUMBER_MASK; - if (is_playback) - subs->syncpipe = usb_rcvisocpipe(dev, ep); - else - subs->syncpipe = usb_sndisocpipe(dev, ep); - if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bRefresh >= 1 && - get_endpoint(alts, 1)->bRefresh <= 9) - subs->syncinterval = get_endpoint(alts, 1)->bRefresh; - else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) - subs->syncinterval = 1; - else if (get_endpoint(alts, 1)->bInterval >= 1 && - get_endpoint(alts, 1)->bInterval <= 16) - subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1; - else - subs->syncinterval = 3; - } - - /* always fill max packet size */ - if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) - subs->fill_max = 1; - - if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0) - return err; - - subs->cur_audiofmt = fmt; - - switch (subs->stream->chip->usb_id) { - case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ - case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ - case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ - set_format_emu_quirk(subs, fmt); - break; - } - -#if 0 - printk(KERN_DEBUG - "setting done: format = %d, rate = %d..%d, channels = %d\n", - fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels); - printk(KERN_DEBUG - " datapipe = 0x%0x, syncpipe = 0x%0x\n", - subs->datapipe, subs->syncpipe); -#endif - - return 0; -} - -/* - * hw_params callback - * - * allocate a buffer and set the given audio format. - * - * so far we use a physically linear buffer although packetize transfer - * doesn't need a continuous area. - * if sg buffer is supported on the later version of alsa, we'll follow - * that. - */ -static int snd_usb_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_usb_substream *subs = substream->runtime->private_data; - struct audioformat *fmt; - unsigned int channels, rate, format; - int ret, changed; - - ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); - fmt = find_format(subs, format, rate, channels); - if (!fmt) { - snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n", - format, rate, channels); - return -EINVAL; - } - - changed = subs->cur_audiofmt != fmt || - subs->period_bytes != params_period_bytes(hw_params) || - subs->cur_rate != rate; - if ((ret = set_format(subs, fmt)) < 0) - return ret; - - if (subs->cur_rate != rate) { - struct usb_host_interface *alts; - struct usb_interface *iface; - iface = usb_ifnum_to_if(subs->dev, fmt->iface); - alts = &iface->altsetting[fmt->altset_idx]; - ret = init_usb_sample_rate(subs->dev, subs->interface, alts, fmt, rate); - if (ret < 0) - return ret; - subs->cur_rate = rate; - } - - if (changed) { - /* format changed */ - release_substream_urbs(subs, 0); - /* influenced: period_bytes, channels, rate, format, */ - ret = init_substream_urbs(subs, params_period_bytes(hw_params), - params_rate(hw_params), - snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params)); - } - - return ret; -} - -/* - * hw_free callback - * - * reset the audio format and release the buffer - */ -static int snd_usb_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_usb_substream *subs = substream->runtime->private_data; - - subs->cur_audiofmt = NULL; - subs->cur_rate = 0; - subs->period_bytes = 0; - if (!subs->stream->chip->shutdown) - release_substream_urbs(subs, 0); - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -/* - * prepare callback - * - * only a few subtle things... - */ -static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_usb_substream *subs = runtime->private_data; - - if (! subs->cur_audiofmt) { - snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); - return -ENXIO; - } - - /* some unit conversions in runtime */ - subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); - subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); - - /* reset the pointer */ - subs->hwptr_done = 0; - subs->transfer_done = 0; - subs->phase = 0; - runtime->delay = 0; - - /* clear urbs (to be sure) */ - deactivate_urbs(subs, 0, 1); - wait_clear_urbs(subs); - - /* for playback, submit the URBs now; otherwise, the first hwptr_done - * updates for all URBs would happen at the same time when starting */ - if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { - subs->ops.prepare = prepare_nodata_playback_urb; - return start_urbs(subs, runtime); - } else - return 0; -} - -static struct snd_pcm_hardware snd_usb_hardware = -{ - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_PAUSE, - .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 64, - .period_bytes_max = 512 * 1024, - .periods_min = 2, - .periods_max = 1024, -}; - -/* - * h/w constraints - */ - -#ifdef HW_CONST_DEBUG -#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) -#else -#define hwc_debug(fmt, args...) /**/ -#endif - -static int hw_check_valid_format(struct snd_usb_substream *subs, - struct snd_pcm_hw_params *params, - struct audioformat *fp) -{ - struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); - unsigned int ptime; - - /* check the format */ - if (!snd_mask_test(fmts, fp->format)) { - hwc_debug(" > check: no supported format %d\n", fp->format); - return 0; - } - /* check the channels */ - if (fp->channels < ct->min || fp->channels > ct->max) { - hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max); - return 0; - } - /* check the rate is within the range */ - if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) { - hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max); - return 0; - } - if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) { - hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); - return 0; - } - /* check whether the period time is >= the data packet interval */ - if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) { - ptime = 125 * (1 << fp->datainterval); - if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { - hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); - return 0; - } - } - return 1; -} - -static int hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_usb_substream *subs = rule->private; - struct list_head *p; - struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - unsigned int rmin, rmax; - int changed; - - hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); - changed = 0; - rmin = rmax = 0; - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(subs, params, fp)) - continue; - if (changed++) { - if (rmin > fp->rate_min) - rmin = fp->rate_min; - if (rmax < fp->rate_max) - rmax = fp->rate_max; - } else { - rmin = fp->rate_min; - rmax = fp->rate_max; - } - } - - if (!changed) { - hwc_debug(" --> get empty\n"); - it->empty = 1; - return -EINVAL; - } - - changed = 0; - if (it->min < rmin) { - it->min = rmin; - it->openmin = 0; - changed = 1; - } - if (it->max > rmax) { - it->max = rmax; - it->openmax = 0; - changed = 1; - } - if (snd_interval_checkempty(it)) { - it->empty = 1; - return -EINVAL; - } - hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); - return changed; -} - - -static int hw_rule_channels(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_usb_substream *subs = rule->private; - struct list_head *p; - struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - unsigned int rmin, rmax; - int changed; - - hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); - changed = 0; - rmin = rmax = 0; - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(subs, params, fp)) - continue; - if (changed++) { - if (rmin > fp->channels) - rmin = fp->channels; - if (rmax < fp->channels) - rmax = fp->channels; - } else { - rmin = fp->channels; - rmax = fp->channels; - } - } - - if (!changed) { - hwc_debug(" --> get empty\n"); - it->empty = 1; - return -EINVAL; - } - - changed = 0; - if (it->min < rmin) { - it->min = rmin; - it->openmin = 0; - changed = 1; - } - if (it->max > rmax) { - it->max = rmax; - it->openmax = 0; - changed = 1; - } - if (snd_interval_checkempty(it)) { - it->empty = 1; - return -EINVAL; - } - hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); - return changed; -} - -static int hw_rule_format(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_usb_substream *subs = rule->private; - struct list_head *p; - struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - u64 fbits; - u32 oldbits[2]; - int changed; - - hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); - fbits = 0; - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(subs, params, fp)) - continue; - fbits |= (1ULL << fp->format); - } - - oldbits[0] = fmt->bits[0]; - oldbits[1] = fmt->bits[1]; - fmt->bits[0] &= (u32)fbits; - fmt->bits[1] &= (u32)(fbits >> 32); - if (!fmt->bits[0] && !fmt->bits[1]) { - hwc_debug(" --> get empty\n"); - return -EINVAL; - } - changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); - hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); - return changed; -} - -static int hw_rule_period_time(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_usb_substream *subs = rule->private; - struct audioformat *fp; - struct snd_interval *it; - unsigned char min_datainterval; - unsigned int pmin; - int changed; - - it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); - hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max); - min_datainterval = 0xff; - list_for_each_entry(fp, &subs->fmt_list, list) { - if (!hw_check_valid_format(subs, params, fp)) - continue; - min_datainterval = min(min_datainterval, fp->datainterval); - } - if (min_datainterval == 0xff) { - hwc_debug(" --> get emtpy\n"); - it->empty = 1; - return -EINVAL; - } - pmin = 125 * (1 << min_datainterval); - changed = 0; - if (it->min < pmin) { - it->min = pmin; - it->openmin = 0; - changed = 1; - } - if (snd_interval_checkempty(it)) { - it->empty = 1; - return -EINVAL; - } - hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed); - return changed; -} - -/* - * If the device supports unusual bit rates, does the request meet these? - */ -static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, - struct snd_usb_substream *subs) -{ - struct audioformat *fp; - int count = 0, needs_knot = 0; - int err; - - list_for_each_entry(fp, &subs->fmt_list, list) { - if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) - return 0; - count += fp->nr_rates; - if (fp->rates & SNDRV_PCM_RATE_KNOT) - needs_knot = 1; - } - if (!needs_knot) - return 0; - - subs->rate_list.count = count; - subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); - subs->rate_list.mask = 0; - count = 0; - list_for_each_entry(fp, &subs->fmt_list, list) { - int i; - for (i = 0; i < fp->nr_rates; i++) - subs->rate_list.list[count++] = fp->rate_table[i]; - } - err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &subs->rate_list); - if (err < 0) - return err; - - return 0; -} - - -/* - * set up the runtime hardware information. - */ - -static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) -{ - struct list_head *p; - unsigned int pt, ptmin; - int param_period_time_if_needed; - int err; - - runtime->hw.formats = subs->formats; - - runtime->hw.rate_min = 0x7fffffff; - runtime->hw.rate_max = 0; - runtime->hw.channels_min = 256; - runtime->hw.channels_max = 0; - runtime->hw.rates = 0; - ptmin = UINT_MAX; - /* check min/max rates and channels */ - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - runtime->hw.rates |= fp->rates; - if (runtime->hw.rate_min > fp->rate_min) - runtime->hw.rate_min = fp->rate_min; - if (runtime->hw.rate_max < fp->rate_max) - runtime->hw.rate_max = fp->rate_max; - if (runtime->hw.channels_min > fp->channels) - runtime->hw.channels_min = fp->channels; - if (runtime->hw.channels_max < fp->channels) - runtime->hw.channels_max = fp->channels; - if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) { - /* FIXME: there might be more than one audio formats... */ - runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = - fp->frame_size; - } - pt = 125 * (1 << fp->datainterval); - ptmin = min(ptmin, pt); - } - - param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; - if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH) - /* full speed devices have fixed data packet interval */ - ptmin = 1000; - if (ptmin == 1000) - /* if period time doesn't go below 1 ms, no rules needed */ - param_period_time_if_needed = -1; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - ptmin, UINT_MAX); - - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - hw_rule_rate, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - return err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_RATE, - param_period_time_if_needed, - -1)) < 0) - return err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format, subs, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - return err; - if (param_period_time_if_needed >= 0) { - err = snd_pcm_hw_rule_add(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - hw_rule_period_time, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - SNDRV_PCM_HW_PARAM_RATE, - -1); - if (err < 0) - return err; - } - if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) - return err; - return 0; -} - -static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) -{ - struct snd_usb_stream *as = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_usb_substream *subs = &as->substream[direction]; - - subs->interface = -1; - subs->format = 0; - runtime->hw = snd_usb_hardware; - runtime->private_data = subs; - subs->pcm_substream = substream; - return setup_hw_info(runtime, subs); -} - -static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) -{ - struct snd_usb_stream *as = snd_pcm_substream_chip(substream); - struct snd_usb_substream *subs = &as->substream[direction]; - - if (!as->chip->shutdown && subs->interface >= 0) { - usb_set_interface(subs->dev, subs->interface, 0); - subs->interface = -1; - } - subs->pcm_substream = NULL; - return 0; -} - -static int snd_usb_playback_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_playback_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_capture_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE); -} - -static int snd_usb_capture_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); -} - -static struct snd_pcm_ops snd_usb_playback_ops = { - .open = snd_usb_playback_open, - .close = snd_usb_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, - .prepare = snd_usb_pcm_prepare, - .trigger = snd_usb_pcm_playback_trigger, - .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - -static struct snd_pcm_ops snd_usb_capture_ops = { - .open = snd_usb_capture_open, - .close = snd_usb_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, - .prepare = snd_usb_pcm_prepare, - .trigger = snd_usb_pcm_capture_trigger, - .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - - - -/* - * helper functions - */ - -/* - * combine bytes and get an integer value - */ -unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size) -{ - switch (size) { - case 1: return *bytes; - case 2: return combine_word(bytes); - case 3: return combine_triple(bytes); - case 4: return combine_quad(bytes); - default: return 0; - } -} - -/* - * parse descriptor buffer and return the pointer starting the given - * descriptor type. - */ -void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype) -{ - u8 *p, *end, *next; - - p = descstart; - end = p + desclen; - for (; p < end;) { - if (p[0] < 2) - return NULL; - next = p + p[0]; - if (next > end) - return NULL; - if (p[1] == dtype && (!after || (void *)p > after)) { - return p; - } - p = next; - } - return NULL; -} - -/* - * find a class-specified interface descriptor with the given subtype. - */ -void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype) -{ - unsigned char *p = after; - - while ((p = snd_usb_find_desc(buffer, buflen, p, - USB_DT_CS_INTERFACE)) != NULL) { - if (p[0] >= 3 && p[2] == dsubtype) - return p; - } - return NULL; -} - -/* - * Wrapper for usb_control_msg(). - * Allocates a temp buffer to prevent dmaing from/to the stack. - */ -int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, - __u8 requesttype, __u16 value, __u16 index, void *data, - __u16 size, int timeout) -{ - int err; - void *buf = NULL; - - if (size > 0) { - buf = kmemdup(data, size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - } - err = usb_control_msg(dev, pipe, request, requesttype, - value, index, buf, size, timeout); - if (size > 0) { - memcpy(data, buf, size); - kfree(buf); - } - return err; -} - - -/* - * entry point for linux usb interface - */ - -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void usb_audio_disconnect(struct usb_interface *intf); - -#ifdef CONFIG_PM -static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message); -static int usb_audio_resume(struct usb_interface *intf); -#else -#define usb_audio_suspend NULL -#define usb_audio_resume NULL -#endif - -static struct usb_device_id usb_audio_ids [] = { -#include "usbquirks.h" - { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_audio_ids); - -static struct usb_driver usb_audio_driver = { - .name = "snd-usb-audio", - .probe = usb_audio_probe, - .disconnect = usb_audio_disconnect, - .suspend = usb_audio_suspend, - .resume = usb_audio_resume, - .id_table = usb_audio_ids, -}; - - -#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS) - -/* - * proc interface for list the supported pcm formats - */ -static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) -{ - struct list_head *p; - static char *sync_types[4] = { - "NONE", "ASYNC", "ADAPTIVE", "SYNC" - }; - - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - snd_iprintf(buffer, " Interface %d\n", fp->iface); - snd_iprintf(buffer, " Altset %d\n", fp->altsetting); - snd_iprintf(buffer, " Format: %s\n", - snd_pcm_format_name(fp->format)); - snd_iprintf(buffer, " Channels: %d\n", fp->channels); - snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", - fp->endpoint & USB_ENDPOINT_NUMBER_MASK, - fp->endpoint & USB_DIR_IN ? "IN" : "OUT", - sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]); - if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { - snd_iprintf(buffer, " Rates: %d - %d (continuous)\n", - fp->rate_min, fp->rate_max); - } else { - unsigned int i; - snd_iprintf(buffer, " Rates: "); - for (i = 0; i < fp->nr_rates; i++) { - if (i > 0) - snd_iprintf(buffer, ", "); - snd_iprintf(buffer, "%d", fp->rate_table[i]); - } - snd_iprintf(buffer, "\n"); - } - if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) - snd_iprintf(buffer, " Data packet interval: %d us\n", - 125 * (1 << fp->datainterval)); - // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); - // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); - } -} - -static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) -{ - if (subs->running) { - unsigned int i; - snd_iprintf(buffer, " Status: Running\n"); - snd_iprintf(buffer, " Interface = %d\n", subs->interface); - snd_iprintf(buffer, " Altset = %d\n", subs->format); - snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs); - for (i = 0; i < subs->nurbs; i++) - snd_iprintf(buffer, "%d ", subs->dataurb[i].packets); - snd_iprintf(buffer, "]\n"); - snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize); - snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", - snd_usb_get_speed(subs->dev) == USB_SPEED_FULL - ? get_full_speed_hz(subs->freqm) - : get_high_speed_hz(subs->freqm), - subs->freqm >> 16, subs->freqm & 0xffff); - } else { - snd_iprintf(buffer, " Status: Stop\n"); - } -} - -static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) -{ - struct snd_usb_stream *stream = entry->private_data; - - snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); - - if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { - snd_iprintf(buffer, "\nPlayback:\n"); - proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); - proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); - } - if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) { - snd_iprintf(buffer, "\nCapture:\n"); - proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); - proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); - } -} - -static void proc_pcm_format_add(struct snd_usb_stream *stream) -{ - struct snd_info_entry *entry; - char name[32]; - struct snd_card *card = stream->chip->card; - - sprintf(name, "stream%d", stream->pcm_index); - if (!snd_card_proc_new(card, name, &entry)) - snd_info_set_text_ops(entry, stream, proc_pcm_format_read); -} - -#else - -static inline void proc_pcm_format_add(struct snd_usb_stream *stream) -{ -} - -#endif - -/* - * initialize the substream instance. - */ - -static void init_substream(struct snd_usb_stream *as, int stream, struct audioformat *fp) -{ - struct snd_usb_substream *subs = &as->substream[stream]; - - INIT_LIST_HEAD(&subs->fmt_list); - spin_lock_init(&subs->lock); - - subs->stream = as; - subs->direction = stream; - subs->dev = as->chip->dev; - subs->txfr_quirk = as->chip->txfr_quirk; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) { - subs->ops = audio_urb_ops[stream]; - } else { - subs->ops = audio_urb_ops_high_speed[stream]; - switch (as->chip->usb_id) { - case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ - case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ - case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ - subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; - break; - } - } - snd_pcm_set_ops(as->pcm, stream, - stream == SNDRV_PCM_STREAM_PLAYBACK ? - &snd_usb_playback_ops : &snd_usb_capture_ops); - - list_add_tail(&fp->list, &subs->fmt_list); - subs->formats |= 1ULL << fp->format; - subs->endpoint = fp->endpoint; - subs->num_formats++; - subs->fmt_type = fp->fmt_type; -} - - -/* - * free a substream - */ -static void free_substream(struct snd_usb_substream *subs) -{ - struct list_head *p, *n; - - if (!subs->num_formats) - return; /* not initialized */ - list_for_each_safe(p, n, &subs->fmt_list) { - struct audioformat *fp = list_entry(p, struct audioformat, list); - kfree(fp->rate_table); - kfree(fp); - } - kfree(subs->rate_list.list); -} - - -/* - * free a usb stream instance - */ -static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) -{ - free_substream(&stream->substream[0]); - free_substream(&stream->substream[1]); - list_del(&stream->list); - kfree(stream); -} - -static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) -{ - struct snd_usb_stream *stream = pcm->private_data; - if (stream) { - stream->pcm = NULL; - snd_usb_audio_stream_free(stream); - } -} - - -/* - * add this endpoint to the chip instance. - * if a stream with the same endpoint already exists, append to it. - * if not, create a new pcm stream. - */ -static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp) -{ - struct list_head *p; - struct snd_usb_stream *as; - struct snd_usb_substream *subs; - struct snd_pcm *pcm; - int err; - - list_for_each(p, &chip->pcm_list) { - as = list_entry(p, struct snd_usb_stream, list); - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (!subs->endpoint) - continue; - if (subs->endpoint == fp->endpoint) { - list_add_tail(&fp->list, &subs->fmt_list); - subs->num_formats++; - subs->formats |= 1ULL << fp->format; - return 0; - } - } - /* look for an empty stream */ - list_for_each(p, &chip->pcm_list) { - as = list_entry(p, struct snd_usb_stream, list); - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (subs->endpoint) - continue; - err = snd_pcm_new_stream(as->pcm, stream, 1); - if (err < 0) - return err; - init_substream(as, stream, fp); - return 0; - } - - /* create a new pcm */ - as = kzalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return -ENOMEM; - as->pcm_index = chip->pcm_devs; - as->chip = chip; - as->fmt_type = fp->fmt_type; - err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, - &pcm); - if (err < 0) { - kfree(as); - return err; - } - as->pcm = pcm; - pcm->private_data = as; - pcm->private_free = snd_usb_audio_pcm_free; - pcm->info_flags = 0; - if (chip->pcm_devs > 0) - sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); - else - strcpy(pcm->name, "USB Audio"); - - init_substream(as, stream, fp); - - list_add(&as->list, &chip->pcm_list); - chip->pcm_devs++; - - proc_pcm_format_add(as); - - return 0; -} - - -/* - * check if the device uses big-endian samples - */ -static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp) -{ - switch (chip->usb_id) { - case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */ - if (fp->endpoint & USB_DIR_IN) - return 1; - break; - case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - if (device_setup[chip->index] == 0x00 || - fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3) - return 1; - } - return 0; -} - -/* - * parse the audio format type I descriptor - * and returns the corresponding pcm format - * - * @dev: usb device - * @fp: audioformat record - * @format: the format tag (wFormatTag) - * @fmt: the format type descriptor - */ -static int parse_audio_format_i_type(struct snd_usb_audio *chip, - struct audioformat *fp, - int format, void *_fmt, - int protocol) -{ - int pcm_format, i; - int sample_width, sample_bytes; - - switch (protocol) { - case UAC_VERSION_1: { - struct uac_format_type_i_discrete_descriptor *fmt = _fmt; - sample_width = fmt->bBitResolution; - sample_bytes = fmt->bSubframeSize; - break; - } - - case UAC_VERSION_2: { - struct uac_format_type_i_ext_descriptor *fmt = _fmt; - sample_width = fmt->bBitResolution; - sample_bytes = fmt->bSubslotSize; - - /* - * FIXME - * USB audio class v2 devices specify a bitmap of possible - * audio formats rather than one fix value. For now, we just - * pick one of them and report that as the only possible - * value for this setting. - * The bit allocation map is in fact compatible to the - * wFormatTag of the v1 AS streaming descriptors, which is why - * we can simply map the matrix. - */ - - for (i = 0; i < 5; i++) - if (format & (1UL << i)) { - format = i + 1; - break; - } - - break; - } - - default: - return -EINVAL; - } - - /* FIXME: correct endianess and sign? */ - pcm_format = -1; - - switch (format) { - case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */ - snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", - chip->dev->devnum, fp->iface, fp->altsetting); - /* fall-through */ - case UAC_FORMAT_TYPE_I_PCM: - if (sample_width > sample_bytes * 8) { - snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", - chip->dev->devnum, fp->iface, fp->altsetting, - sample_width, sample_bytes); - } - /* check the format byte size */ - switch (sample_bytes) { - case 1: - pcm_format = SNDRV_PCM_FORMAT_S8; - break; - case 2: - if (is_big_endian_format(chip, fp)) - pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ - else - pcm_format = SNDRV_PCM_FORMAT_S16_LE; - break; - case 3: - if (is_big_endian_format(chip, fp)) - pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ - else - pcm_format = SNDRV_PCM_FORMAT_S24_3LE; - break; - case 4: - pcm_format = SNDRV_PCM_FORMAT_S32_LE; - break; - default: - snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", - chip->dev->devnum, fp->iface, fp->altsetting, - sample_width, sample_bytes); - break; - } - break; - case UAC_FORMAT_TYPE_I_PCM8: - pcm_format = SNDRV_PCM_FORMAT_U8; - - /* Dallas DS4201 workaround: it advertises U8 format, but really - supports S8. */ - if (chip->usb_id == USB_ID(0x04fa, 0x4201)) - pcm_format = SNDRV_PCM_FORMAT_S8; - break; - case UAC_FORMAT_TYPE_I_IEEE_FLOAT: - pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; - break; - case UAC_FORMAT_TYPE_I_ALAW: - pcm_format = SNDRV_PCM_FORMAT_A_LAW; - break; - case UAC_FORMAT_TYPE_I_MULAW: - pcm_format = SNDRV_PCM_FORMAT_MU_LAW; - break; - default: - snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", - chip->dev->devnum, fp->iface, fp->altsetting, format); - break; - } - return pcm_format; -} - - -/* - * parse the format descriptor and stores the possible sample rates - * on the audioformat table (audio class v1). - * - * @dev: usb device - * @fp: audioformat record - * @fmt: the format descriptor - * @offset: the start offset of descriptor pointing the rate type - * (7 for type I and II, 8 for type II) - */ -static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp, - unsigned char *fmt, int offset) -{ - int nr_rates = fmt[offset]; - - if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { - snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", - chip->dev->devnum, fp->iface, fp->altsetting); - return -1; - } - - if (nr_rates) { - /* - * build the rate table and bitmap flags - */ - int r, idx; - - fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); - if (fp->rate_table == NULL) { - snd_printk(KERN_ERR "cannot malloc\n"); - return -1; - } - - fp->nr_rates = 0; - fp->rate_min = fp->rate_max = 0; - for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { - unsigned int rate = combine_triple(&fmt[idx]); - if (!rate) - continue; - /* C-Media CM6501 mislabels its 96 kHz altsetting */ - if (rate == 48000 && nr_rates == 1 && - (chip->usb_id == USB_ID(0x0d8c, 0x0201) || - chip->usb_id == USB_ID(0x0d8c, 0x0102)) && - fp->altsetting == 5 && fp->maxpacksize == 392) - rate = 96000; - /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */ - if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068)) - rate = 8000; - fp->rate_table[fp->nr_rates] = rate; - if (!fp->rate_min || rate < fp->rate_min) - fp->rate_min = rate; - if (!fp->rate_max || rate > fp->rate_max) - fp->rate_max = rate; - fp->rates |= snd_pcm_rate_to_rate_bit(rate); - fp->nr_rates++; - } - if (!fp->nr_rates) { - hwc_debug("All rates were zero. Skipping format!\n"); - return -1; - } - } else { - /* continuous rates */ - fp->rates = SNDRV_PCM_RATE_CONTINUOUS; - fp->rate_min = combine_triple(&fmt[offset + 1]); - fp->rate_max = combine_triple(&fmt[offset + 4]); - } - return 0; -} - -/* - * parse the format descriptor and stores the possible sample rates - * on the audioformat table (audio class v2). - */ -static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, - struct audioformat *fp, - struct usb_host_interface *iface) -{ - struct usb_device *dev = chip->dev; - unsigned char tmp[2], *data; - int i, nr_rates, data_size, ret = 0; - - /* get the number of sample rates first by only fetching 2 bytes */ - ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - 0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000); - - if (ret < 0) { - snd_printk(KERN_ERR "unable to retrieve number of sample rates\n"); - goto err; - } - - nr_rates = (tmp[1] << 8) | tmp[0]; - data_size = 2 + 12 * nr_rates; - data = kzalloc(data_size, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err; - } - - /* now get the full information */ - ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - 0x0100, chip->clock_id << 8, data, data_size, 1000); - - if (ret < 0) { - snd_printk(KERN_ERR "unable to retrieve sample rate range\n"); - ret = -EINVAL; - goto err_free; - } - - fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); - if (!fp->rate_table) { - ret = -ENOMEM; - goto err_free; - } - - fp->nr_rates = 0; - fp->rate_min = fp->rate_max = 0; - - for (i = 0; i < nr_rates; i++) { - int rate = combine_quad(&data[2 + 12 * i]); - - fp->rate_table[fp->nr_rates] = rate; - if (!fp->rate_min || rate < fp->rate_min) - fp->rate_min = rate; - if (!fp->rate_max || rate > fp->rate_max) - fp->rate_max = rate; - fp->rates |= snd_pcm_rate_to_rate_bit(rate); - fp->nr_rates++; - } - -err_free: - kfree(data); -err: - return ret; -} - -/* - * parse the format type I and III descriptors - */ -static int parse_audio_format_i(struct snd_usb_audio *chip, - struct audioformat *fp, - int format, void *_fmt, - struct usb_host_interface *iface) -{ - struct usb_interface_descriptor *altsd = get_iface_desc(iface); - struct uac_format_type_i_discrete_descriptor *fmt = _fmt; - int protocol = altsd->bInterfaceProtocol; - int pcm_format, ret; - - if (fmt->bFormatType == UAC_FORMAT_TYPE_III) { - /* FIXME: the format type is really IECxxx - * but we give normal PCM format to get the existing - * apps working... - */ - switch (chip->usb_id) { - - case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - if (device_setup[chip->index] == 0x00 && - fp->altsetting == 6) - pcm_format = SNDRV_PCM_FORMAT_S16_BE; - else - pcm_format = SNDRV_PCM_FORMAT_S16_LE; - break; - default: - pcm_format = SNDRV_PCM_FORMAT_S16_LE; - } - } else { - pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol); - if (pcm_format < 0) - return -1; - } - - fp->format = pcm_format; - - /* gather possible sample rates */ - /* audio class v1 reports possible sample rates as part of the - * proprietary class specific descriptor. - * audio class v2 uses class specific EP0 range requests for that. - */ - switch (protocol) { - case UAC_VERSION_1: - fp->channels = fmt->bNrChannels; - ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7); - break; - case UAC_VERSION_2: - /* fp->channels is already set in this case */ - ret = parse_audio_format_rates_v2(chip, fp, iface); - break; - } - - if (fp->channels < 1) { - snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", - chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); - return -1; - } - - return ret; -} - -/* - * parse the format type II descriptor - */ -static int parse_audio_format_ii(struct snd_usb_audio *chip, - struct audioformat *fp, - int format, void *_fmt, - struct usb_host_interface *iface) -{ - int brate, framesize, ret; - struct usb_interface_descriptor *altsd = get_iface_desc(iface); - int protocol = altsd->bInterfaceProtocol; - - switch (format) { - case UAC_FORMAT_TYPE_II_AC3: - /* FIXME: there is no AC3 format defined yet */ - // fp->format = SNDRV_PCM_FORMAT_AC3; - fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */ - break; - case UAC_FORMAT_TYPE_II_MPEG: - fp->format = SNDRV_PCM_FORMAT_MPEG; - break; - default: - snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected. processed as MPEG.\n", - chip->dev->devnum, fp->iface, fp->altsetting, format); - fp->format = SNDRV_PCM_FORMAT_MPEG; - break; - } - - fp->channels = 1; - - switch (protocol) { - case UAC_VERSION_1: { - struct uac_format_type_ii_discrete_descriptor *fmt = _fmt; - brate = le16_to_cpu(fmt->wMaxBitRate); - framesize = le16_to_cpu(fmt->wSamplesPerFrame); - snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); - fp->frame_size = framesize; - ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */ - break; - } - case UAC_VERSION_2: { - struct uac_format_type_ii_ext_descriptor *fmt = _fmt; - brate = le16_to_cpu(fmt->wMaxBitRate); - framesize = le16_to_cpu(fmt->wSamplesPerFrame); - snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); - fp->frame_size = framesize; - ret = parse_audio_format_rates_v2(chip, fp, iface); - break; - } - } - - return ret; -} - -static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, - int format, unsigned char *fmt, int stream, - struct usb_host_interface *iface) -{ - int err; - - switch (fmt[3]) { - case UAC_FORMAT_TYPE_I: - case UAC_FORMAT_TYPE_III: - err = parse_audio_format_i(chip, fp, format, fmt, iface); - break; - case UAC_FORMAT_TYPE_II: - err = parse_audio_format_ii(chip, fp, format, fmt, iface); - break; - default: - snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", - chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]); - return -1; - } - fp->fmt_type = fmt[3]; - if (err < 0) - return err; -#if 1 - /* FIXME: temporary hack for extigy/audigy 2 nx/zs */ - /* extigy apparently supports sample rates other than 48k - * but not in ordinary way. so we enable only 48k atm. - */ - if (chip->usb_id == USB_ID(0x041e, 0x3000) || - chip->usb_id == USB_ID(0x041e, 0x3020) || - chip->usb_id == USB_ID(0x041e, 0x3061)) { - if (fmt[3] == UAC_FORMAT_TYPE_I && - fp->rates != SNDRV_PCM_RATE_48000 && - fp->rates != SNDRV_PCM_RATE_96000) - return -1; - } -#endif - return 0; -} - -static unsigned char parse_datainterval(struct snd_usb_audio *chip, - struct usb_host_interface *alts) -{ - if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH && - get_endpoint(alts, 0)->bInterval >= 1 && - get_endpoint(alts, 0)->bInterval <= 4) - return get_endpoint(alts, 0)->bInterval - 1; - else - return 0; -} - -static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, - int iface, int altno); -static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) -{ - struct usb_device *dev; - struct usb_interface *iface; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - int i, altno, err, stream; - int format = 0, num_channels = 0; - struct audioformat *fp = NULL; - unsigned char *fmt, *csep; - int num, protocol; - - dev = chip->dev; - - /* parse the interface's altsettings */ - iface = usb_ifnum_to_if(dev, iface_no); - - num = iface->num_altsetting; - - /* - * Dallas DS4201 workaround: It presents 5 altsettings, but the last - * one misses syncpipe, and does not produce any sound. - */ - if (chip->usb_id == USB_ID(0x04fa, 0x4201)) - num = 4; - - for (i = 0; i < num; i++) { - alts = &iface->altsetting[i]; - altsd = get_iface_desc(alts); - protocol = altsd->bInterfaceProtocol; - /* skip invalid one */ - if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && - altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || - (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING && - altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) || - altsd->bNumEndpoints < 1 || - le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0) - continue; - /* must be isochronous */ - if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_ISOC) - continue; - /* check direction */ - stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ? - SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - altno = altsd->bAlternateSetting; - - /* audiophile usb: skip altsets incompatible with device_setup - */ - if (chip->usb_id == USB_ID(0x0763, 0x2003) && - audiophile_skip_setting_quirk(chip, iface_no, altno)) - continue; - - /* get audio formats */ - switch (protocol) { - case UAC_VERSION_1: { - struct uac_as_header_descriptor_v1 *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - - if (!as) { - snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", - dev->devnum, iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n", - dev->devnum, iface_no, altno); - continue; - } - - format = le16_to_cpu(as->wFormatTag); /* remember the format value */ - break; - } - - case UAC_VERSION_2: { - struct uac_as_header_descriptor_v2 *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - - if (!as) { - snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", - dev->devnum, iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n", - dev->devnum, iface_no, altno); - continue; - } - - num_channels = as->bNrChannels; - format = le32_to_cpu(as->bmFormats); - - break; - } - - default: - snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n", - dev->devnum, iface_no, altno, protocol); - continue; - } - - /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE); - if (!fmt) { - snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n", - dev->devnum, iface_no, altno); - continue; - } - if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) || - ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) { - snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", - dev->devnum, iface_no, altno); - continue; - } - - /* - * Blue Microphones workaround: The last altsetting is identical - * with the previous one, except for a larger packet size, but - * is actually a mislabeled two-channel setting; ignore it. - */ - if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 && - fp && fp->altsetting == 1 && fp->channels == 1 && - fp->format == SNDRV_PCM_FORMAT_S16_LE && - protocol == UAC_VERSION_1 && - le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == - fp->maxpacksize * 2) - continue; - - csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); - /* Creamware Noah has this descriptor after the 2nd endpoint */ - if (!csep && altsd->bNumEndpoints >= 2) - csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); - if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) { - snd_printk(KERN_WARNING "%d:%u:%d : no or invalid" - " class specific endpoint descriptor\n", - dev->devnum, iface_no, altno); - csep = NULL; - } - - fp = kzalloc(sizeof(*fp), GFP_KERNEL); - if (! fp) { - snd_printk(KERN_ERR "cannot malloc\n"); - return -ENOMEM; - } - - fp->iface = iface_no; - fp->altsetting = altno; - fp->altset_idx = i; - fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; - fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; - fp->datainterval = parse_datainterval(chip, alts); - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - /* num_channels is only set for v2 interfaces */ - fp->channels = num_channels; - if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) - fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) - * (fp->maxpacksize & 0x7ff); - fp->attributes = csep ? csep[3] : 0; - - /* some quirks for attributes here */ - - switch (chip->usb_id) { - case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ - /* Optoplay sets the sample rate attribute although - * it seems not supporting it in fact. - */ - fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ - case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - /* doesn't set the sample rate attribute, but supports it */ - fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ - case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is - an older model 77d:223) */ - /* - * plantronics headset and Griffin iMic have set adaptive-in - * although it's really not... - */ - fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; - else - fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; - break; - } - - /* ok, let's parse further... */ - if (parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { - kfree(fp->rate_table); - kfree(fp); - continue; - } - - snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); - err = add_audio_endpoint(chip, stream, fp); - if (err < 0) { - kfree(fp->rate_table); - kfree(fp); - return err; - } - /* try to set the interface... */ - usb_set_interface(chip->dev, iface_no, altno); - init_usb_pitch(chip->dev, iface_no, alts, fp); - init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); - } - return 0; -} - - -/* - * disconnect streams - * called from snd_usb_audio_disconnect() - */ -static void snd_usb_stream_disconnect(struct list_head *head) -{ - int idx; - struct snd_usb_stream *as; - struct snd_usb_substream *subs; - - as = list_entry(head, struct snd_usb_stream, list); - for (idx = 0; idx < 2; idx++) { - subs = &as->substream[idx]; - if (!subs->num_formats) - return; - release_substream_urbs(subs, 1); - subs->interface = -1; - } -} - -static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface) -{ - struct usb_device *dev = chip->dev; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_interface *iface = usb_ifnum_to_if(dev, interface); - - if (!iface) { - snd_printk(KERN_ERR "%d:%u:%d : does not exist\n", - dev->devnum, ctrlif, interface); - return -EINVAL; - } - - if (usb_interface_claimed(iface)) { - snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", - dev->devnum, ctrlif, interface); - return -EINVAL; - } - - alts = &iface->altsetting[0]; - altsd = get_iface_desc(alts); - if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && - altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = snd_usbmidi_create(chip->card, iface, - &chip->midi_list, NULL); - if (err < 0) { - snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", - dev->devnum, ctrlif, interface); - return -EINVAL; - } - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); - - return 0; - } - - if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && - altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || - altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) { - snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", - dev->devnum, ctrlif, interface, altsd->bInterfaceClass); - /* skip non-supported classes */ - return -EINVAL; - } - - if (snd_usb_get_speed(dev) == USB_SPEED_LOW) { - snd_printk(KERN_ERR "low speed audio streaming not supported\n"); - return -EINVAL; - } - - if (! parse_audio_endpoints(chip, interface)) { - usb_set_interface(dev, interface, 0); /* reset the current interface */ - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); - return -EINVAL; - } - - return 0; -} - -/* - * parse audio control descriptor and create pcm/midi streams - */ -static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) -{ - struct usb_device *dev = chip->dev; - struct usb_host_interface *host_iface; - struct usb_interface_descriptor *altsd; - void *control_header; - int i, protocol; - - /* find audiocontrol interface */ - host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; - control_header = snd_usb_find_csint_desc(host_iface->extra, - host_iface->extralen, - NULL, UAC_HEADER); - altsd = get_iface_desc(host_iface); - protocol = altsd->bInterfaceProtocol; - - if (!control_header) { - snd_printk(KERN_ERR "cannot find UAC_HEADER\n"); - return -EINVAL; - } - - switch (protocol) { - case UAC_VERSION_1: { - struct uac_ac_header_descriptor_v1 *h1 = control_header; - - if (!h1->bInCollection) { - snd_printk(KERN_INFO "skipping empty audio interface (v1)\n"); - return -EINVAL; - } - - if (h1->bLength < sizeof(*h1) + h1->bInCollection) { - snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n"); - return -EINVAL; - } - - for (i = 0; i < h1->bInCollection; i++) - snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); - - break; - } - - case UAC_VERSION_2: { - struct uac_clock_source_descriptor *cs; - struct usb_interface_assoc_descriptor *assoc = - usb_ifnum_to_if(dev, ctrlif)->intf_assoc; - - if (!assoc) { - snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n"); - return -EINVAL; - } - - /* FIXME: for now, we expect there is at least one clock source - * descriptor and we always take the first one. - * We should properly support devices with multiple clock sources, - * clock selectors and sample rate conversion units. */ - - cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, - NULL, UAC_CLOCK_SOURCE); - - if (!cs) { - snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n"); - return -EINVAL; - } - - chip->clock_id = cs->bClockID; - - for (i = 0; i < assoc->bInterfaceCount; i++) { - int intf = assoc->bFirstInterface + i; - - if (intf != ctrlif) - snd_usb_create_stream(chip, ctrlif, intf); - } - - break; - } - - default: - snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol); - return -EINVAL; - } - - return 0; -} - -/* - * create a stream for an endpoint/altsetting without proper descriptors - */ -static int create_fixed_stream_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - struct audioformat *fp; - struct usb_host_interface *alts; - int stream, err; - unsigned *rate_table = NULL; - - fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL); - if (! fp) { - snd_printk(KERN_ERR "cannot memdup\n"); - return -ENOMEM; - } - if (fp->nr_rates > 0) { - rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL); - if (!rate_table) { - kfree(fp); - return -ENOMEM; - } - memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates); - fp->rate_table = rate_table; - } - - stream = (fp->endpoint & USB_DIR_IN) - ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - err = add_audio_endpoint(chip, stream, fp); - if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; - } - if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || - fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; - } - alts = &iface->altsetting[fp->altset_idx]; - fp->datainterval = parse_datainterval(chip, alts); - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - usb_set_interface(chip->dev, fp->iface, 0); - init_usb_pitch(chip->dev, fp->iface, alts, fp); - init_usb_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max); - return 0; -} - -/* - * create a stream for an interface with proper descriptors - */ -static int create_standard_audio_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - int err; - - alts = &iface->altsetting[0]; - altsd = get_iface_desc(alts); - err = parse_audio_endpoints(chip, altsd->bInterfaceNumber); - if (err < 0) { - snd_printk(KERN_ERR "cannot setup if %d: error %d\n", - altsd->bInterfaceNumber, err); - return err; - } - /* reset the current interface */ - usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0); - return 0; -} - -/* - * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface. - * The only way to detect the sample rate is by looking at wMaxPacketSize. - */ -static int create_uaxx_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - static const struct audioformat ua_format = { - .format = SNDRV_PCM_FORMAT_S24_3LE, - .channels = 2, - .fmt_type = UAC_FORMAT_TYPE_I, - .altsetting = 1, - .altset_idx = 1, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - }; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct audioformat *fp; - int stream, err; - - /* both PCM and MIDI interfaces have 2 or more altsettings */ - if (iface->num_altsetting < 2) - return -ENXIO; - alts = &iface->altsetting[1]; - altsd = get_iface_desc(alts); - - if (altsd->bNumEndpoints == 2) { - static const struct snd_usb_midi_endpoint_info ua700_ep = { - .out_cables = 0x0003, - .in_cables = 0x0003 - }; - static const struct snd_usb_audio_quirk ua700_quirk = { - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &ua700_ep - }; - static const struct snd_usb_midi_endpoint_info uaxx_ep = { - .out_cables = 0x0001, - .in_cables = 0x0001 - }; - static const struct snd_usb_audio_quirk uaxx_quirk = { - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &uaxx_ep - }; - const struct snd_usb_audio_quirk *quirk = - chip->usb_id == USB_ID(0x0582, 0x002b) - ? &ua700_quirk : &uaxx_quirk; - return snd_usbmidi_create(chip->card, iface, - &chip->midi_list, quirk); - } - - if (altsd->bNumEndpoints != 1) - return -ENXIO; - - fp = kmalloc(sizeof(*fp), GFP_KERNEL); - if (!fp) - return -ENOMEM; - memcpy(fp, &ua_format, sizeof(*fp)); - - fp->iface = altsd->bInterfaceNumber; - fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; - fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; - fp->datainterval = 0; - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - - switch (fp->maxpacksize) { - case 0x120: - fp->rate_max = fp->rate_min = 44100; - break; - case 0x138: - case 0x140: - fp->rate_max = fp->rate_min = 48000; - break; - case 0x258: - case 0x260: - fp->rate_max = fp->rate_min = 96000; - break; - default: - snd_printk(KERN_ERR "unknown sample rate\n"); - kfree(fp); - return -ENXIO; - } - - stream = (fp->endpoint & USB_DIR_IN) - ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - err = add_audio_endpoint(chip, stream, fp); - if (err < 0) { - kfree(fp); - return err; - } - usb_set_interface(chip->dev, fp->iface, 0); - return 0; -} - -static int snd_usb_create_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk); - -/* - * handle the quirks for the contained interfaces - */ -static int create_composite_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; - int err; - - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { - iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); - if (!iface) - continue; - if (quirk->ifnum != probed_ifnum && - usb_interface_claimed(iface)) - continue; - err = snd_usb_create_quirk(chip, iface, quirk); - if (err < 0) - return err; - if (quirk->ifnum != probed_ifnum) - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); - } - return 0; -} - -static int ignore_interface_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - return 0; -} - -/* - * Allow alignment on audio sub-slot (channel samples) rather than - * on audio slots (audio frames) - */ -static int create_align_transfer_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - chip->txfr_quirk = 1; - return 1; /* Continue with creating streams and mixer */ -} - - -/* - * boot quirks - */ - -#define EXTIGY_FIRMWARE_SIZE_OLD 794 -#define EXTIGY_FIRMWARE_SIZE_NEW 483 - -static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf) -{ - struct usb_host_config *config = dev->actconfig; - int err; - - if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD || - le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) { - snd_printdd("sending Extigy boot sequence...\n"); - /* Send message to force it to reconnect with full interface. */ - err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0), - 0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000); - if (err < 0) snd_printdd("error sending boot message: %d\n", err); - err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; - if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err); - err = usb_reset_configuration(dev); - if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err); - snd_printdd("extigy_boot: new boot length = %d\n", - le16_to_cpu(get_cfg_desc(config)->wTotalLength)); - return -ENODEV; /* quit this anyway */ - } - return 0; -} - -static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) -{ - u8 buf = 1; - - snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 0, 0, &buf, 1, 1000); - if (buf == 0) { - snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 1, 2000, NULL, 0, 1000); - return -ENODEV; - } - return 0; -} - -/* - * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely - * documented in the device's data sheet. - */ -static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value) -{ - u8 buf[4]; - buf[0] = 0x20; - buf[1] = value & 0xff; - buf[2] = (value >> 8) & 0xff; - buf[3] = reg; - return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, - 0, 0, &buf, 4, 1000); -} - -static int snd_usb_cm106_boot_quirk(struct usb_device *dev) -{ - /* - * Enable line-out driver mode, set headphone source to front - * channels, enable stereo mic. - */ - return snd_usb_cm106_write_int_reg(dev, 2, 0x8004); -} - -/* - * C-Media CM6206 is based on CM106 with two additional - * registers that are not documented in the data sheet. - * Values here are chosen based on sniffing USB traffic - * under Windows. - */ -static int snd_usb_cm6206_boot_quirk(struct usb_device *dev) -{ - int err, reg; - int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000}; - - for (reg = 0; reg < ARRAY_SIZE(val); reg++) { - err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]); - if (err < 0) - return err; - } - - return err; -} - -/* - * This call will put the synth in "USB send" mode, i.e it will send MIDI - * messages through USB (this is disabled at startup). The synth will - * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB - * sign on its LCD. Values here are chosen based on sniffing USB traffic - * under Windows. - */ -static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) -{ - int err, actual_length; - - /* "midi send" enable */ - static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 }; - - void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); - if (!buf) - return -ENOMEM; - err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf, - ARRAY_SIZE(seq), &actual_length, 1000); - kfree(buf); - if (err < 0) - return err; - - return 0; -} - -/* - * Setup quirks - */ -#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */ -#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */ -#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */ -#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */ -#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */ -#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */ -#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */ -#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */ -#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */ -#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */ - -static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, - int iface, int altno) -{ - /* Reset ALL ifaces to 0 altsetting. - * Call it for every possible altsetting of every interface. - */ - usb_set_interface(chip->dev, iface, 0); - - if (device_setup[chip->index] & AUDIOPHILE_SET) { - if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS) - && altno != 6) - return 1; /* skip this altsetting */ - if ((device_setup[chip->index] & AUDIOPHILE_SET_96K) - && altno != 1) - return 1; /* skip this altsetting */ - if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_24B_48K_DI && altno != 2) - return 1; /* skip this altsetting */ - if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3) - return 1; /* skip this altsetting */ - if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_16B_48K_DI && altno != 4) - return 1; /* skip this altsetting */ - if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5) - return 1; /* skip this altsetting */ - } - return 0; /* keep this altsetting */ -} - -static int create_any_midi_quirk(struct snd_usb_audio *chip, - struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk) -{ - return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk); -} - -/* - * audio-interface quirks - * - * returns zero if no standard audio/MIDI parsing is needed. - * returns a postive value if standard audio/midi interfaces are parsed - * after this. - * returns a negative value at error. - */ -static int snd_usb_create_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - const struct snd_usb_audio_quirk *quirk) -{ - typedef int (*quirk_func_t)(struct snd_usb_audio *, struct usb_interface *, - const struct snd_usb_audio_quirk *); - static const quirk_func_t quirk_funcs[] = { - [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, - [QUIRK_COMPOSITE] = create_composite_quirk, - [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk, - [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk, - [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk, - [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk, - [QUIRK_MIDI_NOVATION] = create_any_midi_quirk, - [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk, - [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk, - [QUIRK_MIDI_CME] = create_any_midi_quirk, - [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, - [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, - [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, - [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk - }; - - if (quirk->type < QUIRK_TYPE_COUNT) { - return quirk_funcs[quirk->type](chip, iface, quirk); - } else { - snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); - return -ENXIO; - } -} - - -/* - * common proc files to show the usb device info - */ -static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) -{ - struct snd_usb_audio *chip = entry->private_data; - if (!chip->shutdown) - snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum); -} - -static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) -{ - struct snd_usb_audio *chip = entry->private_data; - if (!chip->shutdown) - snd_iprintf(buffer, "%04x:%04x\n", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); -} - -static void snd_usb_audio_create_proc(struct snd_usb_audio *chip) -{ - struct snd_info_entry *entry; - if (!snd_card_proc_new(chip->card, "usbbus", &entry)) - snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read); - if (!snd_card_proc_new(chip->card, "usbid", &entry)) - snd_info_set_text_ops(entry, chip, proc_audio_usbid_read); -} - -/* - * free the chip instance - * - * here we have to do not much, since pcm and controls are already freed - * - */ - -static int snd_usb_audio_free(struct snd_usb_audio *chip) -{ - kfree(chip); - return 0; -} - -static int snd_usb_audio_dev_free(struct snd_device *device) -{ - struct snd_usb_audio *chip = device->device_data; - return snd_usb_audio_free(chip); -} - - -/* - * create a chip instance and set its names. - */ -static int snd_usb_audio_create(struct usb_device *dev, int idx, - const struct snd_usb_audio_quirk *quirk, - struct snd_usb_audio **rchip) -{ - struct snd_card *card; - struct snd_usb_audio *chip; - int err, len; - char component[14]; - static struct snd_device_ops ops = { - .dev_free = snd_usb_audio_dev_free, - }; - - *rchip = NULL; - - if (snd_usb_get_speed(dev) != USB_SPEED_LOW && - snd_usb_get_speed(dev) != USB_SPEED_FULL && - snd_usb_get_speed(dev) != USB_SPEED_HIGH) { - snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev)); - return -ENXIO; - } - - err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card); - if (err < 0) { - snd_printk(KERN_ERR "cannot create card instance %d\n", idx); - return err; - } - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (! chip) { - snd_card_free(card); - return -ENOMEM; - } - - chip->index = idx; - chip->dev = dev; - chip->card = card; - chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - INIT_LIST_HEAD(&chip->pcm_list); - INIT_LIST_HEAD(&chip->midi_list); - INIT_LIST_HEAD(&chip->mixer_list); - - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_usb_audio_free(chip); - snd_card_free(card); - return err; - } - - strcpy(card->driver, "USB-Audio"); - sprintf(component, "USB%04x:%04x", - USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); - snd_component_add(card, component); - - /* retrieve the device string as shortname */ - if (quirk && quirk->product_name) { - strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname)); - } else { - if (!dev->descriptor.iProduct || - usb_string(dev, dev->descriptor.iProduct, - card->shortname, sizeof(card->shortname)) <= 0) { - /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); - } - } - - /* retrieve the vendor and device strings as longname */ - if (quirk && quirk->vendor_name) { - len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname)); - } else { - if (dev->descriptor.iManufacturer) - len = usb_string(dev, dev->descriptor.iManufacturer, - card->longname, sizeof(card->longname)); - else - len = 0; - /* we don't really care if there isn't any vendor string */ - } - if (len > 0) - strlcat(card->longname, " ", sizeof(card->longname)); - - strlcat(card->longname, card->shortname, sizeof(card->longname)); - - len = strlcat(card->longname, " at ", sizeof(card->longname)); - - if (len < sizeof(card->longname)) - usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); - - strlcat(card->longname, - snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" : - snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" : - ", high speed", - sizeof(card->longname)); - - snd_usb_audio_create_proc(chip); - - *rchip = chip; - return 0; -} - - -/* - * probe the active usb device - * - * note that this can be called multiple times per a device, when it - * includes multiple audio control interfaces. - * - * thus we check the usb device pointer and creates the card instance - * only at the first time. the successive calls of this function will - * append the pcm interface to the corresponding card. - */ -static void *snd_usb_audio_probe(struct usb_device *dev, - struct usb_interface *intf, - const struct usb_device_id *usb_id) -{ - const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; - int i, err; - struct snd_usb_audio *chip; - struct usb_host_interface *alts; - int ifnum; - u32 id; - - alts = &intf->altsetting[0]; - ifnum = get_iface_desc(alts)->bInterfaceNumber; - id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) - goto __err_val; - - /* SB Extigy needs special boot-up sequence */ - /* if more models come, this will go to the quirk list. */ - if (id == USB_ID(0x041e, 0x3000)) { - if (snd_usb_extigy_boot_quirk(dev, intf) < 0) - goto __err_val; - } - /* SB Audigy 2 NX needs its own boot-up magic, too */ - if (id == USB_ID(0x041e, 0x3020)) { - if (snd_usb_audigy2nx_boot_quirk(dev) < 0) - goto __err_val; - } - - /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */ - if (id == USB_ID(0x10f5, 0x0200)) { - if (snd_usb_cm106_boot_quirk(dev) < 0) - goto __err_val; - } - - /* C-Media CM6206 / CM106-Like Sound Device */ - if (id == USB_ID(0x0d8c, 0x0102)) { - if (snd_usb_cm6206_boot_quirk(dev) < 0) - goto __err_val; - } - - /* Access Music VirusTI Desktop */ - if (id == USB_ID(0x133e, 0x0815)) { - if (snd_usb_accessmusic_boot_quirk(dev) < 0) - goto __err_val; - } - - /* - * found a config. now register to ALSA - */ - - /* check whether it's already registered */ - chip = NULL; - mutex_lock(®ister_mutex); - for (i = 0; i < SNDRV_CARDS; i++) { - if (usb_chip[i] && usb_chip[i]->dev == dev) { - if (usb_chip[i]->shutdown) { - snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n"); - goto __error; - } - chip = usb_chip[i]; - break; - } - } - if (! chip) { - /* it's a fresh one. - * now look for an empty slot and create a new card instance - */ - for (i = 0; i < SNDRV_CARDS; i++) - if (enable[i] && ! usb_chip[i] && - (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && - (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { - if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) { - goto __error; - } - snd_card_set_dev(chip->card, &intf->dev); - break; - } - if (!chip) { - printk(KERN_ERR "no available usb audio device\n"); - goto __error; - } - } - - chip->txfr_quirk = 0; - err = 1; /* continue */ - if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { - /* need some special handlings */ - if ((err = snd_usb_create_quirk(chip, intf, quirk)) < 0) - goto __error; - } - - if (err > 0) { - /* create normal USB audio interfaces */ - if (snd_usb_create_streams(chip, ifnum) < 0 || - snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) { - goto __error; - } - } - - /* we are allowed to call snd_card_register() many times */ - if (snd_card_register(chip->card) < 0) { - goto __error; - } - - usb_chip[chip->index] = chip; - chip->num_interfaces++; - mutex_unlock(®ister_mutex); - return chip; - - __error: - if (chip && !chip->num_interfaces) - snd_card_free(chip->card); - mutex_unlock(®ister_mutex); - __err_val: - return NULL; -} - -/* - * we need to take care of counter, since disconnection can be called also - * many times as well as usb_audio_probe(). - */ -static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) -{ - struct snd_usb_audio *chip; - struct snd_card *card; - struct list_head *p; - - if (ptr == (void *)-1L) - return; - - chip = ptr; - card = chip->card; - mutex_lock(®ister_mutex); - chip->shutdown = 1; - chip->num_interfaces--; - if (chip->num_interfaces <= 0) { - snd_card_disconnect(card); - /* release the pcm resources */ - list_for_each(p, &chip->pcm_list) { - snd_usb_stream_disconnect(p); - } - /* release the midi resources */ - list_for_each(p, &chip->midi_list) { - snd_usbmidi_disconnect(p); - } - /* release mixer resources */ - list_for_each(p, &chip->mixer_list) { - snd_usb_mixer_disconnect(p); - } - usb_chip[chip->index] = NULL; - mutex_unlock(®ister_mutex); - snd_card_free_when_closed(card); - } else { - mutex_unlock(®ister_mutex); - } -} - -/* - * new 2.5 USB kernel API - */ -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - void *chip; - chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); - if (chip) { - usb_set_intfdata(intf, chip); - return 0; - } else - return -EIO; -} - -static void usb_audio_disconnect(struct usb_interface *intf) -{ - snd_usb_audio_disconnect(interface_to_usbdev(intf), - usb_get_intfdata(intf)); -} - -#ifdef CONFIG_PM -static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct snd_usb_audio *chip = usb_get_intfdata(intf); - struct list_head *p; - struct snd_usb_stream *as; - - if (chip == (void *)-1L) - return 0; - - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); - if (!chip->num_suspended_intf++) { - list_for_each(p, &chip->pcm_list) { - as = list_entry(p, struct snd_usb_stream, list); - snd_pcm_suspend_all(as->pcm); - } - } - - return 0; -} - -static int usb_audio_resume(struct usb_interface *intf) -{ - struct snd_usb_audio *chip = usb_get_intfdata(intf); - - if (chip == (void *)-1L) - return 0; - if (--chip->num_suspended_intf) - return 0; - /* - * ALSA leaves material resumption to user space - * we just notify - */ - - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); - - return 0; -} -#endif /* CONFIG_PM */ - -static int __init snd_usb_audio_init(void) -{ - if (nrpacks < 1 || nrpacks > MAX_PACKS) { - printk(KERN_WARNING "invalid nrpacks value.\n"); - return -EINVAL; - } - return usb_register(&usb_audio_driver); -} - - -static void __exit snd_usb_audio_cleanup(void) -{ - usb_deregister(&usb_audio_driver); -} - -module_init(snd_usb_audio_init); -module_exit(snd_usb_audio_cleanup); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 49a691a..d679e72 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -27,6 +27,7 @@ #define USB_ID_PRODUCT(id) ((u16)(id)) /* + * */ struct snd_usb_audio { @@ -48,6 +49,10 @@ struct snd_usb_audio { struct list_head midi_list; /* list of midi interfaces */ struct list_head mixer_list; /* list of mixer interfaces */ + + int setup; /* from the 'device_setup' module param */ + int nrpacks; /* from the 'nrpacks' module param */ + int async_unlink; /* from the 'async_unlink' module param */ }; /* @@ -86,45 +91,8 @@ struct snd_usb_audio_quirk { const void *data; }; -/* - */ - -/*E-mu USB samplerate control quirk*/ -enum { - EMU_QUIRK_SR_44100HZ = 0, - EMU_QUIRK_SR_48000HZ, - EMU_QUIRK_SR_88200HZ, - EMU_QUIRK_SR_96000HZ, - EMU_QUIRK_SR_176400HZ, - EMU_QUIRK_SR_192000HZ -}; - #define combine_word(s) ((*(s)) | ((unsigned int)(s)[1] << 8)) #define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16)) #define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24)) -unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size); - -void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype); -void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype); - -int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, - __u8 request, __u8 requesttype, __u16 value, __u16 index, - void *data, __u16 size, int timeout); - -/* - * retrieve usb_interface descriptor from the host interface - * (conditional for compatibility with the older API) - */ -#ifndef get_iface_desc -#define get_iface_desc(iface) (&(iface)->desc) -#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc) -#define get_ep_desc(ep) (&(ep)->desc) -#define get_cfg_desc(cfg) (&(cfg)->desc) -#endif - -#ifndef snd_usb_get_speed -#define snd_usb_get_speed(dev) ((dev)->speed) -#endif - #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c deleted file mode 100644 index 5915a04..0000000 --- a/sound/usb/usbmidi.c +++ /dev/null @@ -1,2069 +0,0 @@ -/* - * usbmidi.c - ALSA USB MIDI driver - * - * Copyright (c) 2002-2009 Clemens Ladisch - * All rights reserved. - * - * Based on the OSS usb-midi driver by NAGANO Daisuke, - * NetBSD's umidi driver by Takuya SHIOZAKI, - * the "USB Device Class Definition for MIDI Devices" by Roland - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * Alternatively, this software may be distributed and/or modified under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "usbaudio.h" -#include "usbmidi.h" - - -/* - * define this to log all USB packets - */ -/* #define DUMP_PACKETS */ - -/* - * how long to wait after some USB errors, so that khubd can disconnect() us - * without too many spurious errors - */ -#define ERROR_DELAY_JIFFIES (HZ / 10) - -#define OUTPUT_URBS 7 -#define INPUT_URBS 7 - - -MODULE_AUTHOR("Clemens Ladisch "); -MODULE_DESCRIPTION("USB Audio/MIDI helper module"); -MODULE_LICENSE("Dual BSD/GPL"); - - -struct usb_ms_header_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubtype; - __u8 bcdMSC[2]; - __le16 wTotalLength; -} __attribute__ ((packed)); - -struct usb_ms_endpoint_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubtype; - __u8 bNumEmbMIDIJack; - __u8 baAssocJackID[0]; -} __attribute__ ((packed)); - -struct snd_usb_midi_in_endpoint; -struct snd_usb_midi_out_endpoint; -struct snd_usb_midi_endpoint; - -struct usb_protocol_ops { - void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int); - void (*output)(struct snd_usb_midi_out_endpoint *ep, struct urb *urb); - void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t); - void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*); - void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*); -}; - -struct snd_usb_midi { - struct usb_device *dev; - struct snd_card *card; - struct usb_interface *iface; - const struct snd_usb_audio_quirk *quirk; - struct snd_rawmidi *rmidi; - struct usb_protocol_ops* usb_protocol_ops; - struct list_head list; - struct timer_list error_timer; - spinlock_t disc_lock; - struct mutex mutex; - u32 usb_id; - int next_midi_device; - - struct snd_usb_midi_endpoint { - struct snd_usb_midi_out_endpoint *out; - struct snd_usb_midi_in_endpoint *in; - } endpoints[MIDI_MAX_ENDPOINTS]; - unsigned long input_triggered; - unsigned int opened; - unsigned char disconnected; - - struct snd_kcontrol *roland_load_ctl; -}; - -struct snd_usb_midi_out_endpoint { - struct snd_usb_midi* umidi; - struct out_urb_context { - struct urb *urb; - struct snd_usb_midi_out_endpoint *ep; - } urbs[OUTPUT_URBS]; - unsigned int active_urbs; - unsigned int drain_urbs; - int max_transfer; /* size of urb buffer */ - struct tasklet_struct tasklet; - unsigned int next_urb; - spinlock_t buffer_lock; - - struct usbmidi_out_port { - struct snd_usb_midi_out_endpoint* ep; - struct snd_rawmidi_substream *substream; - int active; - uint8_t cable; /* cable number << 4 */ - uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 - uint8_t data[2]; - } ports[0x10]; - int current_port; - - wait_queue_head_t drain_wait; -}; - -struct snd_usb_midi_in_endpoint { - struct snd_usb_midi* umidi; - struct urb* urbs[INPUT_URBS]; - struct usbmidi_in_port { - struct snd_rawmidi_substream *substream; - u8 running_status_length; - } ports[0x10]; - u8 seen_f5; - u8 error_resubmit; - int current_port; -}; - -static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep); - -static const uint8_t snd_usbmidi_cin_length[] = { - 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 -}; - -/* - * Submits the URB, with error handling. - */ -static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags) -{ - int err = usb_submit_urb(urb, flags); - if (err < 0 && err != -ENODEV) - snd_printk(KERN_ERR "usb_submit_urb: %d\n", err); - return err; -} - -/* - * Error handling for URB completion functions. - */ -static int snd_usbmidi_urb_error(int status) -{ - switch (status) { - /* manually unlinked, or device gone */ - case -ENOENT: - case -ECONNRESET: - case -ESHUTDOWN: - case -ENODEV: - return -ENODEV; - /* errors that might occur during unplugging */ - case -EPROTO: - case -ETIME: - case -EILSEQ: - return -EIO; - default: - snd_printk(KERN_ERR "urb status %d\n", status); - return 0; /* continue */ - } -} - -/* - * Receives a chunk of MIDI data. - */ -static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint* ep, int portidx, - uint8_t* data, int length) -{ - struct usbmidi_in_port* port = &ep->ports[portidx]; - - if (!port->substream) { - snd_printd("unexpected port %d!\n", portidx); - return; - } - if (!test_bit(port->substream->number, &ep->umidi->input_triggered)) - return; - snd_rawmidi_receive(port->substream, data, length); -} - -#ifdef DUMP_PACKETS -static void dump_urb(const char *type, const u8 *data, int length) -{ - snd_printk(KERN_DEBUG "%s packet: [", type); - for (; length > 0; ++data, --length) - printk(" %02x", *data); - printk(" ]\n"); -} -#else -#define dump_urb(type, data, length) /* nothing */ -#endif - -/* - * Processes the data read from the device. - */ -static void snd_usbmidi_in_urb_complete(struct urb* urb) -{ - struct snd_usb_midi_in_endpoint* ep = urb->context; - - if (urb->status == 0) { - dump_urb("received", urb->transfer_buffer, urb->actual_length); - ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer, - urb->actual_length); - } else { - int err = snd_usbmidi_urb_error(urb->status); - if (err < 0) { - if (err != -ENODEV) { - ep->error_resubmit = 1; - mod_timer(&ep->umidi->error_timer, - jiffies + ERROR_DELAY_JIFFIES); - } - return; - } - } - - urb->dev = ep->umidi->dev; - snd_usbmidi_submit_urb(urb, GFP_ATOMIC); -} - -static void snd_usbmidi_out_urb_complete(struct urb* urb) -{ - struct out_urb_context *context = urb->context; - struct snd_usb_midi_out_endpoint* ep = context->ep; - unsigned int urb_index; - - spin_lock(&ep->buffer_lock); - urb_index = context - ep->urbs; - ep->active_urbs &= ~(1 << urb_index); - if (unlikely(ep->drain_urbs)) { - ep->drain_urbs &= ~(1 << urb_index); - wake_up(&ep->drain_wait); - } - spin_unlock(&ep->buffer_lock); - if (urb->status < 0) { - int err = snd_usbmidi_urb_error(urb->status); - if (err < 0) { - if (err != -ENODEV) - mod_timer(&ep->umidi->error_timer, - jiffies + ERROR_DELAY_JIFFIES); - return; - } - } - snd_usbmidi_do_output(ep); -} - -/* - * This is called when some data should be transferred to the device - * (from one or more substreams). - */ -static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep) -{ - unsigned int urb_index; - struct urb* urb; - unsigned long flags; - - spin_lock_irqsave(&ep->buffer_lock, flags); - if (ep->umidi->disconnected) { - spin_unlock_irqrestore(&ep->buffer_lock, flags); - return; - } - - urb_index = ep->next_urb; - for (;;) { - if (!(ep->active_urbs & (1 << urb_index))) { - urb = ep->urbs[urb_index].urb; - urb->transfer_buffer_length = 0; - ep->umidi->usb_protocol_ops->output(ep, urb); - if (urb->transfer_buffer_length == 0) - break; - - dump_urb("sending", urb->transfer_buffer, - urb->transfer_buffer_length); - urb->dev = ep->umidi->dev; - if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0) - break; - ep->active_urbs |= 1 << urb_index; - } - if (++urb_index >= OUTPUT_URBS) - urb_index = 0; - if (urb_index == ep->next_urb) - break; - } - ep->next_urb = urb_index; - spin_unlock_irqrestore(&ep->buffer_lock, flags); -} - -static void snd_usbmidi_out_tasklet(unsigned long data) -{ - struct snd_usb_midi_out_endpoint* ep = (struct snd_usb_midi_out_endpoint *) data; - - snd_usbmidi_do_output(ep); -} - -/* called after transfers had been interrupted due to some USB error */ -static void snd_usbmidi_error_timer(unsigned long data) -{ - struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; - unsigned int i, j; - - spin_lock(&umidi->disc_lock); - if (umidi->disconnected) { - spin_unlock(&umidi->disc_lock); - return; - } - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in; - if (in && in->error_resubmit) { - in->error_resubmit = 0; - for (j = 0; j < INPUT_URBS; ++j) { - in->urbs[j]->dev = umidi->dev; - snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); - } - } - if (umidi->endpoints[i].out) - snd_usbmidi_do_output(umidi->endpoints[i].out); - } - spin_unlock(&umidi->disc_lock); -} - -/* helper function to send static data that may not DMA-able */ -static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep, - const void *data, int len) -{ - int err = 0; - void *buf = kmemdup(data, len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - dump_urb("sending", buf, len); - if (ep->urbs[0].urb) - err = usb_bulk_msg(ep->umidi->dev, ep->urbs[0].urb->pipe, - buf, len, NULL, 250); - kfree(buf); - return err; -} - -/* - * Standard USB MIDI protocol: see the spec. - * Midiman protocol: like the standard protocol, but the control byte is the - * fourth byte in each packet, and uses length instead of CIN. - */ - -static void snd_usbmidi_standard_input(struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - int i; - - for (i = 0; i + 3 < buffer_length; i += 4) - if (buffer[i] != 0) { - int cable = buffer[i] >> 4; - int length = snd_usbmidi_cin_length[buffer[i] & 0x0f]; - snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); - } -} - -static void snd_usbmidi_midiman_input(struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - int i; - - for (i = 0; i + 3 < buffer_length; i += 4) - if (buffer[i + 3] != 0) { - int port = buffer[i + 3] >> 4; - int length = buffer[i + 3] & 3; - snd_usbmidi_input_data(ep, port, &buffer[i], length); - } -} - -/* - * Buggy M-Audio device: running status on input results in a packet that has - * the data bytes but not the status byte and that is marked with CIN 4. - */ -static void snd_usbmidi_maudio_broken_running_status_input( - struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - int i; - - for (i = 0; i + 3 < buffer_length; i += 4) - if (buffer[i] != 0) { - int cable = buffer[i] >> 4; - u8 cin = buffer[i] & 0x0f; - struct usbmidi_in_port *port = &ep->ports[cable]; - int length; - - length = snd_usbmidi_cin_length[cin]; - if (cin == 0xf && buffer[i + 1] >= 0xf8) - ; /* realtime msg: no running status change */ - else if (cin >= 0x8 && cin <= 0xe) - /* channel msg */ - port->running_status_length = length - 1; - else if (cin == 0x4 && - port->running_status_length != 0 && - buffer[i + 1] < 0x80) - /* CIN 4 that is not a SysEx */ - length = port->running_status_length; - else - /* - * All other msgs cannot begin running status. - * (A channel msg sent as two or three CIN 0xF - * packets could in theory, but this device - * doesn't use this format.) - */ - port->running_status_length = 0; - snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); - } -} - -/* - * CME protocol: like the standard protocol, but SysEx commands are sent as a - * single USB packet preceded by a 0x0F byte. - */ -static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep, - uint8_t *buffer, int buffer_length) -{ - if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f) - snd_usbmidi_standard_input(ep, buffer, buffer_length); - else - snd_usbmidi_input_data(ep, buffer[0] >> 4, - &buffer[1], buffer_length - 1); -} - -/* - * Adds one USB MIDI packet to the output buffer. - */ -static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - - uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length; - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - urb->transfer_buffer_length += 4; -} - -/* - * Adds one Midiman packet to the output buffer. - */ -static void snd_usbmidi_output_midiman_packet(struct urb* urb, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - - uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length; - buf[0] = p1; - buf[1] = p2; - buf[2] = p3; - buf[3] = (p0 & 0xf0) | snd_usbmidi_cin_length[p0 & 0x0f]; - urb->transfer_buffer_length += 4; -} - -/* - * Converts MIDI commands to USB MIDI packets. - */ -static void snd_usbmidi_transmit_byte(struct usbmidi_out_port* port, - uint8_t b, struct urb* urb) -{ - uint8_t p0 = port->cable; - void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t) = - port->ep->umidi->usb_protocol_ops->output_packet; - - if (b >= 0xf8) { - output_packet(urb, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { - switch (b) { - case 0xf0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case 0xf1: - case 0xf3: - port->data[0] = b; - port->state = STATE_1PARAM; - break; - case 0xf2: - port->data[0] = b; - port->state = STATE_2PARAM_1; - break; - case 0xf4: - case 0xf5: - port->state = STATE_UNKNOWN; - break; - case 0xf6: - output_packet(urb, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - output_packet(urb, p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - output_packet(urb, p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - output_packet(urb, p0 | 0x07, port->data[0], port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; - break; - } - } else if (b >= 0x80) { - port->data[0] = b; - if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; - else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ - switch (port->state) { - case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - output_packet(urb, p0, port->data[0], b, 0); - break; - case STATE_2PARAM_1: - port->data[1] = b; - port->state = STATE_2PARAM_2; - break; - case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - output_packet(urb, p0, port->data[0], port->data[1], b); - break; - case STATE_SYSEX_0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case STATE_SYSEX_1: - port->data[1] = b; - port->state = STATE_SYSEX_2; - break; - case STATE_SYSEX_2: - output_packet(urb, p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; - break; - } - } -} - -static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep, - struct urb *urb) -{ - int p; - - /* FIXME: lower-numbered ports can starve higher-numbered ports */ - for (p = 0; p < 0x10; ++p) { - struct usbmidi_out_port* port = &ep->ports[p]; - if (!port->active) - continue; - while (urb->transfer_buffer_length + 3 < ep->max_transfer) { - uint8_t b; - if (snd_rawmidi_transmit(port->substream, &b, 1) != 1) { - port->active = 0; - break; - } - snd_usbmidi_transmit_byte(port, b, urb); - } - } -} - -static struct usb_protocol_ops snd_usbmidi_standard_ops = { - .input = snd_usbmidi_standard_input, - .output = snd_usbmidi_standard_output, - .output_packet = snd_usbmidi_output_standard_packet, -}; - -static struct usb_protocol_ops snd_usbmidi_midiman_ops = { - .input = snd_usbmidi_midiman_input, - .output = snd_usbmidi_standard_output, - .output_packet = snd_usbmidi_output_midiman_packet, -}; - -static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = { - .input = snd_usbmidi_maudio_broken_running_status_input, - .output = snd_usbmidi_standard_output, - .output_packet = snd_usbmidi_output_standard_packet, -}; - -static struct usb_protocol_ops snd_usbmidi_cme_ops = { - .input = snd_usbmidi_cme_input, - .output = snd_usbmidi_standard_output, - .output_packet = snd_usbmidi_output_standard_packet, -}; - -/* - * Novation USB MIDI protocol: number of data bytes is in the first byte - * (when receiving) (+1!) or in the second byte (when sending); data begins - * at the third byte. - */ - -static void snd_usbmidi_novation_input(struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - if (buffer_length < 2 || !buffer[0] || buffer_length < buffer[0] + 1) - return; - snd_usbmidi_input_data(ep, 0, &buffer[2], buffer[0] - 1); -} - -static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep, - struct urb *urb) -{ - uint8_t* transfer_buffer; - int count; - - if (!ep->ports[0].active) - return; - transfer_buffer = urb->transfer_buffer; - count = snd_rawmidi_transmit(ep->ports[0].substream, - &transfer_buffer[2], - ep->max_transfer - 2); - if (count < 1) { - ep->ports[0].active = 0; - return; - } - transfer_buffer[0] = 0; - transfer_buffer[1] = count; - urb->transfer_buffer_length = 2 + count; -} - -static struct usb_protocol_ops snd_usbmidi_novation_ops = { - .input = snd_usbmidi_novation_input, - .output = snd_usbmidi_novation_output, -}; - -/* - * "raw" protocol: used by the MOTU FastLane. - */ - -static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - snd_usbmidi_input_data(ep, 0, buffer, buffer_length); -} - -static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep, - struct urb *urb) -{ - int count; - - if (!ep->ports[0].active) - return; - count = snd_rawmidi_transmit(ep->ports[0].substream, - urb->transfer_buffer, - ep->max_transfer); - if (count < 1) { - ep->ports[0].active = 0; - return; - } - urb->transfer_buffer_length = count; -} - -static struct usb_protocol_ops snd_usbmidi_raw_ops = { - .input = snd_usbmidi_raw_input, - .output = snd_usbmidi_raw_output, -}; - -static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep, - uint8_t *buffer, int buffer_length) -{ - if (buffer_length != 9) - return; - buffer_length = 8; - while (buffer_length && buffer[buffer_length - 1] == 0xFD) - buffer_length--; - if (buffer_length) - snd_usbmidi_input_data(ep, 0, buffer, buffer_length); -} - -static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, - struct urb *urb) -{ - int count; - - if (!ep->ports[0].active) - return; - count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2; - count = snd_rawmidi_transmit(ep->ports[0].substream, - urb->transfer_buffer, - count); - if (count < 1) { - ep->ports[0].active = 0; - return; - } - - memset(urb->transfer_buffer + count, 0xFD, 9 - count); - urb->transfer_buffer_length = count; -} - -static struct usb_protocol_ops snd_usbmidi_122l_ops = { - .input = snd_usbmidi_us122l_input, - .output = snd_usbmidi_us122l_output, -}; - -/* - * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching. - */ - -static void snd_usbmidi_emagic_init_out(struct snd_usb_midi_out_endpoint* ep) -{ - static const u8 init_data[] = { - /* initialization magic: "get version" */ - 0xf0, - 0x00, 0x20, 0x31, /* Emagic */ - 0x64, /* Unitor8 */ - 0x0b, /* version number request */ - 0x00, /* command version */ - 0x00, /* EEPROM, box 0 */ - 0xf7 - }; - send_bulk_static_data(ep, init_data, sizeof(init_data)); - /* while we're at it, pour on more magic */ - send_bulk_static_data(ep, init_data, sizeof(init_data)); -} - -static void snd_usbmidi_emagic_finish_out(struct snd_usb_midi_out_endpoint* ep) -{ - static const u8 finish_data[] = { - /* switch to patch mode with last preset */ - 0xf0, - 0x00, 0x20, 0x31, /* Emagic */ - 0x64, /* Unitor8 */ - 0x10, /* patch switch command */ - 0x00, /* command version */ - 0x7f, /* to all boxes */ - 0x40, /* last preset in EEPROM */ - 0xf7 - }; - send_bulk_static_data(ep, finish_data, sizeof(finish_data)); -} - -static void snd_usbmidi_emagic_input(struct snd_usb_midi_in_endpoint* ep, - uint8_t* buffer, int buffer_length) -{ - int i; - - /* FF indicates end of valid data */ - for (i = 0; i < buffer_length; ++i) - if (buffer[i] == 0xff) { - buffer_length = i; - break; - } - - /* handle F5 at end of last buffer */ - if (ep->seen_f5) - goto switch_port; - - while (buffer_length > 0) { - /* determine size of data until next F5 */ - for (i = 0; i < buffer_length; ++i) - if (buffer[i] == 0xf5) - break; - snd_usbmidi_input_data(ep, ep->current_port, buffer, i); - buffer += i; - buffer_length -= i; - - if (buffer_length <= 0) - break; - /* assert(buffer[0] == 0xf5); */ - ep->seen_f5 = 1; - ++buffer; - --buffer_length; - - switch_port: - if (buffer_length <= 0) - break; - if (buffer[0] < 0x80) { - ep->current_port = (buffer[0] - 1) & 15; - ++buffer; - --buffer_length; - } - ep->seen_f5 = 0; - } -} - -static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep, - struct urb *urb) -{ - int port0 = ep->current_port; - uint8_t* buf = urb->transfer_buffer; - int buf_free = ep->max_transfer; - int length, i; - - for (i = 0; i < 0x10; ++i) { - /* round-robin, starting at the last current port */ - int portnum = (port0 + i) & 15; - struct usbmidi_out_port* port = &ep->ports[portnum]; - - if (!port->active) - continue; - if (snd_rawmidi_transmit_peek(port->substream, buf, 1) != 1) { - port->active = 0; - continue; - } - - if (portnum != ep->current_port) { - if (buf_free < 2) - break; - ep->current_port = portnum; - buf[0] = 0xf5; - buf[1] = (portnum + 1) & 15; - buf += 2; - buf_free -= 2; - } - - if (buf_free < 1) - break; - length = snd_rawmidi_transmit(port->substream, buf, buf_free); - if (length > 0) { - buf += length; - buf_free -= length; - if (buf_free < 1) - break; - } - } - if (buf_free < ep->max_transfer && buf_free > 0) { - *buf = 0xff; - --buf_free; - } - urb->transfer_buffer_length = ep->max_transfer - buf_free; -} - -static struct usb_protocol_ops snd_usbmidi_emagic_ops = { - .input = snd_usbmidi_emagic_input, - .output = snd_usbmidi_emagic_output, - .init_out_endpoint = snd_usbmidi_emagic_init_out, - .finish_out_endpoint = snd_usbmidi_emagic_finish_out, -}; - - -static void update_roland_altsetting(struct snd_usb_midi* umidi) -{ - struct usb_interface *intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor *intfd; - int is_light_load; - - intf = umidi->iface; - is_light_load = intf->cur_altsetting != intf->altsetting; - if (umidi->roland_load_ctl->private_value == is_light_load) - return; - hostif = &intf->altsetting[umidi->roland_load_ctl->private_value]; - intfd = get_iface_desc(hostif); - snd_usbmidi_input_stop(&umidi->list); - usb_set_interface(umidi->dev, intfd->bInterfaceNumber, - intfd->bAlternateSetting); - snd_usbmidi_input_start(&umidi->list); -} - -static void substream_open(struct snd_rawmidi_substream *substream, int open) -{ - struct snd_usb_midi* umidi = substream->rmidi->private_data; - struct snd_kcontrol *ctl; - - mutex_lock(&umidi->mutex); - if (open) { - if (umidi->opened++ == 0 && umidi->roland_load_ctl) { - ctl = umidi->roland_load_ctl; - ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(umidi->card, - SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - update_roland_altsetting(umidi); - } - } else { - if (--umidi->opened == 0 && umidi->roland_load_ctl) { - ctl = umidi->roland_load_ctl; - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(umidi->card, - SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - } - } - mutex_unlock(&umidi->mutex); -} - -static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) -{ - struct snd_usb_midi* umidi = substream->rmidi->private_data; - struct usbmidi_out_port* port = NULL; - int i, j; - - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - if (umidi->endpoints[i].out) - for (j = 0; j < 0x10; ++j) - if (umidi->endpoints[i].out->ports[j].substream == substream) { - port = &umidi->endpoints[i].out->ports[j]; - break; - } - if (!port) { - snd_BUG(); - return -ENXIO; - } - substream->runtime->private_data = port; - port->state = STATE_UNKNOWN; - substream_open(substream, 1); - return 0; -} - -static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) -{ - substream_open(substream, 0); - return 0; -} - -static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct usbmidi_out_port* port = (struct usbmidi_out_port*)substream->runtime->private_data; - - port->active = up; - if (up) { - if (port->ep->umidi->disconnected) { - /* gobble up remaining bytes to prevent wait in - * snd_rawmidi_drain_output */ - while (!snd_rawmidi_transmit_empty(substream)) - snd_rawmidi_transmit_ack(substream, 1); - return; - } - tasklet_schedule(&port->ep->tasklet); - } -} - -static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) -{ - struct usbmidi_out_port* port = substream->runtime->private_data; - struct snd_usb_midi_out_endpoint *ep = port->ep; - unsigned int drain_urbs; - DEFINE_WAIT(wait); - long timeout = msecs_to_jiffies(50); - - /* - * The substream buffer is empty, but some data might still be in the - * currently active URBs, so we have to wait for those to complete. - */ - spin_lock_irq(&ep->buffer_lock); - drain_urbs = ep->active_urbs; - if (drain_urbs) { - ep->drain_urbs |= drain_urbs; - do { - prepare_to_wait(&ep->drain_wait, &wait, - TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&ep->buffer_lock); - timeout = schedule_timeout(timeout); - spin_lock_irq(&ep->buffer_lock); - drain_urbs &= ep->drain_urbs; - } while (drain_urbs && timeout); - finish_wait(&ep->drain_wait, &wait); - } - spin_unlock_irq(&ep->buffer_lock); -} - -static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) -{ - substream_open(substream, 1); - return 0; -} - -static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) -{ - substream_open(substream, 0); - return 0; -} - -static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct snd_usb_midi* umidi = substream->rmidi->private_data; - - if (up) - set_bit(substream->number, &umidi->input_triggered); - else - clear_bit(substream->number, &umidi->input_triggered); -} - -static struct snd_rawmidi_ops snd_usbmidi_output_ops = { - .open = snd_usbmidi_output_open, - .close = snd_usbmidi_output_close, - .trigger = snd_usbmidi_output_trigger, - .drain = snd_usbmidi_output_drain, -}; - -static struct snd_rawmidi_ops snd_usbmidi_input_ops = { - .open = snd_usbmidi_input_open, - .close = snd_usbmidi_input_close, - .trigger = snd_usbmidi_input_trigger -}; - -static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb, - unsigned int buffer_length) -{ - usb_buffer_free(umidi->dev, buffer_length, - urb->transfer_buffer, urb->transfer_dma); - usb_free_urb(urb); -} - -/* - * Frees an input endpoint. - * May be called when ep hasn't been initialized completely. - */ -static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep) -{ - unsigned int i; - - for (i = 0; i < INPUT_URBS; ++i) - if (ep->urbs[i]) - free_urb_and_buffer(ep->umidi, ep->urbs[i], - ep->urbs[i]->transfer_buffer_length); - kfree(ep); -} - -/* - * Creates an input endpoint. - */ -static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* ep_info, - struct snd_usb_midi_endpoint* rep) -{ - struct snd_usb_midi_in_endpoint* ep; - void* buffer; - unsigned int pipe; - int length; - unsigned int i; - - rep->in = NULL; - ep = kzalloc(sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - ep->umidi = umidi; - - for (i = 0; i < INPUT_URBS; ++i) { - ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!ep->urbs[i]) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; - } - } - if (ep_info->in_interval) - pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep); - else - pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep); - length = usb_maxpacket(umidi->dev, pipe, 0); - for (i = 0; i < INPUT_URBS; ++i) { - buffer = usb_buffer_alloc(umidi->dev, length, GFP_KERNEL, - &ep->urbs[i]->transfer_dma); - if (!buffer) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; - } - if (ep_info->in_interval) - usb_fill_int_urb(ep->urbs[i], umidi->dev, - pipe, buffer, length, - snd_usbmidi_in_urb_complete, - ep, ep_info->in_interval); - else - usb_fill_bulk_urb(ep->urbs[i], umidi->dev, - pipe, buffer, length, - snd_usbmidi_in_urb_complete, ep); - ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - } - - rep->in = ep; - return 0; -} - -/* - * Frees an output endpoint. - * May be called when ep hasn't been initialized completely. - */ -static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep) -{ - unsigned int i; - - for (i = 0; i < OUTPUT_URBS; ++i) - if (ep->urbs[i].urb) - free_urb_and_buffer(ep->umidi, ep->urbs[i].urb, - ep->max_transfer); - kfree(ep); -} - -/* - * Creates an output endpoint, and initializes output ports. - */ -static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* ep_info, - struct snd_usb_midi_endpoint* rep) -{ - struct snd_usb_midi_out_endpoint* ep; - unsigned int i; - unsigned int pipe; - void* buffer; - - rep->out = NULL; - ep = kzalloc(sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - ep->umidi = umidi; - - for (i = 0; i < OUTPUT_URBS; ++i) { - ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ep->urbs[i].urb) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; - } - ep->urbs[i].ep = ep; - } - if (ep_info->out_interval) - pipe = usb_sndintpipe(umidi->dev, ep_info->out_ep); - else - pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep); - switch (umidi->usb_id) { - default: - ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1); - break; - /* - * Various chips declare a packet size larger than 4 bytes, but - * do not actually work with larger packets: - */ - case USB_ID(0x0a92, 0x1020): /* ESI M4U */ - case USB_ID(0x1430, 0x474b): /* RedOctane GH MIDI INTERFACE */ - case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */ - case USB_ID(0x15ca, 0x1806): /* Textech USB Midi Cable */ - case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */ - ep->max_transfer = 4; - break; - } - for (i = 0; i < OUTPUT_URBS; ++i) { - buffer = usb_buffer_alloc(umidi->dev, - ep->max_transfer, GFP_KERNEL, - &ep->urbs[i].urb->transfer_dma); - if (!buffer) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; - } - if (ep_info->out_interval) - usb_fill_int_urb(ep->urbs[i].urb, umidi->dev, - pipe, buffer, ep->max_transfer, - snd_usbmidi_out_urb_complete, - &ep->urbs[i], ep_info->out_interval); - else - usb_fill_bulk_urb(ep->urbs[i].urb, umidi->dev, - pipe, buffer, ep->max_transfer, - snd_usbmidi_out_urb_complete, - &ep->urbs[i]); - ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - } - - spin_lock_init(&ep->buffer_lock); - tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); - init_waitqueue_head(&ep->drain_wait); - - for (i = 0; i < 0x10; ++i) - if (ep_info->out_cables & (1 << i)) { - ep->ports[i].ep = ep; - ep->ports[i].cable = i << 4; - } - - if (umidi->usb_protocol_ops->init_out_endpoint) - umidi->usb_protocol_ops->init_out_endpoint(ep); - - rep->out = ep; - return 0; -} - -/* - * Frees everything. - */ -static void snd_usbmidi_free(struct snd_usb_midi* umidi) -{ - int i; - - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; - if (ep->out) - snd_usbmidi_out_endpoint_delete(ep->out); - if (ep->in) - snd_usbmidi_in_endpoint_delete(ep->in); - } - mutex_destroy(&umidi->mutex); - kfree(umidi); -} - -/* - * Unlinks all URBs (must be done before the usb_device is deleted). - */ -void snd_usbmidi_disconnect(struct list_head* p) -{ - struct snd_usb_midi* umidi; - unsigned int i, j; - - umidi = list_entry(p, struct snd_usb_midi, list); - /* - * an URB's completion handler may start the timer and - * a timer may submit an URB. To reliably break the cycle - * a flag under lock must be used - */ - spin_lock_irq(&umidi->disc_lock); - umidi->disconnected = 1; - spin_unlock_irq(&umidi->disc_lock); - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; - if (ep->out) - tasklet_kill(&ep->out->tasklet); - if (ep->out) { - for (j = 0; j < OUTPUT_URBS; ++j) - usb_kill_urb(ep->out->urbs[j].urb); - if (umidi->usb_protocol_ops->finish_out_endpoint) - umidi->usb_protocol_ops->finish_out_endpoint(ep->out); - } - if (ep->in) - for (j = 0; j < INPUT_URBS; ++j) - usb_kill_urb(ep->in->urbs[j]); - /* free endpoints here; later call can result in Oops */ - if (ep->out) { - snd_usbmidi_out_endpoint_delete(ep->out); - ep->out = NULL; - } - if (ep->in) { - snd_usbmidi_in_endpoint_delete(ep->in); - ep->in = NULL; - } - } - del_timer_sync(&umidi->error_timer); -} - -static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi) -{ - struct snd_usb_midi* umidi = rmidi->private_data; - snd_usbmidi_free(umidi); -} - -static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi, - int stream, int number) -{ - struct list_head* list; - - list_for_each(list, &umidi->rmidi->streams[stream].substreams) { - struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list); - if (substream->number == number) - return substream; - } - return NULL; -} - -/* - * This list specifies names for ports that do not fit into the standard - * "(product) MIDI (n)" schema because they aren't external MIDI ports, - * such as internal control or synthesizer ports. - */ -static struct port_info { - u32 id; - short int port; - short int voices; - const char *name; - unsigned int seq_flags; -} snd_usbmidi_port_info[] = { -#define PORT_INFO(vendor, product, num, name_, voices_, flags) \ - { .id = USB_ID(vendor, product), \ - .port = num, .voices = voices_, \ - .name = name_, .seq_flags = flags } -#define EXTERNAL_PORT(vendor, product, num, name) \ - PORT_INFO(vendor, product, num, name, 0, \ - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ - SNDRV_SEQ_PORT_TYPE_HARDWARE | \ - SNDRV_SEQ_PORT_TYPE_PORT) -#define CONTROL_PORT(vendor, product, num, name) \ - PORT_INFO(vendor, product, num, name, 0, \ - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ - SNDRV_SEQ_PORT_TYPE_HARDWARE) -#define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \ - PORT_INFO(vendor, product, num, name, voices, \ - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ - SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ - SNDRV_SEQ_PORT_TYPE_HARDWARE | \ - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) -#define SOUNDCANVAS_PORT(vendor, product, num, name, voices) \ - PORT_INFO(vendor, product, num, name, voices, \ - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ - SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ - SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ - SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \ - SNDRV_SEQ_PORT_TYPE_HARDWARE | \ - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) - /* Roland UA-100 */ - CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"), - /* Roland SC-8850 */ - SOUNDCANVAS_PORT(0x0582, 0x0003, 0, "%s Part A", 128), - SOUNDCANVAS_PORT(0x0582, 0x0003, 1, "%s Part B", 128), - SOUNDCANVAS_PORT(0x0582, 0x0003, 2, "%s Part C", 128), - SOUNDCANVAS_PORT(0x0582, 0x0003, 3, "%s Part D", 128), - EXTERNAL_PORT(0x0582, 0x0003, 4, "%s MIDI 1"), - EXTERNAL_PORT(0x0582, 0x0003, 5, "%s MIDI 2"), - /* Roland U-8 */ - EXTERNAL_PORT(0x0582, 0x0004, 0, "%s MIDI"), - CONTROL_PORT(0x0582, 0x0004, 1, "%s Control"), - /* Roland SC-8820 */ - SOUNDCANVAS_PORT(0x0582, 0x0007, 0, "%s Part A", 64), - SOUNDCANVAS_PORT(0x0582, 0x0007, 1, "%s Part B", 64), - EXTERNAL_PORT(0x0582, 0x0007, 2, "%s MIDI"), - /* Roland SK-500 */ - SOUNDCANVAS_PORT(0x0582, 0x000b, 0, "%s Part A", 64), - SOUNDCANVAS_PORT(0x0582, 0x000b, 1, "%s Part B", 64), - EXTERNAL_PORT(0x0582, 0x000b, 2, "%s MIDI"), - /* Roland SC-D70 */ - SOUNDCANVAS_PORT(0x0582, 0x000c, 0, "%s Part A", 64), - SOUNDCANVAS_PORT(0x0582, 0x000c, 1, "%s Part B", 64), - EXTERNAL_PORT(0x0582, 0x000c, 2, "%s MIDI"), - /* Edirol UM-880 */ - CONTROL_PORT(0x0582, 0x0014, 8, "%s Control"), - /* Edirol SD-90 */ - ROLAND_SYNTH_PORT(0x0582, 0x0016, 0, "%s Part A", 128), - ROLAND_SYNTH_PORT(0x0582, 0x0016, 1, "%s Part B", 128), - EXTERNAL_PORT(0x0582, 0x0016, 2, "%s MIDI 1"), - EXTERNAL_PORT(0x0582, 0x0016, 3, "%s MIDI 2"), - /* Edirol UM-550 */ - CONTROL_PORT(0x0582, 0x0023, 5, "%s Control"), - /* Edirol SD-20 */ - ROLAND_SYNTH_PORT(0x0582, 0x0027, 0, "%s Part A", 64), - ROLAND_SYNTH_PORT(0x0582, 0x0027, 1, "%s Part B", 64), - EXTERNAL_PORT(0x0582, 0x0027, 2, "%s MIDI"), - /* Edirol SD-80 */ - ROLAND_SYNTH_PORT(0x0582, 0x0029, 0, "%s Part A", 128), - ROLAND_SYNTH_PORT(0x0582, 0x0029, 1, "%s Part B", 128), - EXTERNAL_PORT(0x0582, 0x0029, 2, "%s MIDI 1"), - EXTERNAL_PORT(0x0582, 0x0029, 3, "%s MIDI 2"), - /* Edirol UA-700 */ - EXTERNAL_PORT(0x0582, 0x002b, 0, "%s MIDI"), - CONTROL_PORT(0x0582, 0x002b, 1, "%s Control"), - /* Roland VariOS */ - EXTERNAL_PORT(0x0582, 0x002f, 0, "%s MIDI"), - EXTERNAL_PORT(0x0582, 0x002f, 1, "%s External MIDI"), - EXTERNAL_PORT(0x0582, 0x002f, 2, "%s Sync"), - /* Edirol PCR */ - EXTERNAL_PORT(0x0582, 0x0033, 0, "%s MIDI"), - EXTERNAL_PORT(0x0582, 0x0033, 1, "%s 1"), - EXTERNAL_PORT(0x0582, 0x0033, 2, "%s 2"), - /* BOSS GS-10 */ - EXTERNAL_PORT(0x0582, 0x003b, 0, "%s MIDI"), - CONTROL_PORT(0x0582, 0x003b, 1, "%s Control"), - /* Edirol UA-1000 */ - EXTERNAL_PORT(0x0582, 0x0044, 0, "%s MIDI"), - CONTROL_PORT(0x0582, 0x0044, 1, "%s Control"), - /* Edirol UR-80 */ - EXTERNAL_PORT(0x0582, 0x0048, 0, "%s MIDI"), - EXTERNAL_PORT(0x0582, 0x0048, 1, "%s 1"), - EXTERNAL_PORT(0x0582, 0x0048, 2, "%s 2"), - /* Edirol PCR-A */ - EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"), - EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"), - EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"), - /* Edirol UM-3EX */ - CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"), - /* M-Audio MidiSport 8x8 */ - CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"), - CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"), - /* MOTU Fastlane */ - EXTERNAL_PORT(0x07fd, 0x0001, 0, "%s MIDI A"), - EXTERNAL_PORT(0x07fd, 0x0001, 1, "%s MIDI B"), - /* Emagic Unitor8/AMT8/MT4 */ - EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"), - EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"), - EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"), - /* Access Music Virus TI */ - EXTERNAL_PORT(0x133e, 0x0815, 0, "%s MIDI"), - PORT_INFO(0x133e, 0x0815, 1, "%s Synth", 0, - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | - SNDRV_SEQ_PORT_TYPE_HARDWARE | - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER), -}; - -static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) { - if (snd_usbmidi_port_info[i].id == umidi->usb_id && - snd_usbmidi_port_info[i].port == number) - return &snd_usbmidi_port_info[i]; - } - return NULL; -} - -static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number, - struct snd_seq_port_info *seq_port_info) -{ - struct snd_usb_midi *umidi = rmidi->private_data; - struct port_info *port_info; - - /* TODO: read port flags from descriptors */ - port_info = find_port_info(umidi, number); - if (port_info) { - seq_port_info->type = port_info->seq_flags; - seq_port_info->midi_voices = port_info->voices; - } -} - -static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi, - int stream, int number, - struct snd_rawmidi_substream ** rsubstream) -{ - struct port_info *port_info; - const char *name_format; - - struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number); - if (!substream) { - snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); - return; - } - - /* TODO: read port name from jack descriptor */ - port_info = find_port_info(umidi, number); - name_format = port_info ? port_info->name : "%s MIDI %d"; - snprintf(substream->name, sizeof(substream->name), - name_format, umidi->card->shortname, number + 1); - - *rsubstream = substream; -} - -/* - * Creates the endpoints and their ports. - */ -static int snd_usbmidi_create_endpoints(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoints) -{ - int i, j, err; - int out_ports = 0, in_ports = 0; - - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - if (endpoints[i].out_cables) { - err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i], - &umidi->endpoints[i]); - if (err < 0) - return err; - } - if (endpoints[i].in_cables) { - err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i], - &umidi->endpoints[i]); - if (err < 0) - return err; - } - - for (j = 0; j < 0x10; ++j) { - if (endpoints[i].out_cables & (1 << j)) { - snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, out_ports, - &umidi->endpoints[i].out->ports[j].substream); - ++out_ports; - } - if (endpoints[i].in_cables & (1 << j)) { - snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, in_ports, - &umidi->endpoints[i].in->ports[j].substream); - ++in_ports; - } - } - } - snd_printdd(KERN_INFO "created %d output and %d input ports\n", - out_ports, in_ports); - return 0; -} - -/* - * Returns MIDIStreaming device capabilities. - */ -static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoints) -{ - struct usb_interface* intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor* intfd; - struct usb_ms_header_descriptor* ms_header; - struct usb_host_endpoint *hostep; - struct usb_endpoint_descriptor* ep; - struct usb_ms_endpoint_descriptor* ms_ep; - int i, epidx; - - intf = umidi->iface; - if (!intf) - return -ENXIO; - hostif = &intf->altsetting[0]; - intfd = get_iface_desc(hostif); - ms_header = (struct usb_ms_header_descriptor*)hostif->extra; - if (hostif->extralen >= 7 && - ms_header->bLength >= 7 && - ms_header->bDescriptorType == USB_DT_CS_INTERFACE && - ms_header->bDescriptorSubtype == UAC_HEADER) - snd_printdd(KERN_INFO "MIDIStreaming version %02x.%02x\n", - ms_header->bcdMSC[1], ms_header->bcdMSC[0]); - else - snd_printk(KERN_WARNING "MIDIStreaming interface descriptor not found\n"); - - epidx = 0; - for (i = 0; i < intfd->bNumEndpoints; ++i) { - hostep = &hostif->endpoint[i]; - ep = get_ep_desc(hostep); - if (!usb_endpoint_xfer_bulk(ep) && !usb_endpoint_xfer_int(ep)) - continue; - ms_ep = (struct usb_ms_endpoint_descriptor*)hostep->extra; - if (hostep->extralen < 4 || - ms_ep->bLength < 4 || - ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || - ms_ep->bDescriptorSubtype != UAC_MS_GENERAL) - continue; - if (usb_endpoint_dir_out(ep)) { - if (endpoints[epidx].out_ep) { - if (++epidx >= MIDI_MAX_ENDPOINTS) { - snd_printk(KERN_WARNING "too many endpoints\n"); - break; - } - } - endpoints[epidx].out_ep = usb_endpoint_num(ep); - if (usb_endpoint_xfer_int(ep)) - endpoints[epidx].out_interval = ep->bInterval; - else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW) - /* - * Low speed bulk transfers don't exist, so - * force interrupt transfers for devices like - * ESI MIDI Mate that try to use them anyway. - */ - endpoints[epidx].out_interval = 1; - endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; - snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", - ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); - } else { - if (endpoints[epidx].in_ep) { - if (++epidx >= MIDI_MAX_ENDPOINTS) { - snd_printk(KERN_WARNING "too many endpoints\n"); - break; - } - } - endpoints[epidx].in_ep = usb_endpoint_num(ep); - if (usb_endpoint_xfer_int(ep)) - endpoints[epidx].in_interval = ep->bInterval; - else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW) - endpoints[epidx].in_interval = 1; - endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; - snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", - ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); - } - } - return 0; -} - -static int roland_load_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - static const char *const names[] = { "High Load", "Light Load" }; - - info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - info->count = 1; - info->value.enumerated.items = 2; - if (info->value.enumerated.item > 1) - info->value.enumerated.item = 1; - strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); - return 0; -} - -static int roland_load_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - value->value.enumerated.item[0] = kcontrol->private_value; - return 0; -} - -static int roland_load_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct snd_usb_midi* umidi = kcontrol->private_data; - int changed; - - if (value->value.enumerated.item[0] > 1) - return -EINVAL; - mutex_lock(&umidi->mutex); - changed = value->value.enumerated.item[0] != kcontrol->private_value; - if (changed) - kcontrol->private_value = value->value.enumerated.item[0]; - mutex_unlock(&umidi->mutex); - return changed; -} - -static struct snd_kcontrol_new roland_load_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "MIDI Input Mode", - .info = roland_load_info, - .get = roland_load_get, - .put = roland_load_put, - .private_value = 1, -}; - -/* - * On Roland devices, use the second alternate setting to be able to use - * the interrupt input endpoint. - */ -static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi) -{ - struct usb_interface* intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor* intfd; - - intf = umidi->iface; - if (!intf || intf->num_altsetting != 2) - return; - - hostif = &intf->altsetting[1]; - intfd = get_iface_desc(hostif); - if (intfd->bNumEndpoints != 2 || - (get_endpoint(hostif, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK || - (get_endpoint(hostif, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) - return; - - snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n", - intfd->bAlternateSetting); - usb_set_interface(umidi->dev, intfd->bInterfaceNumber, - intfd->bAlternateSetting); - - umidi->roland_load_ctl = snd_ctl_new1(&roland_load_ctl, umidi); - if (snd_ctl_add(umidi->card, umidi->roland_load_ctl) < 0) - umidi->roland_load_ctl = NULL; -} - -/* - * Try to find any usable endpoints in the interface. - */ -static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoint, - int max_endpoints) -{ - struct usb_interface* intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor* intfd; - struct usb_endpoint_descriptor* epd; - int i, out_eps = 0, in_eps = 0; - - if (USB_ID_VENDOR(umidi->usb_id) == 0x0582) - snd_usbmidi_switch_roland_altsetting(umidi); - - if (endpoint[0].out_ep || endpoint[0].in_ep) - return 0; - - intf = umidi->iface; - if (!intf || intf->num_altsetting < 1) - return -ENOENT; - hostif = intf->cur_altsetting; - intfd = get_iface_desc(hostif); - - for (i = 0; i < intfd->bNumEndpoints; ++i) { - epd = get_endpoint(hostif, i); - if (!usb_endpoint_xfer_bulk(epd) && - !usb_endpoint_xfer_int(epd)) - continue; - if (out_eps < max_endpoints && - usb_endpoint_dir_out(epd)) { - endpoint[out_eps].out_ep = usb_endpoint_num(epd); - if (usb_endpoint_xfer_int(epd)) - endpoint[out_eps].out_interval = epd->bInterval; - ++out_eps; - } - if (in_eps < max_endpoints && - usb_endpoint_dir_in(epd)) { - endpoint[in_eps].in_ep = usb_endpoint_num(epd); - if (usb_endpoint_xfer_int(epd)) - endpoint[in_eps].in_interval = epd->bInterval; - ++in_eps; - } - } - return (out_eps || in_eps) ? 0 : -ENOENT; -} - -/* - * Detects the endpoints for one-port-per-endpoint protocols. - */ -static int snd_usbmidi_detect_per_port_endpoints(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoints) -{ - int err, i; - - err = snd_usbmidi_detect_endpoints(umidi, endpoints, MIDI_MAX_ENDPOINTS); - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - if (endpoints[i].out_ep) - endpoints[i].out_cables = 0x0001; - if (endpoints[i].in_ep) - endpoints[i].in_cables = 0x0001; - } - return err; -} - -/* - * Detects the endpoints and ports of Yamaha devices. - */ -static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoint) -{ - struct usb_interface* intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor* intfd; - uint8_t* cs_desc; - - intf = umidi->iface; - if (!intf) - return -ENOENT; - hostif = intf->altsetting; - intfd = get_iface_desc(hostif); - if (intfd->bNumEndpoints < 1) - return -ENOENT; - - /* - * For each port there is one MIDI_IN/OUT_JACK descriptor, not - * necessarily with any useful contents. So simply count 'em. - */ - for (cs_desc = hostif->extra; - cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; - cs_desc += cs_desc[0]) { - if (cs_desc[1] == USB_DT_CS_INTERFACE) { - if (cs_desc[2] == UAC_MIDI_IN_JACK) - endpoint->in_cables = (endpoint->in_cables << 1) | 1; - else if (cs_desc[2] == UAC_MIDI_OUT_JACK) - endpoint->out_cables = (endpoint->out_cables << 1) | 1; - } - } - if (!endpoint->in_cables && !endpoint->out_cables) - return -ENOENT; - - return snd_usbmidi_detect_endpoints(umidi, endpoint, 1); -} - -/* - * Creates the endpoints and their ports for Midiman devices. - */ -static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi, - struct snd_usb_midi_endpoint_info* endpoint) -{ - struct snd_usb_midi_endpoint_info ep_info; - struct usb_interface* intf; - struct usb_host_interface *hostif; - struct usb_interface_descriptor* intfd; - struct usb_endpoint_descriptor* epd; - int cable, err; - - intf = umidi->iface; - if (!intf) - return -ENOENT; - hostif = intf->altsetting; - intfd = get_iface_desc(hostif); - /* - * The various MidiSport devices have more or less random endpoint - * numbers, so we have to identify the endpoints by their index in - * the descriptor array, like the driver for that other OS does. - * - * There is one interrupt input endpoint for all input ports, one - * bulk output endpoint for even-numbered ports, and one for odd- - * numbered ports. Both bulk output endpoints have corresponding - * input bulk endpoints (at indices 1 and 3) which aren't used. - */ - if (intfd->bNumEndpoints < (endpoint->out_cables > 0x0001 ? 5 : 3)) { - snd_printdd(KERN_ERR "not enough endpoints\n"); - return -ENOENT; - } - - epd = get_endpoint(hostif, 0); - if (!usb_endpoint_dir_in(epd) || !usb_endpoint_xfer_int(epd)) { - snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n"); - return -ENXIO; - } - epd = get_endpoint(hostif, 2); - if (!usb_endpoint_dir_out(epd) || !usb_endpoint_xfer_bulk(epd)) { - snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n"); - return -ENXIO; - } - if (endpoint->out_cables > 0x0001) { - epd = get_endpoint(hostif, 4); - if (!usb_endpoint_dir_out(epd) || - !usb_endpoint_xfer_bulk(epd)) { - snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n"); - return -ENXIO; - } - } - - ep_info.out_ep = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - ep_info.out_interval = 0; - ep_info.out_cables = endpoint->out_cables & 0x5555; - err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); - if (err < 0) - return err; - - ep_info.in_ep = get_endpoint(hostif, 0)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - ep_info.in_interval = get_endpoint(hostif, 0)->bInterval; - ep_info.in_cables = endpoint->in_cables; - err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); - if (err < 0) - return err; - - if (endpoint->out_cables > 0x0001) { - ep_info.out_ep = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - ep_info.out_cables = endpoint->out_cables & 0xaaaa; - err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]); - if (err < 0) - return err; - } - - for (cable = 0; cable < 0x10; ++cable) { - if (endpoint->out_cables & (1 << cable)) - snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, cable, - &umidi->endpoints[cable & 1].out->ports[cable].substream); - if (endpoint->in_cables & (1 << cable)) - snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, cable, - &umidi->endpoints[0].in->ports[cable].substream); - } - return 0; -} - -static struct snd_rawmidi_global_ops snd_usbmidi_ops = { - .get_port_info = snd_usbmidi_get_port_info, -}; - -static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi, - int out_ports, int in_ports) -{ - struct snd_rawmidi *rmidi; - int err; - - err = snd_rawmidi_new(umidi->card, "USB MIDI", - umidi->next_midi_device++, - out_ports, in_ports, &rmidi); - if (err < 0) - return err; - strcpy(rmidi->name, umidi->card->shortname); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->ops = &snd_usbmidi_ops; - rmidi->private_data = umidi; - rmidi->private_free = snd_usbmidi_rawmidi_free; - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input_ops); - - umidi->rmidi = rmidi; - return 0; -} - -/* - * Temporarily stop input. - */ -void snd_usbmidi_input_stop(struct list_head* p) -{ - struct snd_usb_midi* umidi; - unsigned int i, j; - - umidi = list_entry(p, struct snd_usb_midi, list); - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; - if (ep->in) - for (j = 0; j < INPUT_URBS; ++j) - usb_kill_urb(ep->in->urbs[j]); - } -} - -static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep) -{ - unsigned int i; - - if (!ep) - return; - for (i = 0; i < INPUT_URBS; ++i) { - struct urb* urb = ep->urbs[i]; - urb->dev = ep->umidi->dev; - snd_usbmidi_submit_urb(urb, GFP_KERNEL); - } -} - -/* - * Resume input after a call to snd_usbmidi_input_stop(). - */ -void snd_usbmidi_input_start(struct list_head* p) -{ - struct snd_usb_midi* umidi; - int i; - - umidi = list_entry(p, struct snd_usb_midi, list); - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - snd_usbmidi_input_start_ep(umidi->endpoints[i].in); -} - -/* - * Creates and registers everything needed for a MIDI streaming interface. - */ -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface* iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk* quirk) -{ - struct snd_usb_midi* umidi; - struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; - int out_ports, in_ports; - int i, err; - - umidi = kzalloc(sizeof(*umidi), GFP_KERNEL); - if (!umidi) - return -ENOMEM; - umidi->dev = interface_to_usbdev(iface); - umidi->card = card; - umidi->iface = iface; - umidi->quirk = quirk; - umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; - init_timer(&umidi->error_timer); - spin_lock_init(&umidi->disc_lock); - mutex_init(&umidi->mutex); - umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), - le16_to_cpu(umidi->dev->descriptor.idProduct)); - umidi->error_timer.function = snd_usbmidi_error_timer; - umidi->error_timer.data = (unsigned long)umidi; - - /* detect the endpoint(s) to use */ - memset(endpoints, 0, sizeof(endpoints)); - switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) { - case QUIRK_MIDI_STANDARD_INTERFACE: - err = snd_usbmidi_get_ms_info(umidi, endpoints); - if (umidi->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */ - umidi->usb_protocol_ops = - &snd_usbmidi_maudio_broken_running_status_ops; - break; - case QUIRK_MIDI_US122L: - umidi->usb_protocol_ops = &snd_usbmidi_122l_ops; - /* fall through */ - case QUIRK_MIDI_FIXED_ENDPOINT: - memcpy(&endpoints[0], quirk->data, - sizeof(struct snd_usb_midi_endpoint_info)); - err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); - break; - case QUIRK_MIDI_YAMAHA: - err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); - break; - case QUIRK_MIDI_MIDIMAN: - umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops; - memcpy(&endpoints[0], quirk->data, - sizeof(struct snd_usb_midi_endpoint_info)); - err = 0; - break; - case QUIRK_MIDI_NOVATION: - umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - case QUIRK_MIDI_FASTLANE: - umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; - /* - * Interface 1 contains isochronous endpoints, but with the same - * numbers as in interface 0. Since it is interface 1 that the - * USB core has most recently seen, these descriptors are now - * associated with the endpoint numbers. This will foul up our - * attempts to submit bulk/interrupt URBs to the endpoints in - * interface 0, so we have to make sure that the USB core looks - * again at interface 0 by calling usb_set_interface() on it. - */ - usb_set_interface(umidi->dev, 0, 0); - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - case QUIRK_MIDI_EMAGIC: - umidi->usb_protocol_ops = &snd_usbmidi_emagic_ops; - memcpy(&endpoints[0], quirk->data, - sizeof(struct snd_usb_midi_endpoint_info)); - err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); - break; - case QUIRK_MIDI_CME: - umidi->usb_protocol_ops = &snd_usbmidi_cme_ops; - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - default: - snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); - err = -ENXIO; - break; - } - if (err < 0) { - kfree(umidi); - return err; - } - - /* create rawmidi device */ - out_ports = 0; - in_ports = 0; - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - out_ports += hweight16(endpoints[i].out_cables); - in_ports += hweight16(endpoints[i].in_cables); - } - err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports); - if (err < 0) { - kfree(umidi); - return err; - } - - /* create endpoint/port structures */ - if (quirk && quirk->type == QUIRK_MIDI_MIDIMAN) - err = snd_usbmidi_create_endpoints_midiman(umidi, &endpoints[0]); - else - err = snd_usbmidi_create_endpoints(umidi, endpoints); - if (err < 0) { - snd_usbmidi_free(umidi); - return err; - } - - list_add_tail(&umidi->list, midi_list); - - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - snd_usbmidi_input_start_ep(umidi->endpoints[i].in); - return 0; -} - -EXPORT_SYMBOL(snd_usbmidi_create); -EXPORT_SYMBOL(snd_usbmidi_input_stop); -EXPORT_SYMBOL(snd_usbmidi_input_start); -EXPORT_SYMBOL(snd_usbmidi_disconnect); diff --git a/sound/usb/usbmidi.h b/sound/usb/usbmidi.h deleted file mode 100644 index 2089ec9..0000000 --- a/sound/usb/usbmidi.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef __USBMIDI_H -#define __USBMIDI_H - -/* maximum number of endpoints per interface */ -#define MIDI_MAX_ENDPOINTS 2 - -/* data for QUIRK_MIDI_FIXED_ENDPOINT */ -struct snd_usb_midi_endpoint_info { - int8_t out_ep; /* ep number, 0 autodetect */ - uint8_t out_interval; /* interval for interrupt endpoints */ - int8_t in_ep; - uint8_t in_interval; - uint16_t out_cables; /* bitmask */ - uint16_t in_cables; /* bitmask */ -}; - -/* for QUIRK_MIDI_YAMAHA, data is NULL */ - -/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info - * structure (out_cables and in_cables only) */ - -/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk - * structures, terminated with .ifnum = -1 */ - -/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ - -/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */ - -/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */ - -/* for QUIRK_IGNORE_INTERFACE, data is NULL */ - -/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */ - -/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info - * structure (out_cables and in_cables only) */ - -/* for QUIRK_MIDI_CME, data is NULL */ - -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface *iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk); -void snd_usbmidi_input_stop(struct list_head* p); -void snd_usbmidi_input_start(struct list_head* p); -void snd_usbmidi_disconnect(struct list_head *p); - -#endif /* __USBMIDI_H */ diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 43d53a3..5c05683 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -42,6 +42,7 @@ #include "usbaudio.h" #include "usbmixer.h" +#include "helper.h" /* */ diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h deleted file mode 100644 index 2b426c1..0000000 --- a/sound/usb/usbquirks.h +++ /dev/null @@ -1,2248 +0,0 @@ -/* - * ALSA USB Audio Driver - * - * Copyright (c) 2002 by Takashi Iwai , - * Clemens Ladisch - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * The contents of this file are part of the driver's id_table. - * - * In a perfect world, this file would be empty. - */ - -/* - * Use this for devices where other interfaces are standard compliant, - * to prevent the quirk being applied to those interfaces. (To work with - * hotplugging, bDeviceClass must be set to USB_CLASS_PER_INTERFACE.) - */ -#define USB_DEVICE_VENDOR_SPEC(vend, prod) \ - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \ - USB_DEVICE_ID_MATCH_PRODUCT | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = vend, \ - .idProduct = prod, \ - .bInterfaceClass = USB_CLASS_VENDOR_SPEC - -/* Creative/Toshiba Multimedia Center SB-0500 */ -{ - USB_DEVICE(0x041e, 0x3048), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Toshiba", - .product_name = "SB-0500", - .ifnum = QUIRK_NO_INTERFACE - } -}, - -/* Creative/E-Mu devices */ -{ - USB_DEVICE(0x041e, 0x3010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Creative Labs", - .product_name = "Sound Blaster MP3+", - .ifnum = QUIRK_NO_INTERFACE - } -}, -{ - /* E-Mu 0202 USB */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f02, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - /* E-Mu 0404 USB */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f04, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - /* E-Mu Tracker Pre */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f0a, - .bInterfaceClass = USB_CLASS_AUDIO, -}, - -/* - * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface - * class matches do not take effect without an explicit ID match. - */ -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x0850, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08ae, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08c6, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f5, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f6, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - USB_DEVICE(0x046d, 0x0990), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Logitech, Inc.", - .product_name = "QuickCam Pro 9000", - .ifnum = QUIRK_NO_INTERFACE - } -}, - -/* - * Yamaha devices - */ - -#define YAMAHA_DEVICE(id, name) { \ - USB_DEVICE(0x0499, id), \ - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ - .vendor_name = "Yamaha", \ - .product_name = name, \ - .ifnum = QUIRK_ANY_INTERFACE, \ - .type = QUIRK_MIDI_YAMAHA \ - } \ -} -#define YAMAHA_INTERFACE(id, intf, name) { \ - USB_DEVICE_VENDOR_SPEC(0x0499, id), \ - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ - .vendor_name = "Yamaha", \ - .product_name = name, \ - .ifnum = intf, \ - .type = QUIRK_MIDI_YAMAHA \ - } \ -} -YAMAHA_DEVICE(0x1000, "UX256"), -YAMAHA_DEVICE(0x1001, "MU1000"), -YAMAHA_DEVICE(0x1002, "MU2000"), -YAMAHA_DEVICE(0x1003, "MU500"), -YAMAHA_INTERFACE(0x1004, 3, "UW500"), -YAMAHA_DEVICE(0x1005, "MOTIF6"), -YAMAHA_DEVICE(0x1006, "MOTIF7"), -YAMAHA_DEVICE(0x1007, "MOTIF8"), -YAMAHA_DEVICE(0x1008, "UX96"), -YAMAHA_DEVICE(0x1009, "UX16"), -YAMAHA_INTERFACE(0x100a, 3, "EOS BX"), -YAMAHA_DEVICE(0x100c, "UC-MX"), -YAMAHA_DEVICE(0x100d, "UC-KX"), -YAMAHA_DEVICE(0x100e, "S08"), -YAMAHA_DEVICE(0x100f, "CLP-150"), -YAMAHA_DEVICE(0x1010, "CLP-170"), -YAMAHA_DEVICE(0x1011, "P-250"), -YAMAHA_DEVICE(0x1012, "TYROS"), -YAMAHA_DEVICE(0x1013, "PF-500"), -YAMAHA_DEVICE(0x1014, "S90"), -YAMAHA_DEVICE(0x1015, "MOTIF-R"), -YAMAHA_DEVICE(0x1016, "MDP-5"), -YAMAHA_DEVICE(0x1017, "CVP-204"), -YAMAHA_DEVICE(0x1018, "CVP-206"), -YAMAHA_DEVICE(0x1019, "CVP-208"), -YAMAHA_DEVICE(0x101a, "CVP-210"), -YAMAHA_DEVICE(0x101b, "PSR-1100"), -YAMAHA_DEVICE(0x101c, "PSR-2100"), -YAMAHA_DEVICE(0x101d, "CLP-175"), -YAMAHA_DEVICE(0x101e, "PSR-K1"), -YAMAHA_DEVICE(0x101f, "EZ-J24"), -YAMAHA_DEVICE(0x1020, "EZ-250i"), -YAMAHA_DEVICE(0x1021, "MOTIF ES 6"), -YAMAHA_DEVICE(0x1022, "MOTIF ES 7"), -YAMAHA_DEVICE(0x1023, "MOTIF ES 8"), -YAMAHA_DEVICE(0x1024, "CVP-301"), -YAMAHA_DEVICE(0x1025, "CVP-303"), -YAMAHA_DEVICE(0x1026, "CVP-305"), -YAMAHA_DEVICE(0x1027, "CVP-307"), -YAMAHA_DEVICE(0x1028, "CVP-309"), -YAMAHA_DEVICE(0x1029, "CVP-309GP"), -YAMAHA_DEVICE(0x102a, "PSR-1500"), -YAMAHA_DEVICE(0x102b, "PSR-3000"), -YAMAHA_DEVICE(0x102e, "ELS-01/01C"), -YAMAHA_DEVICE(0x1030, "PSR-295/293"), -YAMAHA_DEVICE(0x1031, "DGX-205/203"), -YAMAHA_DEVICE(0x1032, "DGX-305"), -YAMAHA_DEVICE(0x1033, "DGX-505"), -YAMAHA_DEVICE(0x1034, NULL), -YAMAHA_DEVICE(0x1035, NULL), -YAMAHA_DEVICE(0x1036, NULL), -YAMAHA_DEVICE(0x1037, NULL), -YAMAHA_DEVICE(0x1038, NULL), -YAMAHA_DEVICE(0x1039, NULL), -YAMAHA_DEVICE(0x103a, NULL), -YAMAHA_DEVICE(0x103b, NULL), -YAMAHA_DEVICE(0x103c, NULL), -YAMAHA_DEVICE(0x103d, NULL), -YAMAHA_DEVICE(0x103e, NULL), -YAMAHA_DEVICE(0x103f, NULL), -YAMAHA_DEVICE(0x1040, NULL), -YAMAHA_DEVICE(0x1041, NULL), -YAMAHA_DEVICE(0x1042, NULL), -YAMAHA_DEVICE(0x1043, NULL), -YAMAHA_DEVICE(0x1044, NULL), -YAMAHA_DEVICE(0x1045, NULL), -YAMAHA_INTERFACE(0x104e, 0, NULL), -YAMAHA_DEVICE(0x104f, NULL), -YAMAHA_DEVICE(0x1050, NULL), -YAMAHA_DEVICE(0x1051, NULL), -YAMAHA_DEVICE(0x1052, NULL), -YAMAHA_DEVICE(0x2000, "DGP-7"), -YAMAHA_DEVICE(0x2001, "DGP-5"), -YAMAHA_DEVICE(0x2002, NULL), -YAMAHA_DEVICE(0x5000, "CS1D"), -YAMAHA_DEVICE(0x5001, "DSP1D"), -YAMAHA_DEVICE(0x5002, "DME32"), -YAMAHA_DEVICE(0x5003, "DM2000"), -YAMAHA_DEVICE(0x5004, "02R96"), -YAMAHA_DEVICE(0x5005, "ACU16-C"), -YAMAHA_DEVICE(0x5006, "NHB32-C"), -YAMAHA_DEVICE(0x5007, "DM1000"), -YAMAHA_DEVICE(0x5008, "01V96"), -YAMAHA_DEVICE(0x5009, "SPX2000"), -YAMAHA_DEVICE(0x500a, "PM5D"), -YAMAHA_DEVICE(0x500b, "DME64N"), -YAMAHA_DEVICE(0x500c, "DME24N"), -YAMAHA_DEVICE(0x500d, NULL), -YAMAHA_DEVICE(0x500e, NULL), -YAMAHA_DEVICE(0x500f, NULL), -YAMAHA_DEVICE(0x7000, "DTX"), -YAMAHA_DEVICE(0x7010, "UB99"), -#undef YAMAHA_DEVICE -#undef YAMAHA_INTERFACE - -/* - * Roland/RolandED/Edirol/BOSS devices - */ -{ - USB_DEVICE(0x0582, 0x0000), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "UA-100", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S16_LE, - .channels = 4, - .iface = 0, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x01, - .ep_attr = 0x09, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S16_LE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = UAC_EP_CS_ATTR_FILL_MAX, - .endpoint = 0x81, - .ep_attr = 0x05, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0007, - .in_cables = 0x0007 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0002), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-4", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0003), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SC-8850", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x003f, - .in_cables = 0x003f - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0004), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "U-8", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0005, - .in_cables = 0x0005 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Has ID 0x0099 when not in "Advanced Driver" mode. - * The UM-2EX has only one input, but we cannot detect this. */ - USB_DEVICE(0x0582, 0x0005), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-2", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0007), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SC-8820", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0013, - .in_cables = 0x0013 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0008), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "PC-300", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x009d when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0009), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-1", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x000b), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SK-500", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0013, - .in_cables = 0x0013 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* thanks to Emiliano Grilli - * for helping researching this data */ - USB_DEVICE(0x0582, 0x000c), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SC-D70", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, - .channels = 2, - .iface = 0, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x01, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x81, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0007, - .in_cables = 0x0007 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ /* - * This quirk is for the "Advanced Driver" mode of the Edirol UA-5. - * If the advanced mode switch at the back of the unit is off, the - * UA-5 has ID 0x0582/0x0011 and is standard compliant (no quirks), - * but offers only 16-bit PCM. - * In advanced mode, the UA-5 will output S24_3LE samples (two - * channels) at the rate indicated on the front switch, including - * the 96kHz sample rate. - */ - USB_DEVICE(0x0582, 0x0010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-5", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x0013 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0012), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "XV-5050", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x0015 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0014), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-880", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x01ff, - .in_cables = 0x01ff - } - } -}, -{ - /* has ID 0x0017 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0016), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "SD-90", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x001c when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x001b), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "MMP-2", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x001e when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x001d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "V-SYNTH", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x0024 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0023), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-550", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x003f, - .in_cables = 0x003f - } - } -}, -{ - /* - * This quirk is for the "Advanced Driver" mode. If off, the UA-20 - * has ID 0x0026 and is standard compliant, but has only 16-bit PCM - * and no MIDI. - */ - USB_DEVICE(0x0582, 0x0025), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-20", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x01, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, - .channels = 2, - .iface = 2, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x82, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_min = 44100, - .rate_max = 44100, - } - }, - { - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x0028 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0027), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "SD-20", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0007 - } - } -}, -{ - /* has ID 0x002a when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0029), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "SD-80", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - } -}, -{ /* - * This quirk is for the "Advanced" modes of the Edirol UA-700. - * If the sample format switch is not in an advanced setting, the - * UA-700 has ID 0x0582/0x002c and is standard compliant (no quirks), - * but offers only 16-bit PCM and no MIDI. - */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-700", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 3, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x002e when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x002d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "XV-2020", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x0030 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x002f), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "VariOS", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0007, - .in_cables = 0x0007 - } - } -}, -{ - /* has ID 0x0034 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0033), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "PCR", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0007 - } - } -}, - /* TODO: add Roland M-1000 support */ -{ - /* - * Has ID 0x0038 when not in "Advanced Driver" mode; - * later revisions use IDs 0x0054 and 0x00a2. - */ - USB_DEVICE(0x0582, 0x0037), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "Digital Piano", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* - * This quirk is for the "Advanced Driver" mode. If off, the GS-10 - * has ID 0x003c and is standard compliant, but has only 16-bit PCM - * and no MIDI. - */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "BOSS", - .product_name = "GS-10", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_MIDI_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x0041 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0040), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "GI-20", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x0043 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0042), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "RS-70", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x0049 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0047), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "EDIROL", */ - /* .product_name = "UR-80", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - /* in the 96 kHz modes, only interface 1 is there */ - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x004a when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0048), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "EDIROL", */ - /* .product_name = "UR-80", */ - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0007 - } - } -}, - /* TODO: add Edirol M-100FX support */ -{ - /* has ID 0x004e when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x004c), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "PCR-A", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x004f when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x004d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "PCR-A", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0007 - } - } -}, -{ - /* - * This quirk is for the "Advanced Driver" mode. If off, the UA-3FX - * is standard compliant, but has only 16-bit PCM. - */ - USB_DEVICE(0x0582, 0x0050), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-3FX", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0052), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-1SX", - .ifnum = 0, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, -{ - USB_DEVICE(0x0582, 0x0060), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "EXR Series", - .ifnum = 0, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, -{ - /* has ID 0x0067 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0065), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "PCR-1", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0003 - } - } -}, -{ - /* has ID 0x006b when not in "Advanced Driver" mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x006a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SP-606", - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x006e when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x006d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "FANTOM-X", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ /* - * This quirk is for the "Advanced" modes of the Edirol UA-25. - * If the switch is not in an advanced setting, the UA-25 has - * ID 0x0582/0x0073 and is standard compliant (no quirks), but - * offers only 16-bit PCM at 44.1 kHz and no MIDI. - */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0074), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-25", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x0076 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0075), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "BOSS", - .product_name = "DR-880", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* has ID 0x007b when not in "Advanced Driver" mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x007a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - /* "RD" or "RD-700SX"? */ - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 - } - } -}, -{ - /* has ID 0x0081 when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x0080), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "G-70", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, - /* TODO: add Roland V-SYNTH XT support */ - /* TODO: add BOSS GT-PRO support */ -{ - /* has ID 0x008c when not in "Advanced Driver" mode */ - USB_DEVICE(0x0582, 0x008b), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "PC-50", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, - /* TODO: add Edirol PC-80 support */ -{ - USB_DEVICE(0x0582, 0x0096), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-1EX", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x009a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-3EX", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - } -}, -{ - /* - * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX - * is standard compliant, but has only 16-bit PCM and no MIDI. - */ - USB_DEVICE(0x0582, 0x00a3), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-4FX", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = -1 - } - } - } -}, - /* TODO: add Edirol MD-P1 support */ -{ - USB_DEVICE(0x582, 0x00a6), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "Juno-G", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* Roland SH-201 */ - USB_DEVICE(0x0582, 0x00ad), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SH-201", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Roland SonicCell */ - USB_DEVICE(0x0582, 0x00c2), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SonicCell", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Edirol M-16DX */ - /* FIXME: This quirk gives a good-working capture stream but the - * playback seems problematic because of lacking of sync - * with capture stream. It needs to sync with the capture - * clock. As now, you'll get frequent sound distortions - * via the playback. - */ - USB_DEVICE(0x0582, 0x00c4), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* BOSS GT-10 */ - USB_DEVICE(0x0582, 0x00da), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Advanced modes of the Edirol UA-25EX. - * For the standard mode, UA-25EX has ID 0582:00e7, which - * offers only 16-bit PCM at 44.1 kHz and no MIDI. - */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e6), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-25EX", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_EDIROL_UAXX - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x00ea when not in Advanced Driver mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "Roland", */ - /* .product_name = "UA-1G", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, - -/* Guillemot devices */ -{ - /* - * This is for the "Windows Edition" where the external MIDI ports are - * the only MIDI ports; the control data is reported through HID - * interfaces. The "Macintosh Edition" has ID 0xd002 and uses standard - * compliant USB MIDI ports for external MIDI and controls. - */ - USB_DEVICE_VENDOR_SPEC(0x06f8, 0xb000), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Hercules", - .product_name = "DJ Console (WE)", - .ifnum = 4, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, - -/* Midiman/M-Audio devices */ -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 2x2", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 1x1", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "Keystation", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 4x4", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - } -}, -{ - /* - * For hardware revision 1.05; in the later revisions (1.10 and - * 1.21), 0x1031 is the ID for the device without firmware. - * Thanks to Olaf Giesbrecht - */ - USB_DEVICE_VER(0x0763, 0x1031, 0x0100, 0x0109), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 8x8", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x01ff, - .in_cables = 0x01ff - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 8x8", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x01ff, - .in_cables = 0x01ff - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x1041), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "MidiSport 2x4", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x0003 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "Quattro", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - /* - * Interfaces 0-2 are "Windows-compatible", 16-bit only, - * and share endpoints with the other interfaces. - * Ignore them. The other interfaces can do 24 bits, - * but captured samples are big-endian (see usbaudio.c). - */ - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 4, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 5, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 6, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 7, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 8, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 9, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "AudioPhile", - .ifnum = 6, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x2008), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "Ozone", - .ifnum = 3, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0763, 0x200d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "M-Audio", - .product_name = "OmniStudio", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 4, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 5, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 6, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 7, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 8, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 9, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0763, 0x2019), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "M-Audio", */ - /* .product_name = "Ozone Academic", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, - -/* Casio devices */ -{ - USB_DEVICE(0x07cf, 0x6801), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Casio", - .product_name = "PL-40R", - .ifnum = 0, - .type = QUIRK_MIDI_YAMAHA - } -}, -{ - /* this ID is used by several devices without a product ID */ - USB_DEVICE(0x07cf, 0x6802), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Casio", - .product_name = "Keyboard", - .ifnum = 0, - .type = QUIRK_MIDI_YAMAHA - } -}, - -/* Mark of the Unicorn devices */ -{ - /* thanks to Robert A. Lerche */ - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_PRODUCT | - USB_DEVICE_ID_MATCH_DEV_SUBCLASS, - .idVendor = 0x07fd, - .idProduct = 0x0001, - .bDeviceSubClass = 2, - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "MOTU", - .product_name = "Fastlane", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_MIDI_FASTLANE - }, - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, - -/* Emagic devices */ -{ - USB_DEVICE(0x086a, 0x0001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Emagic", - /* .product_name = "Unitor8", */ - .ifnum = 2, - .type = QUIRK_MIDI_EMAGIC, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x80ff, - .in_cables = 0x80ff - } - } -}, -{ - USB_DEVICE(0x086a, 0x0002), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Emagic", - /* .product_name = "AMT8", */ - .ifnum = 2, - .type = QUIRK_MIDI_EMAGIC, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x80ff, - .in_cables = 0x80ff - } - } -}, -{ - USB_DEVICE(0x086a, 0x0003), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Emagic", - /* .product_name = "MT4", */ - .ifnum = 2, - .type = QUIRK_MIDI_EMAGIC, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x800f, - .in_cables = 0x8003 - } - } -}, - -/* TerraTec devices */ -{ - USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "PHASE 26", - .ifnum = 3, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "PHASE 26", - .ifnum = 3, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0014), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "PHASE 26", - .ifnum = 3, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, -{ - USB_DEVICE(0x0ccd, 0x0028), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "Aureon5.1MkII", - .ifnum = QUIRK_NO_INTERFACE - } -}, -{ - USB_DEVICE(0x0ccd, 0x0035), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Miditech", - .product_name = "Play'n Roll", - .ifnum = 0, - .type = QUIRK_MIDI_CME - } -}, - -/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ -{ - USB_DEVICE(0x103d, 0x0100), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } -}, -{ - USB_DEVICE(0x103d, 0x0101), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } -}, - -/* Novation EMS devices */ -{ - USB_DEVICE_VENDOR_SPEC(0x1235, 0x0001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Novation", - .product_name = "ReMOTE Audio/XStation", - .ifnum = 4, - .type = QUIRK_MIDI_NOVATION - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x1235, 0x0002), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Novation", - .product_name = "Speedio", - .ifnum = 3, - .type = QUIRK_MIDI_NOVATION - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Novation", - .product_name = "ReMOTE25", - .ifnum = 0, - .type = QUIRK_MIDI_NOVATION - } -}, - -/* Access Music devices */ -{ - /* VirusTI Desktop */ - USB_DEVICE_VENDOR_SPEC(0x133e, 0x0815), - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = &(const struct snd_usb_audio_quirk[]) { - { - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &(const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 - } - }, - { - .ifnum = 4, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, - -/* */ -{ - /* aka. Serato Scratch Live DJ Box */ - USB_DEVICE(0x13e5, 0x0001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Rane", - .product_name = "SL-1", - .ifnum = QUIRK_NO_INTERFACE - } -}, - -/* Miditech devices */ -{ - USB_DEVICE(0x4752, 0x0011), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Miditech", - .product_name = "Midistart-2", - .ifnum = 0, - .type = QUIRK_MIDI_CME - } -}, - -/* Central Music devices */ -{ - /* this ID used by both Miditech MidiStudio-2 and CME UF-x */ - USB_DEVICE(0x7104, 0x2202), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = 0, - .type = QUIRK_MIDI_CME - } -}, - -/* Hauppauge HVR-950Q and HVR-850 */ -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7200), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7201), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7202), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7203), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7204), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7205), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7250), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-950Q", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x2040, 0x7230), - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Hauppauge", - .product_name = "HVR-850", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_AUDIO_ALIGN_TRANSFER, - } -}, - -/* Digidesign Mbox */ -{ - /* Thanks to Clemens Ladisch */ - USB_DEVICE(0x0dba, 0x1000), - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Digidesign", - .product_name = "MBox", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]){ - { - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE, - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = &(const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3BE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, - .endpoint = 0x02, - .ep_attr = 0x01, - .maxpacksize = 0x130, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 44100, - .rate_max = 48000, - .nr_rates = 2, - .rate_table = (unsigned int[]) { - 44100, 48000 - } - } - }, - { - .ifnum = -1 - } - } - - } -}, - -{ - /* - * Some USB MIDI devices don't have an audio control interface, - * so we have to grab MIDI streaming interfaces here. - */ - .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, - -#undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 4f6518c..5f7b942 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -25,7 +25,7 @@ #define MODNAME "US122L" #include "usb_stream.c" #include "../usbaudio.h" -#include "../usbmidi.h" +#include "../midi.h" #include "us122l.h" MODULE_AUTHOR("Karsten Wiese "); diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index 9ab97b4..e43c0a8 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -1,7 +1,7 @@ #ifndef USBUSX2Y_H #define USBUSX2Y_H #include "../usbaudio.h" -#include "../usbmidi.h" +#include "../midi.h" #include "usbus428ctldefs.h" #define NRURBS 2 -- cgit v1.1 From e11b4e0e4f5ab40ec342dc07b7201c09a45f9574 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 4 Mar 2010 19:46:14 +0100 Subject: ALSA: usb-audio: rename substream format field to altset_idx The snd_usb_substream::format field actually contains the index of the current alternate setting, so rename it to altset_idx to avoid confusion. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/card.h | 2 +- sound/usb/pcm.c | 8 ++++---- sound/usb/proc.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 71f03c1..856d71b 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -54,7 +54,7 @@ struct snd_usb_substream { struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ unsigned int cur_rate; /* current rate (for hw_params callback) */ unsigned int period_bytes; /* current period bytes (for hw_params callback) */ - unsigned int format; /* USB data format */ + unsigned int altset_idx; /* USB data format: index of alternate setting */ unsigned int datapipe; /* the data i/o pipe */ unsigned int syncpipe; /* 1 - async out or adaptive in */ unsigned int datainterval; /* log_2 of data packet interval */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 87863cc..c3d5a97 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -202,11 +202,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return -EIO; } subs->interface = -1; - subs->format = 0; + subs->altset_idx = 0; } /* set interface */ - if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { + if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) { if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) { snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", dev->devnum, fmt->iface, fmt->altsetting); @@ -214,7 +214,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); subs->interface = fmt->iface; - subs->format = fmt->altset_idx; + subs->altset_idx = fmt->altset_idx; } /* create a data pipe */ @@ -771,7 +771,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) struct snd_usb_substream *subs = &as->substream[direction]; subs->interface = -1; - subs->format = 0; + subs->altset_idx = 0; runtime->hw = snd_usb_hardware; runtime->private_data = subs; subs->pcm_substream = substream; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index be3065e..78fc3ba 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -116,7 +116,7 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn unsigned int i; snd_iprintf(buffer, " Status: Running\n"); snd_iprintf(buffer, " Interface = %d\n", subs->interface); - snd_iprintf(buffer, " Altset = %d\n", subs->format); + snd_iprintf(buffer, " Altset = %d\n", subs->altset_idx); snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs); for (i = 0; i < subs->nurbs; i++) snd_iprintf(buffer, "%d ", subs->dataurb[i].packets); -- cgit v1.1 From 015eb0b08150c6fef843efe22609589ead3d4fb8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 4 Mar 2010 19:46:15 +0100 Subject: ALSA: usb-audio: use a format bitmask per alternate setting In preparation for USB audio 2.0 support, change the audioformat structure so that it uses a bitmask to specify possible formats. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/card.h | 2 +- sound/usb/endpoint.c | 4 ++-- sound/usb/format.c | 10 +++++----- sound/usb/pcm.c | 13 ++++++++++--- sound/usb/proc.c | 9 +++++++-- sound/usb/quirks-table.h | 12 ++++++------ sound/usb/quirks.c | 2 +- sound/usb/urb.c | 4 ++-- 8 files changed, 34 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 856d71b..ed92420 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -9,7 +9,7 @@ struct audioformat { struct list_head list; - snd_pcm_format_t format; /* format type */ + u64 formats; /* ALSA format bits */ unsigned int channels; /* # channels */ unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int frame_size; /* samples per frame for non-audio */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 3f53dee..d65235c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -94,7 +94,7 @@ int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct au if (subs->endpoint == fp->endpoint) { list_add_tail(&fp->list, &subs->fmt_list); subs->num_formats++; - subs->formats |= 1ULL << fp->format; + subs->formats |= fp->formats; return 0; } } @@ -268,7 +268,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) */ if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 && fp && fp->altsetting == 1 && fp->channels == 1 && - fp->format == SNDRV_PCM_FORMAT_S16_LE && + fp->formats == SNDRV_PCM_FMTBIT_S16_LE && protocol == UAC_VERSION_1 && le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == fp->maxpacksize * 2) diff --git a/sound/usb/format.c b/sound/usb/format.c index cbfe0c2..87f07f0 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -323,7 +323,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, return -1; } - fp->format = pcm_format; + fp->formats = 1uLL << pcm_format; /* gather possible sample rates */ /* audio class v1 reports possible sample rates as part of the @@ -365,16 +365,16 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, switch (format) { case UAC_FORMAT_TYPE_II_AC3: /* FIXME: there is no AC3 format defined yet */ - // fp->format = SNDRV_PCM_FORMAT_AC3; - fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */ + // fp->formats = SNDRV_PCM_FMTBIT_AC3; + fp->formats = SNDRV_PCM_FMTBIT_U8; /* temporary hack to receive byte streams */ break; case UAC_FORMAT_TYPE_II_MPEG: - fp->format = SNDRV_PCM_FORMAT_MPEG; + fp->formats = SNDRV_PCM_FMTBIT_MPEG; break; default: snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected. processed as MPEG.\n", chip->dev->devnum, fp->iface, fp->altsetting, format); - fp->format = SNDRV_PCM_FORMAT_MPEG; + fp->formats = SNDRV_PCM_FMTBIT_MPEG; break; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c3d5a97..bd0f84f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -58,7 +58,9 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (fp->format != format || fp->channels != channels) + if (!(fp->formats & (1uLL << format))) + continue; + if (fp->channels != channels) continue; if (rate < fp->rate_min || rate > fp->rate_max) continue; @@ -428,10 +430,15 @@ static int hw_check_valid_format(struct snd_usb_substream *subs, struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); + struct snd_mask check_fmts; unsigned int ptime; /* check the format */ - if (!snd_mask_test(fmts, fp->format)) { + snd_mask_none(&check_fmts); + check_fmts.bits[0] = (u32)fp->formats; + check_fmts.bits[1] = (u32)(fp->formats >> 32); + snd_mask_intersect(&check_fmts, fmts); + if (snd_mask_empty(&check_fmts)) { hwc_debug(" > check: no supported format %d\n", fp->format); return 0; } @@ -584,7 +591,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, fp = list_entry(p, struct audioformat, list); if (!hw_check_valid_format(subs, params, fp)) continue; - fbits |= (1ULL << fp->format); + fbits |= fp->formats; } oldbits[0] = fmt->bits[0]; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 78fc3ba..f5e3f35 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -79,11 +79,16 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s list_for_each(p, &subs->fmt_list) { struct audioformat *fp; + snd_pcm_format_t fmt; fp = list_entry(p, struct audioformat, list); snd_iprintf(buffer, " Interface %d\n", fp->iface); snd_iprintf(buffer, " Altset %d\n", fp->altsetting); - snd_iprintf(buffer, " Format: %s\n", - snd_pcm_format_name(fp->format)); + snd_iprintf(buffer, " Format:"); + for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt) + if (fp->formats & (1uLL << fmt)) + snd_iprintf(buffer, " %s", + snd_pcm_format_name(fmt)); + snd_iprintf(buffer, "\n"); snd_iprintf(buffer, " Channels: %d\n", fp->channels); snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", fp->endpoint & USB_ENDPOINT_NUMBER_MASK, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 2b426c1..6e8651d 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -279,7 +279,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 0, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels = 4, .iface = 0, .altsetting = 1, @@ -296,7 +296,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 1, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels = 2, .iface = 1, .altsetting = 1, @@ -580,7 +580,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 0, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, + .formats = SNDRV_PCM_FMTBIT_S24_3LE, .channels = 2, .iface = 0, .altsetting = 1, @@ -597,7 +597,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 1, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, + .formats = SNDRV_PCM_FMTBIT_S24_3LE, .channels = 2, .iface = 1, .altsetting = 1, @@ -793,7 +793,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 1, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, + .formats = SNDRV_PCM_FMTBIT_S24_3LE, .channels = 2, .iface = 1, .altsetting = 1, @@ -810,7 +810,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 2, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = & (const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3LE, + .formats = SNDRV_PCM_FMTBIT_S24_3LE, .channels = 2, .iface = 2, .altsetting = 1, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4c16920..99a19ba 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -174,7 +174,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, const struct snd_usb_audio_quirk *quirk) { static const struct audioformat ua_format = { - .format = SNDRV_PCM_FORMAT_S24_3LE, + .formats = SNDRV_PCM_FMTBIT_S24_3LE, .channels = 2, .fmt_type = UAC_FORMAT_TYPE_I, .altsetting = 1, diff --git a/sound/usb/urb.c b/sound/usb/urb.c index e9c339f..ad50d43 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -662,7 +662,7 @@ static int prepare_nodata_playback_urb(struct snd_usb_substream *subs, urb->number_of_packets = ctx->packets; urb->transfer_buffer_length = offs * stride; memset(urb->transfer_buffer, - subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0, + runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0, offs * stride); return 0; } @@ -924,7 +924,7 @@ void snd_usb_init_substream(struct snd_usb_stream *as, snd_usb_set_pcm_ops(as->pcm, stream); list_add_tail(&fp->list, &subs->fmt_list); - subs->formats |= 1ULL << fp->format; + subs->formats |= fp->formats; subs->endpoint = fp->endpoint; subs->num_formats++; subs->fmt_type = fp->fmt_type; -- cgit v1.1 From 29088fef3e3f62147c1dd53d764da4f04bf3188d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 4 Mar 2010 19:46:16 +0100 Subject: ALSA: usb-audio: support multiple formats with audio class v2 devices Change the parser to correctly handle v2 descriptors with multiple format bits set. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/format.c | 93 ++++++++++++++++++++---------------------------- sound/usb/quirks-table.h | 2 +- 2 files changed, 39 insertions(+), 56 deletions(-) (limited to 'sound') diff --git a/sound/usb/format.c b/sound/usb/format.c index 87f07f0..b613e0a 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -37,19 +37,20 @@ * @format: the format tag (wFormatTag) * @fmt: the format type descriptor */ -static int parse_audio_format_i_type(struct snd_usb_audio *chip, +static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp, int format, void *_fmt, int protocol) { - int pcm_format, i; int sample_width, sample_bytes; + u64 pcm_formats; switch (protocol) { case UAC_VERSION_1: { struct uac_format_type_i_discrete_descriptor *fmt = _fmt; sample_width = fmt->bBitResolution; sample_bytes = fmt->bSubframeSize; + format = 1 << format; break; } @@ -57,24 +58,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct uac_format_type_i_ext_descriptor *fmt = _fmt; sample_width = fmt->bBitResolution; sample_bytes = fmt->bSubslotSize; - - /* - * FIXME - * USB audio class v2 devices specify a bitmap of possible - * audio formats rather than one fix value. For now, we just - * pick one of them and report that as the only possible - * value for this setting. - * The bit allocation map is in fact compatible to the - * wFormatTag of the v1 AS streaming descriptors, which is why - * we can simply map the matrix. - */ - - for (i = 0; i < 5; i++) - if (format & (1UL << i)) { - format = i + 1; - break; - } - + format <<= 1; break; } @@ -82,15 +66,15 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, return -EINVAL; } - /* FIXME: correct endianess and sign? */ - pcm_format = -1; + pcm_formats = 0; - switch (format) { - case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */ + if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) { + /* some devices don't define this correctly... */ snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", chip->dev->devnum, fp->iface, fp->altsetting); - /* fall-through */ - case UAC_FORMAT_TYPE_I_PCM: + format = 1 << UAC_FORMAT_TYPE_I_PCM; + } + if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) { if (sample_width > sample_bytes * 8) { snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", chip->dev->devnum, fp->iface, fp->altsetting, @@ -99,22 +83,22 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, /* check the format byte size */ switch (sample_bytes) { case 1: - pcm_format = SNDRV_PCM_FORMAT_S8; + pcm_formats |= SNDRV_PCM_FMTBIT_S8; break; case 2: if (snd_usb_is_big_endian_format(chip, fp)) - pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ + pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */ else - pcm_format = SNDRV_PCM_FORMAT_S16_LE; + pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE; break; case 3: if (snd_usb_is_big_endian_format(chip, fp)) - pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ + pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */ else - pcm_format = SNDRV_PCM_FORMAT_S24_3LE; + pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE; break; case 4: - pcm_format = SNDRV_PCM_FORMAT_S32_LE; + pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE; break; default: snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", @@ -122,30 +106,29 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, sample_width, sample_bytes); break; } - break; - case UAC_FORMAT_TYPE_I_PCM8: - pcm_format = SNDRV_PCM_FORMAT_U8; - + } + if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) { /* Dallas DS4201 workaround: it advertises U8 format, but really supports S8. */ if (chip->usb_id == USB_ID(0x04fa, 0x4201)) - pcm_format = SNDRV_PCM_FORMAT_S8; - break; - case UAC_FORMAT_TYPE_I_IEEE_FLOAT: - pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; - break; - case UAC_FORMAT_TYPE_I_ALAW: - pcm_format = SNDRV_PCM_FORMAT_A_LAW; - break; - case UAC_FORMAT_TYPE_I_MULAW: - pcm_format = SNDRV_PCM_FORMAT_MU_LAW; - break; - default: - snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", + pcm_formats |= SNDRV_PCM_FMTBIT_S8; + else + pcm_formats |= SNDRV_PCM_FMTBIT_U8; + } + if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) { + pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; + } + if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) { + pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW; + } + if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) { + pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW; + } + if (format & ~0x3f) { + snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n", chip->dev->devnum, fp->iface, fp->altsetting, format); - break; } - return pcm_format; + return pcm_formats; } @@ -317,14 +300,14 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, default: pcm_format = SNDRV_PCM_FORMAT_S16_LE; } + fp->formats = 1uLL << pcm_format; } else { - pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol); - if (pcm_format < 0) + fp->formats = parse_audio_format_i_type(chip, fp, format, + fmt, protocol); + if (!fp->formats) return -1; } - fp->formats = 1uLL << pcm_format; - /* gather possible sample rates */ /* audio class v1 reports possible sample rates as part of the * proprietary class specific descriptor. diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 6e8651d..81c5f8a 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2203,7 +2203,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = 1, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = &(const struct audioformat) { - .format = SNDRV_PCM_FORMAT_S24_3BE, + .formats = SNDRV_PCM_FMTBIT_S24_3BE, .channels = 2, .iface = 1, .altsetting = 1, -- cgit v1.1 From 767d75ad1c08c31646498a13837a5c59db90ccad Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Mar 2010 19:46:17 +0100 Subject: ALSA: usb-audio: add support for samplerate setting on v2 devices Sample rate setting is done with a 4-byte long class request that addresses the interface. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 4 +- sound/usb/pcm.c | 173 +++++++++++++++++++++++++++++++++++++-------------- sound/usb/pcm.h | 4 +- sound/usb/quirks.c | 4 +- 4 files changed, 133 insertions(+), 52 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index d65235c..91850f8 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -350,8 +350,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) } /* try to set the interface... */ usb_set_interface(chip->dev, iface_no, altno); - snd_usb_init_pitch(chip->dev, iface_no, alts, fp); - snd_usb_init_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); + snd_usb_init_pitch(chip, iface_no, alts, fp); + snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max); } return 0; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index bd0f84f..e0f3f87 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -107,69 +107,150 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned return found; } +static int init_pitch_v1(struct snd_usb_audio *chip, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt) +{ + struct usb_device *dev = chip->dev; + unsigned int ep; + unsigned char data[1]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + + /* if endpoint doesn't have pitch control, bail out */ + if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL)) + return 0; + + data[0] = 1; + if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, + data, sizeof(data), 1000)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", + dev->devnum, iface, ep); + return err; + } + + return 0; +} /* * initialize the picth control and sample rate */ -int snd_usb_init_pitch(struct usb_device *dev, int iface, +int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt) { + struct usb_interface_descriptor *altsd = get_iface_desc(alts); + + switch (altsd->bInterfaceProtocol) { + case UAC_VERSION_1: + return init_pitch_v1(chip, iface, alts, fmt); + + case UAC_VERSION_2: + /* not implemented yet */ + return 0; + } + + return -EINVAL; +} + +static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + struct usb_device *dev = chip->dev; unsigned int ep; - unsigned char data[1]; - int err; + unsigned char data[3]; + int err, crate; ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) { - data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", - dev->devnum, iface, ep); - return err; - } + /* if endpoint doesn't have sampling rate control, bail out */ + if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) { + snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n", + dev->devnum, iface, fmt->altsetting); + return 0; + } + + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, + data, sizeof(data), 1000)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", + dev->devnum, iface, fmt->altsetting, rate, ep); + return err; } + if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, + data, sizeof(data), 1000)) < 0) { + snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", + dev->devnum, iface, fmt->altsetting, ep); + return 0; /* some devices don't support reading */ + } + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + // runtime->rate = crate; + } + return 0; } -int snd_usb_init_sample_rate(struct usb_device *dev, int iface, +static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + struct usb_device *dev = chip->dev; + unsigned char data[4]; + int err, crate; + + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + data[3] = rate >> 24; + if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x0100, chip->clock_id << 8, + data, sizeof(data), 1000)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n", + dev->devnum, iface, fmt->altsetting, rate); + return err; + } + if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 0x0100, chip->clock_id << 8, + data, sizeof(data), 1000)) < 0) { + snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", + dev->devnum, iface, fmt->altsetting); + return err; + } + crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + if (crate != rate) + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + + return 0; +} + +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { - unsigned int ep; - unsigned char data[3]; - int err; + struct usb_interface_descriptor *altsd = get_iface_desc(alts); - ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) { - int crate; - data[0] = rate; - data[1] = rate >> 8; - data[2] = rate >> 16; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", - dev->devnum, iface, fmt->altsetting, rate, ep); - return err; - } - if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { - snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", - dev->devnum, iface, fmt->altsetting, ep); - return 0; /* some devices don't support reading */ - } - crate = data[0] | (data[1] << 8) | (data[2] << 16); - if (crate != rate) { - snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); - // runtime->rate = crate; - } + switch (altsd->bInterfaceProtocol) { + case UAC_VERSION_1: + return set_sample_rate_v1(chip, iface, alts, fmt, rate); + + case UAC_VERSION_2: + return set_sample_rate_v2(chip, iface, alts, fmt, rate); } - return 0; + + return -EINVAL; } /* @@ -280,7 +361,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) subs->fill_max = 1; - if ((err = snd_usb_init_pitch(dev, subs->interface, alts, fmt)) < 0) + if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0) return err; subs->cur_audiofmt = fmt; @@ -343,7 +424,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct usb_interface *iface; iface = usb_ifnum_to_if(subs->dev, fmt->iface); alts = &iface->altsetting[fmt->altset_idx]; - ret = snd_usb_init_sample_rate(subs->dev, subs->interface, alts, fmt, rate); + ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate); if (ret < 0) return ret; subs->cur_rate = rate; diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 8585601..1c931b6 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -3,11 +3,11 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); -int snd_usb_init_pitch(struct usb_device *dev, int iface, +int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt); -int snd_usb_init_sample_rate(struct usb_device *dev, int iface, +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 99a19ba..0c0b23b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -159,8 +159,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); usb_set_interface(chip->dev, fp->iface, 0); - snd_usb_init_pitch(chip->dev, fp->iface, alts, fp); - snd_usb_init_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max); + snd_usb_init_pitch(chip, fp->iface, alts, fp); + snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; } -- cgit v1.1 From 7e847894039d7590321de306fca2b1ae58662f29 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:20 +0100 Subject: linux/usb/audio.h: split header - Split the audio.h file in two to clearly denote the differences between the standards. - Add many more defines to audio-v2.h. Most of them are not currently used. - Replaced a magic value with a proper define Signed-off-by: Daniel Mack Acked-by: Greg Kroah-Hartman Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/card.c | 3 ++- sound/usb/endpoint.c | 1 + sound/usb/format.c | 1 + sound/usb/pcm.c | 5 +++-- 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 426aabc..78d12ff 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -250,7 +251,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) * clock selectors and sample rate conversion units. */ cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, - NULL, UAC_CLOCK_SOURCE); + NULL, UAC2_CLOCK_SOURCE); if (!cs) { snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n"); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 91850f8..b1309cd 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/sound/usb/format.c b/sound/usb/format.c index b613e0a..0e04efe 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e0f3f87..630e220 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -215,7 +216,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, data[3] = rate >> 24; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0x0100, chip->clock_id << 8, + UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, data, sizeof(data), 1000)) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n", dev->devnum, iface, fmt->altsetting, rate); @@ -223,7 +224,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, } if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - 0x0100, chip->clock_id << 8, + UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, data, sizeof(data), 1000)) < 0) { snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", dev->devnum, iface, fmt->altsetting); -- cgit v1.1 From 45d760567a7d773237b8996584a4ae0440d5e369 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:21 +0100 Subject: ALSA: usb-mixer: use defines from audio.h No need for the private enum. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/format.c | 6 ++++-- sound/usb/usbmixer.c | 27 ++++++--------------------- sound/usb/usbmixer_maps.c | 4 ++-- 3 files changed, 12 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/usb/format.c b/sound/usb/format.c index 0e04efe..fcadedd 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -218,7 +218,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, /* get the number of sample rates first by only fetching 2 bytes */ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - 0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000); + UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, + tmp, sizeof(tmp), 1000); if (ret < 0) { snd_printk(KERN_ERR "unable to retrieve number of sample rates\n"); @@ -236,7 +237,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, /* now get the full information */ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - 0x0100, chip->clock_id << 8, data, data_size, 1000); + UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, + data, data_size, 1000); if (ret < 0) { snd_printk(KERN_ERR "unable to retrieve sample rate range\n"); diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 5c05683..ab8f0f0 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -136,21 +136,6 @@ struct usb_mixer_elem_info { u8 initialized; }; - -enum { - USB_FEATURE_NONE = 0, - USB_FEATURE_MUTE = 1, - USB_FEATURE_VOLUME, - USB_FEATURE_BASS, - USB_FEATURE_MID, - USB_FEATURE_TREBLE, - USB_FEATURE_GEQ, - USB_FEATURE_AGC, - USB_FEATURE_DELAY, - USB_FEATURE_BASSBOOST, - USB_FEATURE_LOUDNESS -}; - enum { USB_MIXER_BOOLEAN, USB_MIXER_INV_BOOLEAN, @@ -954,7 +939,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, control++; /* change from zero-based to 1-based value */ - if (control == USB_FEATURE_GEQ) { + if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) { /* FIXME: not supported yet */ return; } @@ -1001,8 +986,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, kctl->id.name, sizeof(kctl->id.name)); switch (control) { - case USB_FEATURE_MUTE: - case USB_FEATURE_VOLUME: + case UAC_MUTE_CONTROL: + case UAC_VOLUME_CONTROL: /* determine the control name. the rule is: * - if a name id is given in descriptor, use it. * - if the connected input can be determined, then use the name @@ -1029,9 +1014,9 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, len = append_ctl_name(kctl, " Playback"); } } - append_ctl_name(kctl, control == USB_FEATURE_MUTE ? + append_ctl_name(kctl, control == UAC_MUTE_CONTROL ? " Switch" : " Volume"); - if (control == USB_FEATURE_VOLUME) { + if (control == UAC_VOLUME_CONTROL) { kctl->tlv.c = mixer_vol_tlv; kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | @@ -1120,7 +1105,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void snd_printk(KERN_INFO "usbmixer: master volume quirk for PCM2702 chip\n"); /* disable non-functional volume control */ - master_bits &= ~(1 << (USB_FEATURE_VOLUME - 1)); + master_bits &= ~UAC_FU_VOLUME; break; } if (channels > 0) diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c index 79e903a..d93fc89 100644 --- a/sound/usb/usbmixer_maps.c +++ b/sound/usb/usbmixer_maps.c @@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = { /* 16: MU (w/o controls) */ { 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */ { 17, "Channel Routing", 2 }, /* PU: mode select */ - { 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */ - { 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */ + { 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */ + { 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */ { 18, "Master Playback" }, /* FU; others */ /* 19: OT speaker */ /* 20: OT headphone */ -- cgit v1.1 From 7b1eda223debcba706ab989a09c4eecb327aebdf Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:22 +0100 Subject: ALSA: usb-mixer: factor out quirks Move all non-standard mixer controls and vendor-specific extensions to a separate file. Some structs need to be exported now. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 1 + sound/usb/mixer_quirks.c | 411 +++++++++++++++++++++++++++++++++++++++++++++ sound/usb/mixer_quirks.h | 13 ++ sound/usb/quirks.c | 1 + sound/usb/usbmixer.c | 425 ++--------------------------------------------- sound/usb/usbmixer.h | 45 ++++- 6 files changed, 480 insertions(+), 416 deletions(-) create mode 100644 sound/usb/mixer_quirks.c create mode 100644 sound/usb/mixer_quirks.h (limited to 'sound') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 0758d8d..744024a 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -4,6 +4,7 @@ snd-usb-audio-objs := card.o \ usbmixer.o \ + mixer_quirks.o \ proc.o \ quirks.o \ format.o \ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c new file mode 100644 index 0000000..d2f4dcd --- /dev/null +++ b/sound/usb/mixer_quirks.c @@ -0,0 +1,411 @@ +/* + * USB Audio Driver for ALSA + * + * Quirks and vendor-specific extensions for mixer interfaces + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "usbaudio.h" +#include "usbmixer.h" +#include "mixer_quirks.h" +#include "helper.h" + +/* + * Sound Blaster remote control configuration + * + * format of remote control data: + * Extigy: xx 00 + * Audigy 2 NX: 06 80 xx 00 00 00 + * Live! 24-bit: 06 80 xx yy 22 83 + */ +static const struct rc_config { + u32 usb_id; + u8 offset; + u8 length; + u8 packet_length; + u8 min_packet_length; /* minimum accepted length of the URB result */ + u8 mute_mixer_id; + u32 mute_code; +} rc_configs[] = { + { USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */ + { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ + { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ + { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ +}; + +static void snd_usb_soundblaster_remote_complete(struct urb *urb) +{ + struct usb_mixer_interface *mixer = urb->context; + const struct rc_config *rc = mixer->rc_cfg; + u32 code; + + if (urb->status < 0 || urb->actual_length < rc->min_packet_length) + return; + + code = mixer->rc_buffer[rc->offset]; + if (rc->length == 2) + code |= mixer->rc_buffer[rc->offset + 1] << 8; + + /* the Mute button actually changes the mixer control */ + if (code == rc->mute_code) + snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); + mixer->rc_code = code; + wmb(); + wake_up(&mixer->rc_waitq); +} + +static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, + long count, loff_t *offset) +{ + struct usb_mixer_interface *mixer = hw->private_data; + int err; + u32 rc_code; + + if (count != 1 && count != 4) + return -EINVAL; + err = wait_event_interruptible(mixer->rc_waitq, + (rc_code = xchg(&mixer->rc_code, 0)) != 0); + if (err == 0) { + if (count == 1) + err = put_user(rc_code, buf); + else + err = put_user(rc_code, (u32 __user *)buf); + } + return err < 0 ? err : count; +} + +static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, + poll_table *wait) +{ + struct usb_mixer_interface *mixer = hw->private_data; + + poll_wait(file, &mixer->rc_waitq, wait); + return mixer->rc_code ? POLLIN | POLLRDNORM : 0; +} + +static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) +{ + struct snd_hwdep *hwdep; + int err, len, i; + + for (i = 0; i < ARRAY_SIZE(rc_configs); ++i) + if (rc_configs[i].usb_id == mixer->chip->usb_id) + break; + if (i >= ARRAY_SIZE(rc_configs)) + return 0; + mixer->rc_cfg = &rc_configs[i]; + + len = mixer->rc_cfg->packet_length; + + init_waitqueue_head(&mixer->rc_waitq); + err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); + if (err < 0) + return err; + snprintf(hwdep->name, sizeof(hwdep->name), + "%s remote control", mixer->chip->card->shortname); + hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC; + hwdep->private_data = mixer; + hwdep->ops.read = snd_usb_sbrc_hwdep_read; + hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; + hwdep->exclusive = 1; + + mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mixer->rc_urb) + return -ENOMEM; + mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL); + if (!mixer->rc_setup_packet) { + usb_free_urb(mixer->rc_urb); + mixer->rc_urb = NULL; + return -ENOMEM; + } + mixer->rc_setup_packet->bRequestType = + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + mixer->rc_setup_packet->bRequest = UAC_GET_MEM; + mixer->rc_setup_packet->wValue = cpu_to_le16(0); + mixer->rc_setup_packet->wIndex = cpu_to_le16(0); + mixer->rc_setup_packet->wLength = cpu_to_le16(len); + usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, + snd_usb_soundblaster_remote_complete, mixer); + return 0; +} + +#define snd_audigy2nx_led_info snd_ctl_boolean_mono_info + +static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + + ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; + return 0; +} + +static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + int err, changed; + + if (value > 1) + return -EINVAL; + changed = value != mixer->audigy2nx_leds[index]; + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + value, index + 2, NULL, 0, 100); + if (err < 0) + return err; + mixer->audigy2nx_leds[index] = value; + return changed; +} + +static struct snd_kcontrol_new snd_audigy2nx_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CMSS LED Switch", + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, + .private_value = 0, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Power LED Switch", + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, + .private_value = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Dolby Digital LED Switch", + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, + .private_value = 2, + }, +}; + +static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { + if (i > 1 && /* Live24ext has 2 LEDs only */ + (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) + break; + err = snd_ctl_add(mixer->chip->card, + snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); + if (err < 0) + return err; + } + mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ + return 0; +} + +static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const struct sb_jack { + int unitid; + const char *name; + } jacks_audigy2nx[] = { + {4, "dig in "}, + {7, "line in"}, + {19, "spk out"}, + {20, "hph out"}, + {-1, NULL} + }, jacks_live24ext[] = { + {4, "line in"}, /* &1=Line, &2=Mic*/ + {3, "hph out"}, /* headphones */ + {0, "RC "}, /* last command, 6 bytes see rc_config above */ + {-1, NULL} + }; + const struct sb_jack *jacks; + struct usb_mixer_interface *mixer = entry->private_data; + int i, err; + u8 buf[3]; + + snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname); + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) + jacks = jacks_audigy2nx; + else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) + jacks = jacks_live24ext; + else + return; + + for (i = 0; jacks[i].name; ++i) { + snd_iprintf(buffer, "%s: ", jacks[i].name); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, + jacks[i].unitid << 8, buf, 3, 100); + if (err == 3 && (buf[0] == 3 || buf[0] == 6)) + snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); + else + snd_iprintf(buffer, "?\n"); + } +} + +static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); + return 0; +} + +static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + u8 old_status, new_status; + int err, changed; + + old_status = mixer->xonar_u1_status; + if (ucontrol->value.integer.value[0]) + new_status = old_status | 0x02; + else + new_status = old_status & ~0x02; + changed = new_status != old_status; + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 50, 0, &new_status, 1, 100); + if (err < 0) + return err; + mixer->xonar_u1_status = new_status; + return changed; +} + +static struct snd_kcontrol_new snd_xonar_u1_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_xonar_u1_switch_get, + .put = snd_xonar_u1_switch_put, +}; + +static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) +{ + int err; + + err = snd_ctl_add(mixer->chip->card, + snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); + if (err < 0) + return err; + mixer->xonar_u1_status = 0x05; + return 0; +} + +void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, + unsigned char samplerate_id) +{ + struct usb_mixer_interface *mixer; + struct usb_mixer_elem_info *cval; + int unitid = 12; /* SamleRate ExtensionUnit ID */ + + list_for_each_entry(mixer, &chip->mixer_list, list) { + cval = mixer->id_elems[unitid]; + if (cval) { + snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, + cval->control << 8, + samplerate_id); + snd_usb_mixer_notify_id(mixer, unitid); + } + break; + } +} + +int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) +{ + int err; + struct snd_info_entry *entry; + + if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) + return err; + + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) { + if ((err = snd_audigy2nx_controls_create(mixer)) < 0) + return err; + if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry)) + snd_info_set_text_ops(entry, mixer, + snd_audigy2nx_proc_read); + } + + if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) || + mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) { + err = snd_xonar_u1_controls_create(mixer); + if (err < 0) + return err; + } + + return 0; +} + +void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, + int unitid) +{ + if (!mixer->rc_cfg) + return; + /* unit ids specific to Extigy/Audigy 2 NX: */ + switch (unitid) { + case 0: /* remote control */ + mixer->rc_urb->dev = mixer->chip->dev; + usb_submit_urb(mixer->rc_urb, GFP_ATOMIC); + break; + case 4: /* digital in jack */ + case 7: /* line in jacks */ + case 19: /* speaker out jacks */ + case 20: /* headphones out jack */ + break; + /* live24ext: 4 = line-in jack */ + case 3: /* hp-out jack (may actuate Mute) */ + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) + snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id); + break; + default: + snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid); + break; + } +} + diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h new file mode 100644 index 0000000..bdbfab0 --- /dev/null +++ b/sound/usb/mixer_quirks.h @@ -0,0 +1,13 @@ +#ifndef SND_USB_MIXER_QUIRKS_H +#define SND_USB_MIXER_QUIRKS_H + +int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer); + +void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, + unsigned char samplerate_id); + +void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, + int unitid); + +#endif /* SND_USB_MIXER_QUIRKS_H */ + diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 0c0b23b..a82cfed 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -25,6 +25,7 @@ #include "usbaudio.h" #include "card.h" #include "usbmixer.h" +#include "mixer_quirks.h" #include "midi.h" #include "quirks.h" #include "helper.h" diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index ab8f0f0..ec2436e 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -43,60 +43,10 @@ #include "usbaudio.h" #include "usbmixer.h" #include "helper.h" - -/* - */ - -/* ignore error from controls - for debugging */ -/* #define IGNORE_CTL_ERROR */ - -/* - * Sound Blaster remote control configuration - * - * format of remote control data: - * Extigy: xx 00 - * Audigy 2 NX: 06 80 xx 00 00 00 - * Live! 24-bit: 06 80 xx yy 22 83 - */ -static const struct rc_config { - u32 usb_id; - u8 offset; - u8 length; - u8 packet_length; - u8 min_packet_length; /* minimum accepted length of the URB result */ - u8 mute_mixer_id; - u32 mute_code; -} rc_configs[] = { - { USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */ - { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ - { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ - { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ -}; +#include "mixer_quirks.h" #define MAX_ID_ELEMS 256 -struct usb_mixer_interface { - struct snd_usb_audio *chip; - unsigned int ctrlif; - struct list_head list; - unsigned int ignore_ctl_error; - struct urb *urb; - /* array[MAX_ID_ELEMS], indexed by unit id */ - struct usb_mixer_elem_info **id_elems; - - /* Sound Blaster remote control stuff */ - const struct rc_config *rc_cfg; - u32 rc_code; - wait_queue_head_t rc_waitq; - struct urb *rc_urb; - struct usb_ctrlrequest *rc_setup_packet; - u8 rc_buffer[6]; - - u8 audigy2nx_leds[3]; - u8 xonar_u1_status; -}; - - struct usb_audio_term { int id; int type; @@ -118,24 +68,6 @@ struct mixer_build { const struct usbmix_selector_map *selector_map; }; -#define MAX_CHANNELS 10 /* max logical channels */ - -struct usb_mixer_elem_info { - struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ - struct snd_ctl_elem_id *elem_id; - unsigned int id; - unsigned int control; /* CS or ICN (high byte) */ - unsigned int cmask; /* channel mask bitmap: 0 = master */ - int channels; - int val_type; - int min, max, res; - int dBmin, dBmax; - int cached; - int cache_val[MAX_CHANNELS]; - u8 initialized; -}; - enum { USB_MIXER_BOOLEAN, USB_MIXER_INV_BOOLEAN, @@ -431,7 +363,8 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval, * set a mixer value */ -static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) +int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, + int request, int validx, int value_set) { unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; @@ -455,14 +388,14 @@ static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value) { - return set_ctl_value(cval, UAC_SET_CUR, validx, value); + return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); } static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value) { int err; - err = set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel, + err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel, value); if (err < 0) return err; @@ -751,7 +684,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) int last_valid_res = cval->res; while (cval->res > 1) { - if (set_ctl_value(cval, UAC_SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0) + if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES, + (cval->control << 8) | minchn, cval->res / 2) < 0) break; cval->res /= 2; } @@ -1808,8 +1742,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) return 0; } -static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, - int unitid) +void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { struct usb_mixer_elem_info *info; @@ -1858,34 +1791,6 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, } } -static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer, - int unitid) -{ - if (!mixer->rc_cfg) - return; - /* unit ids specific to Extigy/Audigy 2 NX: */ - switch (unitid) { - case 0: /* remote control */ - mixer->rc_urb->dev = mixer->chip->dev; - usb_submit_urb(mixer->rc_urb, GFP_ATOMIC); - break; - case 4: /* digital in jack */ - case 7: /* line in jacks */ - case 19: /* speaker out jacks */ - case 20: /* headphones out jack */ - break; - /* live24ext: 4 = line-in jack */ - case 3: /* hp-out jack (may actuate Mute) */ - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) - snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id); - break; - default: - snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid); - break; - } -} - static void snd_usb_mixer_status_complete(struct urb *urb) { struct usb_mixer_interface *mixer = urb->context; @@ -1903,7 +1808,7 @@ static void snd_usb_mixer_status_complete(struct urb *urb) if (!(buf[0] & 0x40)) snd_usb_mixer_notify_id(mixer, buf[1]); else - snd_usb_mixer_memory_change(mixer, buf[1]); + snd_usb_mixer_rc_memory_change(mixer, buf[1]); } } if (urb->status != -ENOENT && urb->status != -ECONNRESET) { @@ -1947,296 +1852,6 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) return 0; } -static void snd_usb_soundblaster_remote_complete(struct urb *urb) -{ - struct usb_mixer_interface *mixer = urb->context; - const struct rc_config *rc = mixer->rc_cfg; - u32 code; - - if (urb->status < 0 || urb->actual_length < rc->min_packet_length) - return; - - code = mixer->rc_buffer[rc->offset]; - if (rc->length == 2) - code |= mixer->rc_buffer[rc->offset + 1] << 8; - - /* the Mute button actually changes the mixer control */ - if (code == rc->mute_code) - snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); - mixer->rc_code = code; - wmb(); - wake_up(&mixer->rc_waitq); -} - -static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, - long count, loff_t *offset) -{ - struct usb_mixer_interface *mixer = hw->private_data; - int err; - u32 rc_code; - - if (count != 1 && count != 4) - return -EINVAL; - err = wait_event_interruptible(mixer->rc_waitq, - (rc_code = xchg(&mixer->rc_code, 0)) != 0); - if (err == 0) { - if (count == 1) - err = put_user(rc_code, buf); - else - err = put_user(rc_code, (u32 __user *)buf); - } - return err < 0 ? err : count; -} - -static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, - poll_table *wait) -{ - struct usb_mixer_interface *mixer = hw->private_data; - - poll_wait(file, &mixer->rc_waitq, wait); - return mixer->rc_code ? POLLIN | POLLRDNORM : 0; -} - -static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) -{ - struct snd_hwdep *hwdep; - int err, len, i; - - for (i = 0; i < ARRAY_SIZE(rc_configs); ++i) - if (rc_configs[i].usb_id == mixer->chip->usb_id) - break; - if (i >= ARRAY_SIZE(rc_configs)) - return 0; - mixer->rc_cfg = &rc_configs[i]; - - len = mixer->rc_cfg->packet_length; - - init_waitqueue_head(&mixer->rc_waitq); - err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); - if (err < 0) - return err; - snprintf(hwdep->name, sizeof(hwdep->name), - "%s remote control", mixer->chip->card->shortname); - hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC; - hwdep->private_data = mixer; - hwdep->ops.read = snd_usb_sbrc_hwdep_read; - hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; - hwdep->exclusive = 1; - - mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->rc_urb) - return -ENOMEM; - mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL); - if (!mixer->rc_setup_packet) { - usb_free_urb(mixer->rc_urb); - mixer->rc_urb = NULL; - return -ENOMEM; - } - mixer->rc_setup_packet->bRequestType = - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - mixer->rc_setup_packet->bRequest = UAC_GET_MEM; - mixer->rc_setup_packet->wValue = cpu_to_le16(0); - mixer->rc_setup_packet->wIndex = cpu_to_le16(0); - mixer->rc_setup_packet->wLength = cpu_to_le16(len); - usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), - (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, - snd_usb_soundblaster_remote_complete, mixer); - return 0; -} - -#define snd_audigy2nx_led_info snd_ctl_boolean_mono_info - -static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - - ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; - return 0; -} - -static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - int err, changed; - - if (value > 1) - return -EINVAL; - changed = value != mixer->audigy2nx_leds[index]; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - value, index + 2, NULL, 0, 100); - if (err < 0) - return err; - mixer->audigy2nx_leds[index] = value; - return changed; -} - -static struct snd_kcontrol_new snd_audigy2nx_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "CMSS LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 0, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Power LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 1, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dolby Digital LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 2, - }, -}; - -static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) -{ - int i, err; - - for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { - if (i > 1 && /* Live24ext has 2 LEDs only */ - (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) - break; - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); - if (err < 0) - return err; - } - mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ - return 0; -} - -static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - static const struct sb_jack { - int unitid; - const char *name; - } jacks_audigy2nx[] = { - {4, "dig in "}, - {7, "line in"}, - {19, "spk out"}, - {20, "hph out"}, - {-1, NULL} - }, jacks_live24ext[] = { - {4, "line in"}, /* &1=Line, &2=Mic*/ - {3, "hph out"}, /* headphones */ - {0, "RC "}, /* last command, 6 bytes see rc_config above */ - {-1, NULL} - }; - const struct sb_jack *jacks; - struct usb_mixer_interface *mixer = entry->private_data; - int i, err; - u8 buf[3]; - - snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname); - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) - jacks = jacks_audigy2nx; - else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) - jacks = jacks_live24ext; - else - return; - - for (i = 0; jacks[i].name; ++i) { - snd_iprintf(buffer, "%s: ", jacks[i].name); - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), - UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, - jacks[i].unitid << 8, buf, 3, 100); - if (err == 3 && (buf[0] == 3 || buf[0] == 6)) - snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); - else - snd_iprintf(buffer, "?\n"); - } -} - -static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); - return 0; -} - -static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - u8 old_status, new_status; - int err, changed; - - old_status = mixer->xonar_u1_status; - if (ucontrol->value.integer.value[0]) - new_status = old_status | 0x02; - else - new_status = old_status & ~0x02; - changed = new_status != old_status; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 50, 0, &new_status, 1, 100); - if (err < 0) - return err; - mixer->xonar_u1_status = new_status; - return changed; -} - -static struct snd_kcontrol_new snd_xonar_u1_output_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = snd_xonar_u1_switch_get, - .put = snd_xonar_u1_switch_put, -}; - -static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) -{ - int err; - - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); - if (err < 0) - return err; - mixer->xonar_u1_status = 0x05; - return 0; -} - -void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, - unsigned char samplerate_id) -{ - struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *cval; - int unitid = 12; /* SamleRate ExtensionUnit ID */ - - list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer->id_elems[unitid]; - if (cval) { - set_cur_ctl_value(cval, cval->control << 8, - samplerate_id); - snd_usb_mixer_notify_id(mixer, unitid); - } - break; - } -} - int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error) { @@ -2277,25 +1892,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, (err = snd_usb_mixer_status_create(mixer)) < 0) goto _error; - if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) - goto _error; - - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) { - if ((err = snd_audigy2nx_controls_create(mixer)) < 0) - goto _error; - if (!snd_card_proc_new(chip->card, "audigy2nx", &entry)) - snd_info_set_text_ops(entry, mixer, - snd_audigy2nx_proc_read); - } - - if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) || - mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) { - err = snd_xonar_u1_controls_create(mixer); - if (err < 0) - goto _error; - } + snd_usb_mixer_apply_create_quirk(mixer); err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); if (err < 0) @@ -2316,7 +1913,7 @@ _error: void snd_usb_mixer_disconnect(struct list_head *p) { struct usb_mixer_interface *mixer; - + mixer = list_entry(p, struct usb_mixer_interface, list); usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb); diff --git a/sound/usb/usbmixer.h b/sound/usb/usbmixer.h index e199e4b..63101ae 100644 --- a/sound/usb/usbmixer.h +++ b/sound/usb/usbmixer.h @@ -1,11 +1,52 @@ #ifndef __USBMIXER_H #define __USBMIXER_H +struct usb_mixer_interface { + struct snd_usb_audio *chip; + unsigned int ctrlif; + struct list_head list; + unsigned int ignore_ctl_error; + struct urb *urb; + /* array[MAX_ID_ELEMS], indexed by unit id */ + struct usb_mixer_elem_info **id_elems; + + /* Sound Blaster remote control stuff */ + const struct rc_config *rc_cfg; + u32 rc_code; + wait_queue_head_t rc_waitq; + struct urb *rc_urb; + struct usb_ctrlrequest *rc_setup_packet; + u8 rc_buffer[6]; + + u8 audigy2nx_leds[3]; + u8 xonar_u1_status; +}; + +#define MAX_CHANNELS 10 /* max logical channels */ + +struct usb_mixer_elem_info { + struct usb_mixer_interface *mixer; + struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ + struct snd_ctl_elem_id *elem_id; + unsigned int id; + unsigned int control; /* CS or ICN (high byte) */ + unsigned int cmask; /* channel mask bitmap: 0 = master */ + int channels; + int val_type; + int min, max, res; + int dBmin, dBmax; + int cached; + int cache_val[MAX_CHANNELS]; + u8 initialized; +}; + int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error); void snd_usb_mixer_disconnect(struct list_head *p); -void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, - unsigned char samplerate_id); +void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); + +int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, + int request, int validx, int value_set); #endif /* __USBMIXER_H */ -- cgit v1.1 From f0b5e634ff25e02a64676022ee13284a9c810879 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:23 +0100 Subject: ALSA: usbmixer: rename usbmixer.[ch] -> mixer.[ch] For clearer namespace, also rename usbmixer_maps.c -> mixer_maps.c Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 2 +- sound/usb/card.c | 2 +- sound/usb/mixer.c | 1920 +++++++++++++++++++++++++++++++++++++++++++++ sound/usb/mixer.h | 52 ++ sound/usb/mixer_maps.c | 376 +++++++++ sound/usb/mixer_quirks.c | 2 +- sound/usb/quirks.c | 2 +- sound/usb/usbmixer.c | 1920 --------------------------------------------- sound/usb/usbmixer.h | 52 -- sound/usb/usbmixer_maps.c | 376 --------- 10 files changed, 2352 insertions(+), 2352 deletions(-) create mode 100644 sound/usb/mixer.c create mode 100644 sound/usb/mixer.h create mode 100644 sound/usb/mixer_maps.c delete mode 100644 sound/usb/usbmixer.c delete mode 100644 sound/usb/usbmixer.h delete mode 100644 sound/usb/usbmixer_maps.c (limited to 'sound') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 744024a..e7ac7f4 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -3,7 +3,7 @@ # snd-usb-audio-objs := card.o \ - usbmixer.o \ + mixer.o \ mixer_quirks.o \ proc.o \ quirks.o \ diff --git a/sound/usb/card.c b/sound/usb/card.c index 78d12ff..0bd62a1 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -56,7 +56,7 @@ #include "usbaudio.h" #include "card.h" #include "midi.h" -#include "usbmixer.h" +#include "mixer.h" #include "proc.h" #include "quirks.h" #include "endpoint.h" diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c new file mode 100644 index 0000000..4e7c2fd --- /dev/null +++ b/sound/usb/mixer.c @@ -0,0 +1,1920 @@ +/* + * (Tentative) USB Audio Driver for ALSA + * + * Mixer control part + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" +#include "mixer_quirks.h" + +#define MAX_ID_ELEMS 256 + +struct usb_audio_term { + int id; + int type; + int channels; + unsigned int chconfig; + int name; +}; + +struct usbmix_name_map; + +struct mixer_build { + struct snd_usb_audio *chip; + struct usb_mixer_interface *mixer; + unsigned char *buffer; + unsigned int buflen; + DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); + struct usb_audio_term oterm; + const struct usbmix_name_map *map; + const struct usbmix_selector_map *selector_map; +}; + +enum { + USB_MIXER_BOOLEAN, + USB_MIXER_INV_BOOLEAN, + USB_MIXER_S8, + USB_MIXER_U8, + USB_MIXER_S16, + USB_MIXER_U16, +}; + +enum { + USB_PROC_UPDOWN = 1, + USB_PROC_UPDOWN_SWITCH = 1, + USB_PROC_UPDOWN_MODE_SEL = 2, + + USB_PROC_PROLOGIC = 2, + USB_PROC_PROLOGIC_SWITCH = 1, + USB_PROC_PROLOGIC_MODE_SEL = 2, + + USB_PROC_3DENH = 3, + USB_PROC_3DENH_SWITCH = 1, + USB_PROC_3DENH_SPACE = 2, + + USB_PROC_REVERB = 4, + USB_PROC_REVERB_SWITCH = 1, + USB_PROC_REVERB_LEVEL = 2, + USB_PROC_REVERB_TIME = 3, + USB_PROC_REVERB_DELAY = 4, + + USB_PROC_CHORUS = 5, + USB_PROC_CHORUS_SWITCH = 1, + USB_PROC_CHORUS_LEVEL = 2, + USB_PROC_CHORUS_RATE = 3, + USB_PROC_CHORUS_DEPTH = 4, + + USB_PROC_DCR = 6, + USB_PROC_DCR_SWITCH = 1, + USB_PROC_DCR_RATIO = 2, + USB_PROC_DCR_MAX_AMP = 3, + USB_PROC_DCR_THRESHOLD = 4, + USB_PROC_DCR_ATTACK = 5, + USB_PROC_DCR_RELEASE = 6, +}; + +/*E-mu 0202(0404) eXtension Unit(XU) control*/ +enum { + USB_XU_CLOCK_RATE = 0xe301, + USB_XU_CLOCK_SOURCE = 0xe302, + USB_XU_DIGITAL_IO_STATUS = 0xe303, + USB_XU_DEVICE_OPTIONS = 0xe304, + USB_XU_DIRECT_MONITORING = 0xe305, + USB_XU_METERING = 0xe306 +}; +enum { + USB_XU_CLOCK_SOURCE_SELECTOR = 0x02, /* clock source*/ + USB_XU_CLOCK_RATE_SELECTOR = 0x03, /* clock rate */ + USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01, /* the spdif format */ + USB_XU_SOFT_LIMIT_SELECTOR = 0x03 /* soft limiter */ +}; + +/* + * manual mapping of mixer names + * if the mixer topology is too complicated and the parsed names are + * ambiguous, add the entries in usbmixer_maps.c. + */ +#include "mixer_maps.c" + +static const struct usbmix_name_map * +find_map(struct mixer_build *state, int unitid, int control) +{ + const struct usbmix_name_map *p = state->map; + + if (!p) + return NULL; + + for (p = state->map; p->id; p++) { + if (p->id == unitid && + (!control || !p->control || control == p->control)) + return p; + } + return NULL; +} + +/* get the mapped name if the unit matches */ +static int +check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) +{ + if (!p || !p->name) + return 0; + + buflen--; + return strlcpy(buf, p->name, buflen); +} + +/* check whether the control should be ignored */ +static inline int +check_ignored_ctl(const struct usbmix_name_map *p) +{ + if (!p || p->name || p->dB) + return 0; + return 1; +} + +/* dB mapping */ +static inline void check_mapped_dB(const struct usbmix_name_map *p, + struct usb_mixer_elem_info *cval) +{ + if (p && p->dB) { + cval->dBmin = p->dB->min; + cval->dBmax = p->dB->max; + } +} + +/* get the mapped selector source name */ +static int check_mapped_selector_name(struct mixer_build *state, int unitid, + int index, char *buf, int buflen) +{ + const struct usbmix_selector_map *p; + + if (! state->selector_map) + return 0; + for (p = state->selector_map; p->id; p++) { + if (p->id == unitid && index < p->count) + return strlcpy(buf, p->names[index], buflen); + } + return 0; +} + +/* + * find an audio control unit with the given unit id + */ +static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit) +{ + unsigned char *p; + + p = NULL; + while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, + USB_DT_CS_INTERFACE)) != NULL) { + if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit) + return p; + } + return NULL; +} + + +/* + * copy a string with the given id + */ +static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen) +{ + int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + buf[len] = 0; + return len; +} + +/* + * convert from the byte/word on usb descriptor to the zero-based integer + */ +static int convert_signed_value(struct usb_mixer_elem_info *cval, int val) +{ + switch (cval->val_type) { + case USB_MIXER_BOOLEAN: + return !!val; + case USB_MIXER_INV_BOOLEAN: + return !val; + case USB_MIXER_U8: + val &= 0xff; + break; + case USB_MIXER_S8: + val &= 0xff; + if (val >= 0x80) + val -= 0x100; + break; + case USB_MIXER_U16: + val &= 0xffff; + break; + case USB_MIXER_S16: + val &= 0xffff; + if (val >= 0x8000) + val -= 0x10000; + break; + } + return val; +} + +/* + * convert from the zero-based int to the byte/word for usb descriptor + */ +static int convert_bytes_value(struct usb_mixer_elem_info *cval, int val) +{ + switch (cval->val_type) { + case USB_MIXER_BOOLEAN: + return !!val; + case USB_MIXER_INV_BOOLEAN: + return !val; + case USB_MIXER_S8: + case USB_MIXER_U8: + return val & 0xff; + case USB_MIXER_S16: + case USB_MIXER_U16: + return val & 0xffff; + } + return 0; /* not reached */ +} + +static int get_relative_value(struct usb_mixer_elem_info *cval, int val) +{ + if (! cval->res) + cval->res = 1; + if (val < cval->min) + return 0; + else if (val >= cval->max) + return (cval->max - cval->min + cval->res - 1) / cval->res; + else + return (val - cval->min) / cval->res; +} + +static int get_abs_value(struct usb_mixer_elem_info *cval, int val) +{ + if (val < 0) + return cval->min; + if (! cval->res) + cval->res = 1; + val *= cval->res; + val += cval->min; + if (val > cval->max) + return cval->max; + return val; +} + + +/* + * retrieve a mixer value + */ + +static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) +{ + unsigned char buf[2]; + int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + int timeout = 10; + + while (timeout-- > 0) { + if (snd_usb_ctl_msg(cval->mixer->chip->dev, + usb_rcvctrlpipe(cval->mixer->chip->dev, 0), + request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, cval->mixer->ctrlif | (cval->id << 8), + buf, val_len, 100) >= val_len) { + *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); + return 0; + } + } + snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", + request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type); + return -EINVAL; +} + +static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value) +{ + return get_ctl_value(cval, UAC_GET_CUR, validx, value); +} + +/* channel = 0: master, 1 = first channel */ +static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, + int channel, int *value) +{ + return get_ctl_value(cval, UAC_GET_CUR, (cval->control << 8) | channel, value); +} + +static int get_cur_mix_value(struct usb_mixer_elem_info *cval, + int channel, int index, int *value) +{ + int err; + + if (cval->cached & (1 << channel)) { + *value = cval->cache_val[index]; + return 0; + } + err = get_cur_mix_raw(cval, channel, value); + if (err < 0) { + if (!cval->mixer->ignore_ctl_error) + snd_printd(KERN_ERR "cannot get current value for " + "control %d ch %d: err = %d\n", + cval->control, channel, err); + return err; + } + cval->cached |= 1 << channel; + cval->cache_val[index] = *value; + return 0; +} + + +/* + * set a mixer value + */ + +int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, + int request, int validx, int value_set) +{ + unsigned char buf[2]; + int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + int timeout = 10; + + value_set = convert_bytes_value(cval, value_set); + buf[0] = value_set & 0xff; + buf[1] = (value_set >> 8) & 0xff; + while (timeout-- > 0) + if (snd_usb_ctl_msg(cval->mixer->chip->dev, + usb_sndctrlpipe(cval->mixer->chip->dev, 0), + request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + validx, cval->mixer->ctrlif | (cval->id << 8), + buf, val_len, 100) >= 0) + return 0; + snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", + request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]); + return -EINVAL; +} + +static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value) +{ + return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); +} + +static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, + int index, int value) +{ + int err; + err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel, + value); + if (err < 0) + return err; + cval->cached |= 1 << channel; + cval->cache_val[index] = value; + return 0; +} + +/* + * TLV callback for mixer volume controls + */ +static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + DECLARE_TLV_DB_MINMAX(scale, 0, 0); + + if (size < sizeof(scale)) + return -ENOMEM; + scale[2] = cval->dBmin; + scale[3] = cval->dBmax; + if (copy_to_user(_tlv, scale, sizeof(scale))) + return -EFAULT; + return 0; +} + +/* + * parser routines begin here... + */ + +static int parse_audio_unit(struct mixer_build *state, int unitid); + + +/* + * check if the input/output channel routing is enabled on the given bitmap. + * used for mixer unit parser + */ +static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs) +{ + int idx = ich * num_outs + och; + return bmap[idx >> 3] & (0x80 >> (idx & 7)); +} + + +/* + * add an alsa control element + * search and increment the index until an empty slot is found. + * + * if failed, give up and free the control instance. + */ + +static int add_control_to_empty(struct mixer_build *state, struct snd_kcontrol *kctl) +{ + struct usb_mixer_elem_info *cval = kctl->private_data; + int err; + + while (snd_ctl_find_id(state->chip->card, &kctl->id)) + kctl->id.index++; + if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { + snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); + return err; + } + cval->elem_id = &kctl->id; + cval->next_id_elem = state->mixer->id_elems[cval->id]; + state->mixer->id_elems[cval->id] = cval; + return 0; +} + + +/* + * get a terminal name string + */ + +static struct iterm_name_combo { + int type; + char *name; +} iterm_names[] = { + { 0x0300, "Output" }, + { 0x0301, "Speaker" }, + { 0x0302, "Headphone" }, + { 0x0303, "HMD Audio" }, + { 0x0304, "Desktop Speaker" }, + { 0x0305, "Room Speaker" }, + { 0x0306, "Com Speaker" }, + { 0x0307, "LFE" }, + { 0x0600, "External In" }, + { 0x0601, "Analog In" }, + { 0x0602, "Digital In" }, + { 0x0603, "Line" }, + { 0x0604, "Legacy In" }, + { 0x0605, "IEC958 In" }, + { 0x0606, "1394 DA Stream" }, + { 0x0607, "1394 DV Stream" }, + { 0x0700, "Embedded" }, + { 0x0701, "Noise Source" }, + { 0x0702, "Equalization Noise" }, + { 0x0703, "CD" }, + { 0x0704, "DAT" }, + { 0x0705, "DCC" }, + { 0x0706, "MiniDisk" }, + { 0x0707, "Analog Tape" }, + { 0x0708, "Phonograph" }, + { 0x0709, "VCR Audio" }, + { 0x070a, "Video Disk Audio" }, + { 0x070b, "DVD Audio" }, + { 0x070c, "TV Tuner Audio" }, + { 0x070d, "Satellite Rec Audio" }, + { 0x070e, "Cable Tuner Audio" }, + { 0x070f, "DSS Audio" }, + { 0x0710, "Radio Receiver" }, + { 0x0711, "Radio Transmitter" }, + { 0x0712, "Multi-Track Recorder" }, + { 0x0713, "Synthesizer" }, + { 0 }, +}; + +static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, + unsigned char *name, int maxlen, int term_only) +{ + struct iterm_name_combo *names; + + if (iterm->name) + return snd_usb_copy_string_desc(state, iterm->name, name, maxlen); + + /* virtual type - not a real terminal */ + if (iterm->type >> 16) { + if (term_only) + return 0; + switch (iterm->type >> 16) { + case UAC_SELECTOR_UNIT: + strcpy(name, "Selector"); return 8; + case UAC_PROCESSING_UNIT_V1: + strcpy(name, "Process Unit"); return 12; + case UAC_EXTENSION_UNIT_V1: + strcpy(name, "Ext Unit"); return 8; + case UAC_MIXER_UNIT: + strcpy(name, "Mixer"); return 5; + default: + return sprintf(name, "Unit %d", iterm->id); + } + } + + switch (iterm->type & 0xff00) { + case 0x0100: + strcpy(name, "PCM"); return 3; + case 0x0200: + strcpy(name, "Mic"); return 3; + case 0x0400: + strcpy(name, "Headset"); return 7; + case 0x0500: + strcpy(name, "Phone"); return 5; + } + + for (names = iterm_names; names->type; names++) + if (names->type == iterm->type) { + strcpy(name, names->name); + return strlen(names->name); + } + return 0; +} + + +/* + * parse the source unit recursively until it reaches to a terminal + * or a branched unit. + */ +static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) +{ + unsigned char *p1; + + memset(term, 0, sizeof(*term)); + while ((p1 = find_audio_control_unit(state, id)) != NULL) { + term->id = id; + switch (p1[2]) { + case UAC_INPUT_TERMINAL: + term->type = combine_word(p1 + 4); + term->channels = p1[7]; + term->chconfig = combine_word(p1 + 8); + term->name = p1[11]; + return 0; + case UAC_FEATURE_UNIT: + id = p1[4]; + break; /* continue to parse */ + case UAC_MIXER_UNIT: + term->type = p1[2] << 16; /* virtual type */ + term->channels = p1[5 + p1[4]]; + term->chconfig = combine_word(p1 + 6 + p1[4]); + term->name = p1[p1[0] - 1]; + return 0; + case UAC_SELECTOR_UNIT: + /* call recursively to retrieve the channel info */ + if (check_input_term(state, p1[5], term) < 0) + return -ENODEV; + term->type = p1[2] << 16; /* virtual type */ + term->id = id; + term->name = p1[9 + p1[0] - 1]; + return 0; + case UAC_PROCESSING_UNIT_V1: + case UAC_EXTENSION_UNIT_V1: + if (p1[6] == 1) { + id = p1[7]; + break; /* continue to parse */ + } + term->type = p1[2] << 16; /* virtual type */ + term->channels = p1[7 + p1[6]]; + term->chconfig = combine_word(p1 + 8 + p1[6]); + term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; + return 0; + default: + return -ENODEV; + } + } + return -ENODEV; +} + + +/* + * Feature Unit + */ + +/* feature unit control information */ +struct usb_feature_control_info { + const char *name; + unsigned int type; /* control type (mute, volume, etc.) */ +}; + +static struct usb_feature_control_info audio_feature_info[] = { + { "Mute", USB_MIXER_INV_BOOLEAN }, + { "Volume", USB_MIXER_S16 }, + { "Tone Control - Bass", USB_MIXER_S8 }, + { "Tone Control - Mid", USB_MIXER_S8 }, + { "Tone Control - Treble", USB_MIXER_S8 }, + { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ + { "Auto Gain Control", USB_MIXER_BOOLEAN }, + { "Delay Control", USB_MIXER_U16 }, + { "Bass Boost", USB_MIXER_BOOLEAN }, + { "Loudness", USB_MIXER_BOOLEAN }, +}; + + +/* private_free callback */ +static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +{ + kfree(kctl->private_data); + kctl->private_data = NULL; +} + + +/* + * interface to ALSA control for feature/mixer units + */ + +/* + * retrieve the minimum and maximum values for the specified control + */ +static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) +{ + /* for failsafe */ + cval->min = default_min; + cval->max = cval->min + 1; + cval->res = 1; + cval->dBmin = cval->dBmax = 0; + + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) { + cval->initialized = 1; + } else { + int minchn = 0; + if (cval->cmask) { + int i; + for (i = 0; i < MAX_CHANNELS; i++) + if (cval->cmask & (1 << i)) { + minchn = i + 1; + break; + } + } + if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { + snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", + cval->id, cval->mixer->ctrlif, cval->control, cval->id); + return -EINVAL; + } + if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { + cval->res = 1; + } else { + int last_valid_res = cval->res; + + while (cval->res > 1) { + if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES, + (cval->control << 8) | minchn, cval->res / 2) < 0) + break; + cval->res /= 2; + } + if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) + cval->res = last_valid_res; + } + if (cval->res == 0) + cval->res = 1; + + /* Additional checks for the proper resolution + * + * Some devices report smaller resolutions than actually + * reacting. They don't return errors but simply clip + * to the lower aligned value. + */ + if (cval->min + cval->res < cval->max) { + int last_valid_res = cval->res; + int saved, test, check; + get_cur_mix_raw(cval, minchn, &saved); + for (;;) { + test = saved; + if (test < cval->max) + test += cval->res; + else + test -= cval->res; + if (test < cval->min || test > cval->max || + set_cur_mix_value(cval, minchn, 0, test) || + get_cur_mix_raw(cval, minchn, &check)) { + cval->res = last_valid_res; + break; + } + if (test == check) + break; + cval->res *= 2; + } + set_cur_mix_value(cval, minchn, 0, saved); + } + + cval->initialized = 1; + } + + /* USB descriptions contain the dB scale in 1/256 dB unit + * while ALSA TLV contains in 1/100 dB unit + */ + cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256; + cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256; + if (cval->dBmin > cval->dBmax) { + /* something is wrong; assume it's either from/to 0dB */ + if (cval->dBmin < 0) + cval->dBmax = 0; + else if (cval->dBmin > 0) + cval->dBmin = 0; + if (cval->dBmin > cval->dBmax) { + /* totally crap, return an error */ + return -EINVAL; + } + } + + return 0; +} + + +/* get a feature/mixer unit info */ +static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = cval->channels; + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) { + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } else { + if (! cval->initialized) + get_min_max(cval, 0); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = + (cval->max - cval->min + cval->res - 1) / cval->res; + } + return 0; +} + +/* get the current value from feature/mixer unit */ +static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int c, cnt, val, err; + + ucontrol->value.integer.value[0] = cval->min; + if (cval->cmask) { + cnt = 0; + for (c = 0; c < MAX_CHANNELS; c++) { + if (!(cval->cmask & (1 << c))) + continue; + err = get_cur_mix_value(cval, c + 1, cnt, &val); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = get_relative_value(cval, val); + ucontrol->value.integer.value[cnt] = val; + cnt++; + } + return 0; + } else { + /* master channel */ + err = get_cur_mix_value(cval, 0, 0, &val); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = get_relative_value(cval, val); + ucontrol->value.integer.value[0] = val; + } + return 0; +} + +/* put the current value to feature/mixer unit */ +static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int c, cnt, val, oval, err; + int changed = 0; + + if (cval->cmask) { + cnt = 0; + for (c = 0; c < MAX_CHANNELS; c++) { + if (!(cval->cmask & (1 << c))) + continue; + err = get_cur_mix_value(cval, c + 1, cnt, &oval); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = ucontrol->value.integer.value[cnt]; + val = get_abs_value(cval, val); + if (oval != val) { + set_cur_mix_value(cval, c + 1, cnt, val); + changed = 1; + } + cnt++; + } + } else { + /* master channel */ + err = get_cur_mix_value(cval, 0, 0, &oval); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = ucontrol->value.integer.value[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_mix_value(cval, 0, 0, val); + changed = 1; + } + } + return changed; +} + +static struct snd_kcontrol_new usb_feature_unit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later manually */ + .info = mixer_ctl_feature_info, + .get = mixer_ctl_feature_get, + .put = mixer_ctl_feature_put, +}; + + +/* + * build a feature control + */ + +static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) +{ + return strlcat(kctl->id.name, str, sizeof(kctl->id.name)); +} + +static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, int unitid) +{ + unsigned int len = 0; + int mapped_name = 0; + int nameid = desc[desc[0] - 1]; + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *cval; + const struct usbmix_name_map *map; + + control++; /* change from zero-based to 1-based value */ + + if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) { + /* FIXME: not supported yet */ + return; + } + + map = find_map(state, unitid, control); + if (check_ignored_ctl(map)) + return; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return; + } + cval->mixer = state->mixer; + cval->id = unitid; + cval->control = control; + cval->cmask = ctl_mask; + cval->val_type = audio_feature_info[control-1].type; + if (ctl_mask == 0) + cval->channels = 1; /* master channel */ + else { + int i, c = 0; + for (i = 0; i < 16; i++) + if (ctl_mask & (1 << i)) + c++; + cval->channels = c; + } + + /* get min/max values */ + get_min_max(cval, 0); + + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + kfree(cval); + return; + } + kctl->private_free = usb_mixer_elem_free; + + len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); + mapped_name = len != 0; + if (! len && nameid) + len = snd_usb_copy_string_desc(state, nameid, + kctl->id.name, sizeof(kctl->id.name)); + + switch (control) { + case UAC_MUTE_CONTROL: + case UAC_VOLUME_CONTROL: + /* determine the control name. the rule is: + * - if a name id is given in descriptor, use it. + * - if the connected input can be determined, then use the name + * of terminal type. + * - if the connected output can be determined, use it. + * - otherwise, anonymous name. + */ + if (! len) { + len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); + if (! len) + len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); + if (! len) + len = snprintf(kctl->id.name, sizeof(kctl->id.name), + "Feature %d", unitid); + } + /* determine the stream direction: + * if the connected output is USB stream, then it's likely a + * capture stream. otherwise it should be playback (hopefully :) + */ + if (! mapped_name && ! (state->oterm.type >> 16)) { + if ((state->oterm.type & 0xff00) == 0x0100) { + len = append_ctl_name(kctl, " Capture"); + } else { + len = append_ctl_name(kctl, " Playback"); + } + } + append_ctl_name(kctl, control == UAC_MUTE_CONTROL ? + " Switch" : " Volume"); + if (control == UAC_VOLUME_CONTROL) { + kctl->tlv.c = mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + check_mapped_dB(map, cval); + } + break; + + default: + if (! len) + strlcpy(kctl->id.name, audio_feature_info[control-1].name, + sizeof(kctl->id.name)); + break; + } + + /* volume control quirks */ + switch (state->chip->usb_id) { + case USB_ID(0x0471, 0x0101): + case USB_ID(0x0471, 0x0104): + case USB_ID(0x0471, 0x0105): + case USB_ID(0x0672, 0x1041): + /* quirk for UDA1321/N101. + * note that detection between firmware 2.1.1.7 (N101) + * and later 2.1.1.21 is not very clear from datasheets. + * I hope that the min value is -15360 for newer firmware --jk + */ + if (!strcmp(kctl->id.name, "PCM Playback Volume") && + cval->min == -15616) { + snd_printk(KERN_INFO + "set volume quirk for UDA1321/N101 chip\n"); + cval->max = -256; + } + break; + + case USB_ID(0x046d, 0x09a4): + if (!strcmp(kctl->id.name, "Mic Capture Volume")) { + snd_printk(KERN_INFO + "set volume quirk for QuickCam E3500\n"); + cval->min = 6080; + cval->max = 8768; + cval->res = 192; + } + break; + + } + + snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); + add_control_to_empty(state, kctl); +} + + + +/* + * parse a feature unit + * + * most of controlls are defined here. + */ +static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr) +{ + int channels, i, j; + struct usb_audio_term iterm; + unsigned int master_bits, first_ch_bits; + int err, csize; + struct uac_feature_unit_descriptor *ftr = _ftr; + + if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) { + snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); + return -EINVAL; + } + + /* parse the source unit */ + if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0) + return err; + + /* determine the input source type and name */ + if (check_input_term(state, ftr->bSourceID, &iterm) < 0) + return -EINVAL; + + channels = (ftr->bLength - 7) / csize - 1; + + master_bits = snd_usb_combine_bytes(ftr->controls, csize); + /* master configuration quirks */ + switch (state->chip->usb_id) { + case USB_ID(0x08bb, 0x2702): + snd_printk(KERN_INFO + "usbmixer: master volume quirk for PCM2702 chip\n"); + /* disable non-functional volume control */ + master_bits &= ~UAC_FU_VOLUME; + break; + } + if (channels > 0) + first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize); + else + first_ch_bits = 0; + /* check all control types */ + for (i = 0; i < 10; i++) { + unsigned int ch_bits = 0; + for (j = 0; j < channels; j++) { + unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize); + if (mask & (1 << i)) + ch_bits |= (1 << j); + } + if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ + build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid); + if (master_bits & (1 << i)) + build_feature_ctl(state, _ftr, 0, i, &iterm, unitid); + } + + return 0; +} + + +/* + * Mixer Unit + */ + +/* + * build a mixer unit control + * + * the callbacks are identical with feature unit. + * input channel number (zero based) is given in control field instead. + */ + +static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, + int in_pin, int in_ch, int unitid, + struct usb_audio_term *iterm) +{ + struct usb_mixer_elem_info *cval; + unsigned int input_pins = desc[4]; + unsigned int num_outs = desc[5 + input_pins]; + unsigned int i, len; + struct snd_kcontrol *kctl; + const struct usbmix_name_map *map; + + map = find_map(state, unitid, 0); + if (check_ignored_ctl(map)) + return; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (! cval) + return; + + cval->mixer = state->mixer; + cval->id = unitid; + cval->control = in_ch + 1; /* based on 1 */ + cval->val_type = USB_MIXER_S16; + for (i = 0; i < num_outs; i++) { + if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) { + cval->cmask |= (1 << i); + cval->channels++; + } + } + + /* get min/max values */ + get_min_max(cval, 0); + + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + kfree(cval); + return; + } + kctl->private_free = usb_mixer_elem_free; + + len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); + if (! len) + len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0); + if (! len) + len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); + append_ctl_name(kctl, " Volume"); + + snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max); + add_control_to_empty(state, kctl); +} + + +/* + * parse a mixer unit + */ +static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc) +{ + struct usb_audio_term iterm; + int input_pins, num_ins, num_outs; + int pin, ich, err; + + if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) { + snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid); + return -EINVAL; + } + /* no bmControls field (e.g. Maya44) -> ignore */ + if (desc[0] <= 10 + input_pins) { + snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid); + return 0; + } + + num_ins = 0; + ich = 0; + for (pin = 0; pin < input_pins; pin++) { + err = parse_audio_unit(state, desc[5 + pin]); + if (err < 0) + return err; + err = check_input_term(state, desc[5 + pin], &iterm); + if (err < 0) + return err; + num_ins += iterm.channels; + for (; ich < num_ins; ++ich) { + int och, ich_has_controls = 0; + + for (och = 0; och < num_outs; ++och) { + if (check_matrix_bitmap(desc + 9 + input_pins, + ich, och, num_outs)) { + ich_has_controls = 1; + break; + } + } + if (ich_has_controls) + build_mixer_unit_ctl(state, desc, pin, ich, + unitid, &iterm); + } + } + return 0; +} + + +/* + * Processing Unit / Extension Unit + */ + +/* get callback for processing/extension unit */ +static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int err, val; + + err = get_cur_ctl_value(cval, cval->control << 8, &val); + if (err < 0 && cval->mixer->ignore_ctl_error) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } + if (err < 0) + return err; + val = get_relative_value(cval, val); + ucontrol->value.integer.value[0] = val; + return 0; +} + +/* put callback for processing/extension unit */ +static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int val, oval, err; + + err = get_cur_ctl_value(cval, cval->control << 8, &oval); + if (err < 0) { + if (cval->mixer->ignore_ctl_error) + return 0; + return err; + } + val = ucontrol->value.integer.value[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_ctl_value(cval, cval->control << 8, val); + return 1; + } + return 0; +} + +/* alsa control interface for processing/extension unit */ +static struct snd_kcontrol_new mixer_procunit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = mixer_ctl_feature_info, + .get = mixer_ctl_procunit_get, + .put = mixer_ctl_procunit_put, +}; + + +/* + * predefined data for processing units + */ +struct procunit_value_info { + int control; + char *suffix; + int val_type; + int min_value; +}; + +struct procunit_info { + int type; + char *name; + struct procunit_value_info *values; +}; + +static struct procunit_value_info updown_proc_info[] = { + { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, + { 0 } +}; +static struct procunit_value_info prologic_proc_info[] = { + { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, + { 0 } +}; +static struct procunit_value_info threed_enh_proc_info[] = { + { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 }, + { 0 } +}; +static struct procunit_value_info reverb_proc_info[] = { + { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 }, + { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 }, + { 0 } +}; +static struct procunit_value_info chorus_proc_info[] = { + { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, + { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, + { 0 } +}; +static struct procunit_value_info dcr_proc_info[] = { + { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 }, + { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 }, + { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 }, + { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 }, + { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 }, + { 0 } +}; + +static struct procunit_info procunits[] = { + { USB_PROC_UPDOWN, "Up Down", updown_proc_info }, + { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info }, + { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info }, + { USB_PROC_REVERB, "Reverb", reverb_proc_info }, + { USB_PROC_CHORUS, "Chorus", chorus_proc_info }, + { USB_PROC_DCR, "DCR", dcr_proc_info }, + { 0 }, +}; +/* + * predefined data for extension units + */ +static struct procunit_value_info clock_rate_xu_info[] = { + { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 }, + { 0 } +}; +static struct procunit_value_info clock_source_xu_info[] = { + { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN }, + { 0 } +}; +static struct procunit_value_info spdif_format_xu_info[] = { + { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN }, + { 0 } +}; +static struct procunit_value_info soft_limit_xu_info[] = { + { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN }, + { 0 } +}; +static struct procunit_info extunits[] = { + { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info }, + { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info }, + { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info }, + { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info }, + { 0 } +}; +/* + * build a processing/extension unit + */ +static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name) +{ + int num_ins = dsc[6]; + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + int i, err, nameid, type, len; + struct procunit_info *info; + struct procunit_value_info *valinfo; + const struct usbmix_name_map *map; + static struct procunit_value_info default_value_info[] = { + { 0x01, "Switch", USB_MIXER_BOOLEAN }, + { 0 } + }; + static struct procunit_info default_info = { + 0, NULL, default_value_info + }; + + if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { + snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); + return -EINVAL; + } + + for (i = 0; i < num_ins; i++) { + if ((err = parse_audio_unit(state, dsc[7 + i])) < 0) + return err; + } + + type = combine_word(&dsc[4]); + for (info = list; info && info->type; info++) + if (info->type == type) + break; + if (! info || ! info->type) + info = &default_info; + + for (valinfo = info->values; valinfo->control; valinfo++) { + /* FIXME: bitmap might be longer than 8bit */ + if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) + continue; + map = find_map(state, unitid, valinfo->control); + if (check_ignored_ctl(map)) + continue; + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return -ENOMEM; + } + cval->mixer = state->mixer; + cval->id = unitid; + cval->control = valinfo->control; + cval->val_type = valinfo->val_type; + cval->channels = 1; + + /* get min/max values */ + if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { + /* FIXME: hard-coded */ + cval->min = 1; + cval->max = dsc[15]; + cval->res = 1; + cval->initialized = 1; + } else { + if (type == USB_XU_CLOCK_RATE) { + /* E-Mu USB 0404/0202/TrackerPre + * samplerate control quirk + */ + cval->min = 0; + cval->max = 5; + cval->res = 1; + cval->initialized = 1; + } else + get_min_max(cval, valinfo->min_value); + } + + kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + kfree(cval); + return -ENOMEM; + } + kctl->private_free = usb_mixer_elem_free; + + if (check_mapped_name(map, kctl->id.name, + sizeof(kctl->id.name))) + /* nothing */ ; + else if (info->name) + strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); + else { + nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; + len = 0; + if (nameid) + len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + if (! len) + strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); + } + append_ctl_name(kctl, " "); + append_ctl_name(kctl, valinfo->suffix); + + snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max); + if ((err = add_control_to_empty(state, kctl)) < 0) + return err; + } + return 0; +} + + +static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc) +{ + return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit"); +} + +static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc) +{ + return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit"); +} + + +/* + * Selector Unit + */ + +/* info callback for selector unit + * use an enumerator type for routing + */ +static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + char **itemlist = (char **)kcontrol->private_value; + + if (snd_BUG_ON(!itemlist)) + return -EINVAL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = cval->max; + if ((int)uinfo->value.enumerated.item >= cval->max) + uinfo->value.enumerated.item = cval->max - 1; + strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]); + return 0; +} + +/* get callback for selector unit */ +static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int val, err; + + err = get_cur_ctl_value(cval, 0, &val); + if (err < 0) { + if (cval->mixer->ignore_ctl_error) { + ucontrol->value.enumerated.item[0] = 0; + return 0; + } + return err; + } + val = get_relative_value(cval, val); + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +/* put callback for selector unit */ +static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int val, oval, err; + + err = get_cur_ctl_value(cval, 0, &oval); + if (err < 0) { + if (cval->mixer->ignore_ctl_error) + return 0; + return err; + } + val = ucontrol->value.enumerated.item[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_ctl_value(cval, 0, val); + return 1; + } + return 0; +} + +/* alsa control interface for selector unit */ +static struct snd_kcontrol_new mixer_selectunit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = mixer_ctl_selector_info, + .get = mixer_ctl_selector_get, + .put = mixer_ctl_selector_put, +}; + + +/* private free callback. + * free both private_data and private_value + */ +static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl) +{ + int i, num_ins = 0; + + if (kctl->private_data) { + struct usb_mixer_elem_info *cval = kctl->private_data; + num_ins = cval->max; + kfree(cval); + kctl->private_data = NULL; + } + if (kctl->private_value) { + char **itemlist = (char **)kctl->private_value; + for (i = 0; i < num_ins; i++) + kfree(itemlist[i]); + kfree(itemlist); + kctl->private_value = 0; + } +} + +/* + * parse a selector unit + */ +static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc) +{ + unsigned int num_ins = desc[4]; + unsigned int i, nameid, len; + int err; + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + const struct usbmix_name_map *map; + char **namelist; + + if (! num_ins || desc[0] < 5 + num_ins) { + snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid); + return -EINVAL; + } + + for (i = 0; i < num_ins; i++) { + if ((err = parse_audio_unit(state, desc[5 + i])) < 0) + return err; + } + + if (num_ins == 1) /* only one ? nonsense! */ + return 0; + + map = find_map(state, unitid, 0); + if (check_ignored_ctl(map)) + return 0; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return -ENOMEM; + } + cval->mixer = state->mixer; + cval->id = unitid; + cval->val_type = USB_MIXER_U8; + cval->channels = 1; + cval->min = 1; + cval->max = num_ins; + cval->res = 1; + cval->initialized = 1; + + namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); + if (! namelist) { + snd_printk(KERN_ERR "cannot malloc\n"); + kfree(cval); + return -ENOMEM; + } +#define MAX_ITEM_NAME_LEN 64 + for (i = 0; i < num_ins; i++) { + struct usb_audio_term iterm; + len = 0; + namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); + if (! namelist[i]) { + snd_printk(KERN_ERR "cannot malloc\n"); + while (i--) + kfree(namelist[i]); + kfree(namelist); + kfree(cval); + return -ENOMEM; + } + len = check_mapped_selector_name(state, unitid, i, namelist[i], + MAX_ITEM_NAME_LEN); + if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0) + len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); + if (! len) + sprintf(namelist[i], "Input %d", i); + } + + kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + kfree(namelist); + kfree(cval); + return -ENOMEM; + } + kctl->private_value = (unsigned long)namelist; + kctl->private_free = usb_mixer_selector_elem_free; + + nameid = desc[desc[0] - 1]; + len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); + if (len) + ; + else if (nameid) + snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + else { + len = get_term_name(state, &state->oterm, + kctl->id.name, sizeof(kctl->id.name), 0); + if (! len) + strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); + + if ((state->oterm.type & 0xff00) == 0x0100) + append_ctl_name(kctl, " Capture Source"); + else + append_ctl_name(kctl, " Playback Source"); + } + + snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", + cval->id, kctl->id.name, num_ins); + if ((err = add_control_to_empty(state, kctl)) < 0) + return err; + + return 0; +} + + +/* + * parse an audio unit recursively + */ + +static int parse_audio_unit(struct mixer_build *state, int unitid) +{ + unsigned char *p1; + + if (test_and_set_bit(unitid, state->unitbitmap)) + return 0; /* the unit already visited */ + + p1 = find_audio_control_unit(state, unitid); + if (!p1) { + snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); + return -EINVAL; + } + + switch (p1[2]) { + case UAC_INPUT_TERMINAL: + return 0; /* NOP */ + case UAC_MIXER_UNIT: + return parse_audio_mixer_unit(state, unitid, p1); + case UAC_SELECTOR_UNIT: + return parse_audio_selector_unit(state, unitid, p1); + case UAC_FEATURE_UNIT: + return parse_audio_feature_unit(state, unitid, p1); + case UAC_PROCESSING_UNIT_V1: + return parse_audio_processing_unit(state, unitid, p1); + case UAC_EXTENSION_UNIT_V1: + return parse_audio_extension_unit(state, unitid, p1); + default: + snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return -EINVAL; + } +} + +static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) +{ + kfree(mixer->id_elems); + if (mixer->urb) { + kfree(mixer->urb->transfer_buffer); + usb_free_urb(mixer->urb); + } + usb_free_urb(mixer->rc_urb); + kfree(mixer->rc_setup_packet); + kfree(mixer); +} + +static int snd_usb_mixer_dev_free(struct snd_device *device) +{ + struct usb_mixer_interface *mixer = device->device_data; + snd_usb_mixer_free(mixer); + return 0; +} + +/* + * create mixer controls + * + * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers + */ +static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) +{ + struct uac_output_terminal_descriptor_v1 *desc; + struct mixer_build state; + int err; + const struct usbmix_ctl_map *map; + struct usb_host_interface *hostif; + + hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; + memset(&state, 0, sizeof(state)); + state.chip = mixer->chip; + state.mixer = mixer; + state.buffer = hostif->extra; + state.buflen = hostif->extralen; + + /* check the mapping table */ + for (map = usbmix_ctl_maps; map->id; map++) { + if (map->id == state.chip->usb_id) { + state.map = map->map; + state.selector_map = map->selector_map; + mixer->ignore_ctl_error = map->ignore_ctl_error; + break; + } + } + + desc = NULL; + while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) { + if (desc->bLength < 9) + continue; /* invalid descriptor? */ + set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ + state.oterm.id = desc->bTerminalID; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0) + return err; + } + return 0; +} + +void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) +{ + struct usb_mixer_elem_info *info; + + for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + info->elem_id); +} + +static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, + int unitid, + struct usb_mixer_elem_info *cval) +{ + static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", + "S8", "U8", "S16", "U16"}; + snd_iprintf(buffer, " Unit: %i\n", unitid); + if (cval->elem_id) + snd_iprintf(buffer, " Control: name=\"%s\", index=%i\n", + cval->elem_id->name, cval->elem_id->index); + snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " + "channels=%i, type=\"%s\"\n", cval->id, + cval->control, cval->cmask, cval->channels, + val_types[cval->val_type]); + snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", + cval->min, cval->max, cval->dBmin, cval->dBmax); +} + +static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_usb_audio *chip = entry->private_data; + struct usb_mixer_interface *mixer; + struct usb_mixer_elem_info *cval; + int unitid; + + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_iprintf(buffer, + "USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n", + chip->usb_id, mixer->ctrlif, + mixer->ignore_ctl_error); + snd_iprintf(buffer, "Card: %s\n", chip->card->longname); + for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { + for (cval = mixer->id_elems[unitid]; cval; + cval = cval->next_id_elem) + snd_usb_mixer_dump_cval(buffer, unitid, cval); + } + } +} + +static void snd_usb_mixer_status_complete(struct urb *urb) +{ + struct usb_mixer_interface *mixer = urb->context; + + if (urb->status == 0) { + u8 *buf = urb->transfer_buffer; + int i; + + for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) { + snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n", + buf[0], buf[1]); + /* ignore any notifications not from the control interface */ + if ((buf[0] & 0x0f) != 0) + continue; + if (!(buf[0] & 0x40)) + snd_usb_mixer_notify_id(mixer, buf[1]); + else + snd_usb_mixer_rc_memory_change(mixer, buf[1]); + } + } + if (urb->status != -ENOENT && urb->status != -ECONNRESET) { + urb->dev = mixer->chip->dev; + usb_submit_urb(urb, GFP_ATOMIC); + } +} + +/* create the handler for the optional status interrupt endpoint */ +static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) +{ + struct usb_host_interface *hostif; + struct usb_endpoint_descriptor *ep; + void *transfer_buffer; + int buffer_length; + unsigned int epnum; + + hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; + /* we need one interrupt input endpoint */ + if (get_iface_desc(hostif)->bNumEndpoints < 1) + return 0; + ep = get_endpoint(hostif, 0); + if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep)) + return 0; + + epnum = usb_endpoint_num(ep); + buffer_length = le16_to_cpu(ep->wMaxPacketSize); + transfer_buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!transfer_buffer) + return -ENOMEM; + mixer->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mixer->urb) { + kfree(transfer_buffer); + return -ENOMEM; + } + usb_fill_int_urb(mixer->urb, mixer->chip->dev, + usb_rcvintpipe(mixer->chip->dev, epnum), + transfer_buffer, buffer_length, + snd_usb_mixer_status_complete, mixer, ep->bInterval); + usb_submit_urb(mixer->urb, GFP_KERNEL); + return 0; +} + +int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, + int ignore_error) +{ + static struct snd_device_ops dev_ops = { + .dev_free = snd_usb_mixer_dev_free + }; + struct usb_mixer_interface *mixer; + struct snd_info_entry *entry; + struct usb_host_interface *host_iface; + int err, protocol; + + strcpy(chip->card->mixername, "USB Mixer"); + + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + mixer->chip = chip; + mixer->ctrlif = ctrlif; + mixer->ignore_ctl_error = ignore_error; + mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), + GFP_KERNEL); + if (!mixer->id_elems) { + kfree(mixer); + return -ENOMEM; + } + + host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; + protocol = host_iface->desc.bInterfaceProtocol; + + /* FIXME! */ + if (protocol != UAC_VERSION_1) { + snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n", + protocol); + return 0; + } + + if ((err = snd_usb_mixer_controls(mixer)) < 0 || + (err = snd_usb_mixer_status_create(mixer)) < 0) + goto _error; + + snd_usb_mixer_apply_create_quirk(mixer); + + err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); + if (err < 0) + goto _error; + + if (list_empty(&chip->mixer_list) && + !snd_card_proc_new(chip->card, "usbmixer", &entry)) + snd_info_set_text_ops(entry, chip, snd_usb_mixer_proc_read); + + list_add(&mixer->list, &chip->mixer_list); + return 0; + +_error: + snd_usb_mixer_free(mixer); + return err; +} + +void snd_usb_mixer_disconnect(struct list_head *p) +{ + struct usb_mixer_interface *mixer; + + mixer = list_entry(p, struct usb_mixer_interface, list); + usb_kill_urb(mixer->urb); + usb_kill_urb(mixer->rc_urb); +} diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h new file mode 100644 index 0000000..63101ae --- /dev/null +++ b/sound/usb/mixer.h @@ -0,0 +1,52 @@ +#ifndef __USBMIXER_H +#define __USBMIXER_H + +struct usb_mixer_interface { + struct snd_usb_audio *chip; + unsigned int ctrlif; + struct list_head list; + unsigned int ignore_ctl_error; + struct urb *urb; + /* array[MAX_ID_ELEMS], indexed by unit id */ + struct usb_mixer_elem_info **id_elems; + + /* Sound Blaster remote control stuff */ + const struct rc_config *rc_cfg; + u32 rc_code; + wait_queue_head_t rc_waitq; + struct urb *rc_urb; + struct usb_ctrlrequest *rc_setup_packet; + u8 rc_buffer[6]; + + u8 audigy2nx_leds[3]; + u8 xonar_u1_status; +}; + +#define MAX_CHANNELS 10 /* max logical channels */ + +struct usb_mixer_elem_info { + struct usb_mixer_interface *mixer; + struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ + struct snd_ctl_elem_id *elem_id; + unsigned int id; + unsigned int control; /* CS or ICN (high byte) */ + unsigned int cmask; /* channel mask bitmap: 0 = master */ + int channels; + int val_type; + int min, max, res; + int dBmin, dBmax; + int cached; + int cache_val[MAX_CHANNELS]; + u8 initialized; +}; + +int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, + int ignore_error); +void snd_usb_mixer_disconnect(struct list_head *p); + +void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); + +int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, + int request, int validx, int value_set); + +#endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c new file mode 100644 index 0000000..d93fc89 --- /dev/null +++ b/sound/usb/mixer_maps.c @@ -0,0 +1,376 @@ +/* + * Additional mixer mapping + * + * Copyright (c) 2002 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +struct usbmix_dB_map { + u32 min; + u32 max; +}; + +struct usbmix_name_map { + int id; + const char *name; + int control; + struct usbmix_dB_map *dB; +}; + +struct usbmix_selector_map { + int id; + int count; + const char **names; +}; + +struct usbmix_ctl_map { + u32 id; + const struct usbmix_name_map *map; + const struct usbmix_selector_map *selector_map; + int ignore_ctl_error; +}; + +/* + * USB control mappers for SB Exitigy + */ + +/* + * Topology of SB Extigy (see on the wide screen :) + +USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PU[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24] + ^ | | | | +USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PU[15]->+ | | | +->FU[25] > Dig_OUT[26] + ^ ^ | | | | +Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19] + | | | | +Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20] + | | | +Mic-IN[9] --+->FU[10]----------------------------+ | + || | + || +----------------------------------------------------+ + VV V + ++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13] +*/ + +static struct usbmix_name_map extigy_map[] = { + /* 1: IT pcm */ + { 2, "PCM Playback" }, /* FU */ + /* 3: IT pcm */ + /* 4: IT digital in */ + { 5, NULL }, /* DISABLED: this seems to be bogus on some firmware */ + { 6, "Digital In" }, /* FU */ + /* 7: IT line */ + { 8, "Line Playback" }, /* FU */ + /* 9: IT mic */ + { 10, "Mic Playback" }, /* FU */ + { 11, "Capture Source" }, /* SU */ + { 12, "Capture" }, /* FU */ + /* 13: OT pcm capture */ + /* 14: MU (w/o controls) */ + /* 15: PU (3D enh) */ + /* 16: MU (w/o controls) */ + { 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */ + { 17, "Channel Routing", 2 }, /* PU: mode select */ + { 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */ + { 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */ + { 18, "Master Playback" }, /* FU; others */ + /* 19: OT speaker */ + /* 20: OT headphone */ + { 21, NULL }, /* DISABLED: EU (for what?) */ + { 22, "Digital Out Playback" }, /* FU */ + { 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */ + /* 24: OT digital out */ + { 25, "IEC958 Optical Playback" }, /* FU */ + { 26, "IEC958 Optical Playback" }, /* OT */ + { 27, NULL }, /* DISABLED: EU (for what?) */ + /* 28: FU speaker (mute) */ + { 29, NULL }, /* Digital Input Playback Source? */ + { 0 } /* terminator */ +}; + +/* Sound Blaster MP3+ controls mapping + * The default mixer channels have totally misleading names, + * e.g. no Master and fake PCM volume + * Pavel Mihaylov + */ +static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */ +static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */ + +static struct usbmix_name_map mp3plus_map[] = { + /* 1: IT pcm */ + /* 2: IT mic */ + /* 3: IT line */ + /* 4: IT digital in */ + /* 5: OT digital out */ + /* 6: OT speaker */ + /* 7: OT pcm capture */ + { 8, "Capture Source" }, /* FU, default PCM Capture Source */ + /* (Mic, Input 1 = Line input, Input 2 = Optical input) */ + { 9, "Master Playback" }, /* FU, default Speaker 1 */ + /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */ + { 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 }, + /* FU, Mic Capture */ + { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */ + { 11, "Line Capture", .dB = &mp3plus_dB_2 }, + /* FU, default PCM Capture */ + { 12, "Digital In Playback" }, /* FU, default PCM 1 */ + { 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 }, + /* FU, default Mic Playback */ + { 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */ + /* 15: MU */ + { 0 } /* terminator */ +}; + +/* Topology of SB Audigy 2 NX + + +----------------------------->EU[27]--+ + | v + | +----------------------------------->SU[29]---->FU[22]-->Dig_OUT[24] + | | ^ +USB_IN[1]-+------------+ +->EU[17]->+->FU[11]-+ + | v | v | +Dig_IN[4]---+->FU[6]-->MU[16]->FU[18]-+->EU[21]->SU[31]----->FU[30]->Hph_OUT[20] + | ^ | | +Lin_IN[7]-+--->FU[8]---+ +->EU[23]->FU[28]------------->Spk_OUT[19] + | | v + +--->FU[12]------------------------------------->SU[14]--->USB_OUT[15] + | ^ + +->FU[13]--------------------------------------+ +*/ +static struct usbmix_name_map audigy2nx_map[] = { + /* 1: IT pcm playback */ + /* 4: IT digital in */ + { 6, "Digital In Playback" }, /* FU */ + /* 7: IT line in */ + { 8, "Line Playback" }, /* FU */ + { 11, "What-U-Hear Capture" }, /* FU */ + { 12, "Line Capture" }, /* FU */ + { 13, "Digital In Capture" }, /* FU */ + { 14, "Capture Source" }, /* SU */ + /* 15: OT pcm capture */ + /* 16: MU w/o controls */ + { 17, NULL }, /* DISABLED: EU (for what?) */ + { 18, "Master Playback" }, /* FU */ + /* 19: OT speaker */ + /* 20: OT headphone */ + { 21, NULL }, /* DISABLED: EU (for what?) */ + { 22, "Digital Out Playback" }, /* FU */ + { 23, NULL }, /* DISABLED: EU (for what?) */ + /* 24: OT digital out */ + { 27, NULL }, /* DISABLED: EU (for what?) */ + { 28, "Speaker Playback" }, /* FU */ + { 29, "Digital Out Source" }, /* SU */ + { 30, "Headphone Playback" }, /* FU */ + { 31, "Headphone Source" }, /* SU */ + { 0 } /* terminator */ +}; + +static struct usbmix_selector_map audigy2nx_selectors[] = { + { + .id = 14, /* Capture Source */ + .count = 3, + .names = (const char*[]) {"Line", "Digital In", "What-U-Hear"} + }, + { + .id = 29, /* Digital Out Source */ + .count = 3, + .names = (const char*[]) {"Front", "PCM", "Digital In"} + }, + { + .id = 31, /* Headphone Source */ + .count = 2, + .names = (const char*[]) {"Front", "Side"} + }, + { 0 } /* terminator */ +}; + +/* Creative SoundBlaster Live! 24-bit External */ +static struct usbmix_name_map live24ext_map[] = { + /* 2: PCM Playback Volume */ + { 5, "Mic Capture" }, /* FU, default PCM Capture Volume */ + { 0 } /* terminator */ +}; + +/* LineX FM Transmitter entry - needed to bypass controls bug */ +static struct usbmix_name_map linex_map[] = { + /* 1: IT pcm */ + /* 2: OT Speaker */ + { 3, "Master" }, /* FU: master volume - left / right / mute */ + { 0 } /* terminator */ +}; + +static struct usbmix_name_map maya44_map[] = { + /* 1: IT line */ + { 2, "Line Playback" }, /* FU */ + /* 3: IT line */ + { 4, "Line Playback" }, /* FU */ + /* 5: IT pcm playback */ + /* 6: MU */ + { 7, "Master Playback" }, /* FU */ + /* 8: OT speaker */ + /* 9: IT line */ + { 10, "Line Capture" }, /* FU */ + /* 11: MU */ + /* 12: OT pcm capture */ + { } +}; + +/* Section "justlink_map" below added by James Courtier-Dutton + * sourced from Maplin Electronics (http://www.maplin.co.uk), part number A56AK + * Part has 2 connectors that act as a single output. (TOSLINK Optical for digital out, and 3.5mm Jack for Analogue out.) + * The USB Mixer publishes a Microphone and extra Volume controls for it, but none exist on the device, + * so this map removes all unwanted sliders from alsamixer + */ + +static struct usbmix_name_map justlink_map[] = { + /* 1: IT pcm playback */ + /* 2: Not present */ + { 3, NULL}, /* IT mic (No mic input on device) */ + /* 4: Not present */ + /* 5: OT speacker */ + /* 6: OT pcm capture */ + { 7, "Master Playback" }, /* Mute/volume for speaker */ + { 8, NULL }, /* Capture Switch (No capture inputs on device) */ + { 9, NULL }, /* Capture Mute/volume (No capture inputs on device */ + /* 0xa: Not present */ + /* 0xb: MU (w/o controls) */ + { 0xc, NULL }, /* Mic feedback Mute/volume (No capture inputs on device) */ + { 0 } /* terminator */ +}; + +/* TerraTec Aureon 5.1 MkII USB */ +static struct usbmix_name_map aureon_51_2_map[] = { + /* 1: IT USB */ + /* 2: IT Mic */ + /* 3: IT Line */ + /* 4: IT SPDIF */ + /* 5: OT SPDIF */ + /* 6: OT Speaker */ + /* 7: OT USB */ + { 8, "Capture Source" }, /* SU */ + { 9, "Master Playback" }, /* FU */ + { 10, "Mic Capture" }, /* FU */ + { 11, "Line Capture" }, /* FU */ + { 12, "IEC958 In Capture" }, /* FU */ + { 13, "Mic Playback" }, /* FU */ + { 14, "Line Playback" }, /* FU */ + /* 15: MU */ + {} /* terminator */ +}; + +static struct usbmix_name_map scratch_live_map[] = { + /* 1: IT Line 1 (USB streaming) */ + /* 2: OT Line 1 (Speaker) */ + /* 3: IT Line 1 (Line connector) */ + { 4, "Line 1 In" }, /* FU */ + /* 5: OT Line 1 (USB streaming) */ + /* 6: IT Line 2 (USB streaming) */ + /* 7: OT Line 2 (Speaker) */ + /* 8: IT Line 2 (Line connector) */ + { 9, "Line 2 In" }, /* FU */ + /* 10: OT Line 2 (USB streaming) */ + /* 11: IT Mic (Line connector) */ + /* 12: OT Mic (USB streaming) */ + { 0 } /* terminator */ +}; + +/* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+" + * most importand difference is SU[8], it should be set to "Capture Source" + * to make alsamixer and PA working properly. + * FIXME: or mp3plus_map should use "Capture Source" too, + * so this maps can be merget + */ +static struct usbmix_name_map hercules_usb51_map[] = { + { 8, "Capture Source" }, /* SU, default "PCM Capture Source" */ + { 9, "Master Playback" }, /* FU, default "Speaker Playback" */ + { 10, "Mic Boost", 7 }, /* FU, default "Auto Gain Input" */ + { 11, "Line Capture" }, /* FU, default "PCM Capture" */ + { 13, "Mic Bypass Playback" }, /* FU, default "Mic Playback" */ + { 14, "Line Bypass Playback" }, /* FU, default "Line Playback" */ + { 0 } /* terminator */ +}; + +/* + * Control map entries + */ + +static struct usbmix_ctl_map usbmix_ctl_maps[] = { + { + .id = USB_ID(0x041e, 0x3000), + .map = extigy_map, + .ignore_ctl_error = 1, + }, + { + .id = USB_ID(0x041e, 0x3010), + .map = mp3plus_map, + }, + { + .id = USB_ID(0x041e, 0x3020), + .map = audigy2nx_map, + .selector_map = audigy2nx_selectors, + }, + { + .id = USB_ID(0x041e, 0x3040), + .map = live24ext_map, + }, + { + .id = USB_ID(0x041e, 0x3048), + .map = audigy2nx_map, + .selector_map = audigy2nx_selectors, + }, + { + /* Hercules DJ Console (Windows Edition) */ + .id = USB_ID(0x06f8, 0xb000), + .ignore_ctl_error = 1, + }, + { + /* Hercules DJ Console (Macintosh Edition) */ + .id = USB_ID(0x06f8, 0xd002), + .ignore_ctl_error = 1, + }, + { + /* Hercules Gamesurround Muse Pocket LT + * (USB 5.1 Channel Audio Adapter) + */ + .id = USB_ID(0x06f8, 0xc000), + .map = hercules_usb51_map, + }, + { + .id = USB_ID(0x08bb, 0x2702), + .map = linex_map, + .ignore_ctl_error = 1, + }, + { + .id = USB_ID(0x0a92, 0x0091), + .map = maya44_map, + }, + { + .id = USB_ID(0x0c45, 0x1158), + .map = justlink_map, + }, + { + .id = USB_ID(0x0ccd, 0x0028), + .map = aureon_51_2_map, + }, + { + .id = USB_ID(0x13e5, 0x0001), + .map = scratch_live_map, + .ignore_ctl_error = 1, + }, + { 0 } /* terminator */ +}; + diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index d2f4dcd..56b6659 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -35,7 +35,7 @@ #include #include "usbaudio.h" -#include "usbmixer.h" +#include "mixer.h" #include "mixer_quirks.h" #include "helper.h" diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a82cfed..d4ced64 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -24,7 +24,7 @@ #include "usbaudio.h" #include "card.h" -#include "usbmixer.h" +#include "mixer.h" #include "mixer_quirks.h" #include "midi.h" #include "quirks.h" diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c deleted file mode 100644 index ec2436e..0000000 --- a/sound/usb/usbmixer.c +++ /dev/null @@ -1,1920 +0,0 @@ -/* - * (Tentative) USB Audio Driver for ALSA - * - * Mixer control part - * - * Copyright (c) 2002 by Takashi Iwai - * - * Many codes borrowed from audio.c by - * Alan Cox (alan@lxorguk.ukuu.org.uk) - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "usbaudio.h" -#include "usbmixer.h" -#include "helper.h" -#include "mixer_quirks.h" - -#define MAX_ID_ELEMS 256 - -struct usb_audio_term { - int id; - int type; - int channels; - unsigned int chconfig; - int name; -}; - -struct usbmix_name_map; - -struct mixer_build { - struct snd_usb_audio *chip; - struct usb_mixer_interface *mixer; - unsigned char *buffer; - unsigned int buflen; - DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); - struct usb_audio_term oterm; - const struct usbmix_name_map *map; - const struct usbmix_selector_map *selector_map; -}; - -enum { - USB_MIXER_BOOLEAN, - USB_MIXER_INV_BOOLEAN, - USB_MIXER_S8, - USB_MIXER_U8, - USB_MIXER_S16, - USB_MIXER_U16, -}; - -enum { - USB_PROC_UPDOWN = 1, - USB_PROC_UPDOWN_SWITCH = 1, - USB_PROC_UPDOWN_MODE_SEL = 2, - - USB_PROC_PROLOGIC = 2, - USB_PROC_PROLOGIC_SWITCH = 1, - USB_PROC_PROLOGIC_MODE_SEL = 2, - - USB_PROC_3DENH = 3, - USB_PROC_3DENH_SWITCH = 1, - USB_PROC_3DENH_SPACE = 2, - - USB_PROC_REVERB = 4, - USB_PROC_REVERB_SWITCH = 1, - USB_PROC_REVERB_LEVEL = 2, - USB_PROC_REVERB_TIME = 3, - USB_PROC_REVERB_DELAY = 4, - - USB_PROC_CHORUS = 5, - USB_PROC_CHORUS_SWITCH = 1, - USB_PROC_CHORUS_LEVEL = 2, - USB_PROC_CHORUS_RATE = 3, - USB_PROC_CHORUS_DEPTH = 4, - - USB_PROC_DCR = 6, - USB_PROC_DCR_SWITCH = 1, - USB_PROC_DCR_RATIO = 2, - USB_PROC_DCR_MAX_AMP = 3, - USB_PROC_DCR_THRESHOLD = 4, - USB_PROC_DCR_ATTACK = 5, - USB_PROC_DCR_RELEASE = 6, -}; - -/*E-mu 0202(0404) eXtension Unit(XU) control*/ -enum { - USB_XU_CLOCK_RATE = 0xe301, - USB_XU_CLOCK_SOURCE = 0xe302, - USB_XU_DIGITAL_IO_STATUS = 0xe303, - USB_XU_DEVICE_OPTIONS = 0xe304, - USB_XU_DIRECT_MONITORING = 0xe305, - USB_XU_METERING = 0xe306 -}; -enum { - USB_XU_CLOCK_SOURCE_SELECTOR = 0x02, /* clock source*/ - USB_XU_CLOCK_RATE_SELECTOR = 0x03, /* clock rate */ - USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01, /* the spdif format */ - USB_XU_SOFT_LIMIT_SELECTOR = 0x03 /* soft limiter */ -}; - -/* - * manual mapping of mixer names - * if the mixer topology is too complicated and the parsed names are - * ambiguous, add the entries in usbmixer_maps.c. - */ -#include "usbmixer_maps.c" - -static const struct usbmix_name_map * -find_map(struct mixer_build *state, int unitid, int control) -{ - const struct usbmix_name_map *p = state->map; - - if (!p) - return NULL; - - for (p = state->map; p->id; p++) { - if (p->id == unitid && - (!control || !p->control || control == p->control)) - return p; - } - return NULL; -} - -/* get the mapped name if the unit matches */ -static int -check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) -{ - if (!p || !p->name) - return 0; - - buflen--; - return strlcpy(buf, p->name, buflen); -} - -/* check whether the control should be ignored */ -static inline int -check_ignored_ctl(const struct usbmix_name_map *p) -{ - if (!p || p->name || p->dB) - return 0; - return 1; -} - -/* dB mapping */ -static inline void check_mapped_dB(const struct usbmix_name_map *p, - struct usb_mixer_elem_info *cval) -{ - if (p && p->dB) { - cval->dBmin = p->dB->min; - cval->dBmax = p->dB->max; - } -} - -/* get the mapped selector source name */ -static int check_mapped_selector_name(struct mixer_build *state, int unitid, - int index, char *buf, int buflen) -{ - const struct usbmix_selector_map *p; - - if (! state->selector_map) - return 0; - for (p = state->selector_map; p->id; p++) { - if (p->id == unitid && index < p->count) - return strlcpy(buf, p->names[index], buflen); - } - return 0; -} - -/* - * find an audio control unit with the given unit id - */ -static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit) -{ - unsigned char *p; - - p = NULL; - while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, - USB_DT_CS_INTERFACE)) != NULL) { - if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit) - return p; - } - return NULL; -} - - -/* - * copy a string with the given id - */ -static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen) -{ - int len = usb_string(state->chip->dev, index, buf, maxlen - 1); - buf[len] = 0; - return len; -} - -/* - * convert from the byte/word on usb descriptor to the zero-based integer - */ -static int convert_signed_value(struct usb_mixer_elem_info *cval, int val) -{ - switch (cval->val_type) { - case USB_MIXER_BOOLEAN: - return !!val; - case USB_MIXER_INV_BOOLEAN: - return !val; - case USB_MIXER_U8: - val &= 0xff; - break; - case USB_MIXER_S8: - val &= 0xff; - if (val >= 0x80) - val -= 0x100; - break; - case USB_MIXER_U16: - val &= 0xffff; - break; - case USB_MIXER_S16: - val &= 0xffff; - if (val >= 0x8000) - val -= 0x10000; - break; - } - return val; -} - -/* - * convert from the zero-based int to the byte/word for usb descriptor - */ -static int convert_bytes_value(struct usb_mixer_elem_info *cval, int val) -{ - switch (cval->val_type) { - case USB_MIXER_BOOLEAN: - return !!val; - case USB_MIXER_INV_BOOLEAN: - return !val; - case USB_MIXER_S8: - case USB_MIXER_U8: - return val & 0xff; - case USB_MIXER_S16: - case USB_MIXER_U16: - return val & 0xffff; - } - return 0; /* not reached */ -} - -static int get_relative_value(struct usb_mixer_elem_info *cval, int val) -{ - if (! cval->res) - cval->res = 1; - if (val < cval->min) - return 0; - else if (val >= cval->max) - return (cval->max - cval->min + cval->res - 1) / cval->res; - else - return (val - cval->min) / cval->res; -} - -static int get_abs_value(struct usb_mixer_elem_info *cval, int val) -{ - if (val < 0) - return cval->min; - if (! cval->res) - cval->res = 1; - val *= cval->res; - val += cval->min; - if (val > cval->max) - return cval->max; - return val; -} - - -/* - * retrieve a mixer value - */ - -static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) -{ - unsigned char buf[2]; - int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; - int timeout = 10; - - while (timeout-- > 0) { - if (snd_usb_ctl_msg(cval->mixer->chip->dev, - usb_rcvctrlpipe(cval->mixer->chip->dev, 0), - request, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - validx, cval->mixer->ctrlif | (cval->id << 8), - buf, val_len, 100) >= val_len) { - *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); - return 0; - } - } - snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", - request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type); - return -EINVAL; -} - -static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value) -{ - return get_ctl_value(cval, UAC_GET_CUR, validx, value); -} - -/* channel = 0: master, 1 = first channel */ -static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, - int channel, int *value) -{ - return get_ctl_value(cval, UAC_GET_CUR, (cval->control << 8) | channel, value); -} - -static int get_cur_mix_value(struct usb_mixer_elem_info *cval, - int channel, int index, int *value) -{ - int err; - - if (cval->cached & (1 << channel)) { - *value = cval->cache_val[index]; - return 0; - } - err = get_cur_mix_raw(cval, channel, value); - if (err < 0) { - if (!cval->mixer->ignore_ctl_error) - snd_printd(KERN_ERR "cannot get current value for " - "control %d ch %d: err = %d\n", - cval->control, channel, err); - return err; - } - cval->cached |= 1 << channel; - cval->cache_val[index] = *value; - return 0; -} - - -/* - * set a mixer value - */ - -int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, - int request, int validx, int value_set) -{ - unsigned char buf[2]; - int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; - int timeout = 10; - - value_set = convert_bytes_value(cval, value_set); - buf[0] = value_set & 0xff; - buf[1] = (value_set >> 8) & 0xff; - while (timeout-- > 0) - if (snd_usb_ctl_msg(cval->mixer->chip->dev, - usb_sndctrlpipe(cval->mixer->chip->dev, 0), - request, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - validx, cval->mixer->ctrlif | (cval->id << 8), - buf, val_len, 100) >= 0) - return 0; - snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", - request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]); - return -EINVAL; -} - -static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value) -{ - return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); -} - -static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, - int index, int value) -{ - int err; - err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel, - value); - if (err < 0) - return err; - cval->cached |= 1 << channel; - cval->cache_val[index] = value; - return 0; -} - -/* - * TLV callback for mixer volume controls - */ -static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - DECLARE_TLV_DB_MINMAX(scale, 0, 0); - - if (size < sizeof(scale)) - return -ENOMEM; - scale[2] = cval->dBmin; - scale[3] = cval->dBmax; - if (copy_to_user(_tlv, scale, sizeof(scale))) - return -EFAULT; - return 0; -} - -/* - * parser routines begin here... - */ - -static int parse_audio_unit(struct mixer_build *state, int unitid); - - -/* - * check if the input/output channel routing is enabled on the given bitmap. - * used for mixer unit parser - */ -static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs) -{ - int idx = ich * num_outs + och; - return bmap[idx >> 3] & (0x80 >> (idx & 7)); -} - - -/* - * add an alsa control element - * search and increment the index until an empty slot is found. - * - * if failed, give up and free the control instance. - */ - -static int add_control_to_empty(struct mixer_build *state, struct snd_kcontrol *kctl) -{ - struct usb_mixer_elem_info *cval = kctl->private_data; - int err; - - while (snd_ctl_find_id(state->chip->card, &kctl->id)) - kctl->id.index++; - if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { - snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); - return err; - } - cval->elem_id = &kctl->id; - cval->next_id_elem = state->mixer->id_elems[cval->id]; - state->mixer->id_elems[cval->id] = cval; - return 0; -} - - -/* - * get a terminal name string - */ - -static struct iterm_name_combo { - int type; - char *name; -} iterm_names[] = { - { 0x0300, "Output" }, - { 0x0301, "Speaker" }, - { 0x0302, "Headphone" }, - { 0x0303, "HMD Audio" }, - { 0x0304, "Desktop Speaker" }, - { 0x0305, "Room Speaker" }, - { 0x0306, "Com Speaker" }, - { 0x0307, "LFE" }, - { 0x0600, "External In" }, - { 0x0601, "Analog In" }, - { 0x0602, "Digital In" }, - { 0x0603, "Line" }, - { 0x0604, "Legacy In" }, - { 0x0605, "IEC958 In" }, - { 0x0606, "1394 DA Stream" }, - { 0x0607, "1394 DV Stream" }, - { 0x0700, "Embedded" }, - { 0x0701, "Noise Source" }, - { 0x0702, "Equalization Noise" }, - { 0x0703, "CD" }, - { 0x0704, "DAT" }, - { 0x0705, "DCC" }, - { 0x0706, "MiniDisk" }, - { 0x0707, "Analog Tape" }, - { 0x0708, "Phonograph" }, - { 0x0709, "VCR Audio" }, - { 0x070a, "Video Disk Audio" }, - { 0x070b, "DVD Audio" }, - { 0x070c, "TV Tuner Audio" }, - { 0x070d, "Satellite Rec Audio" }, - { 0x070e, "Cable Tuner Audio" }, - { 0x070f, "DSS Audio" }, - { 0x0710, "Radio Receiver" }, - { 0x0711, "Radio Transmitter" }, - { 0x0712, "Multi-Track Recorder" }, - { 0x0713, "Synthesizer" }, - { 0 }, -}; - -static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, - unsigned char *name, int maxlen, int term_only) -{ - struct iterm_name_combo *names; - - if (iterm->name) - return snd_usb_copy_string_desc(state, iterm->name, name, maxlen); - - /* virtual type - not a real terminal */ - if (iterm->type >> 16) { - if (term_only) - return 0; - switch (iterm->type >> 16) { - case UAC_SELECTOR_UNIT: - strcpy(name, "Selector"); return 8; - case UAC_PROCESSING_UNIT_V1: - strcpy(name, "Process Unit"); return 12; - case UAC_EXTENSION_UNIT_V1: - strcpy(name, "Ext Unit"); return 8; - case UAC_MIXER_UNIT: - strcpy(name, "Mixer"); return 5; - default: - return sprintf(name, "Unit %d", iterm->id); - } - } - - switch (iterm->type & 0xff00) { - case 0x0100: - strcpy(name, "PCM"); return 3; - case 0x0200: - strcpy(name, "Mic"); return 3; - case 0x0400: - strcpy(name, "Headset"); return 7; - case 0x0500: - strcpy(name, "Phone"); return 5; - } - - for (names = iterm_names; names->type; names++) - if (names->type == iterm->type) { - strcpy(name, names->name); - return strlen(names->name); - } - return 0; -} - - -/* - * parse the source unit recursively until it reaches to a terminal - * or a branched unit. - */ -static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) -{ - unsigned char *p1; - - memset(term, 0, sizeof(*term)); - while ((p1 = find_audio_control_unit(state, id)) != NULL) { - term->id = id; - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - term->type = combine_word(p1 + 4); - term->channels = p1[7]; - term->chconfig = combine_word(p1 + 8); - term->name = p1[11]; - return 0; - case UAC_FEATURE_UNIT: - id = p1[4]; - break; /* continue to parse */ - case UAC_MIXER_UNIT: - term->type = p1[2] << 16; /* virtual type */ - term->channels = p1[5 + p1[4]]; - term->chconfig = combine_word(p1 + 6 + p1[4]); - term->name = p1[p1[0] - 1]; - return 0; - case UAC_SELECTOR_UNIT: - /* call recursively to retrieve the channel info */ - if (check_input_term(state, p1[5], term) < 0) - return -ENODEV; - term->type = p1[2] << 16; /* virtual type */ - term->id = id; - term->name = p1[9 + p1[0] - 1]; - return 0; - case UAC_PROCESSING_UNIT_V1: - case UAC_EXTENSION_UNIT_V1: - if (p1[6] == 1) { - id = p1[7]; - break; /* continue to parse */ - } - term->type = p1[2] << 16; /* virtual type */ - term->channels = p1[7 + p1[6]]; - term->chconfig = combine_word(p1 + 8 + p1[6]); - term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; - return 0; - default: - return -ENODEV; - } - } - return -ENODEV; -} - - -/* - * Feature Unit - */ - -/* feature unit control information */ -struct usb_feature_control_info { - const char *name; - unsigned int type; /* control type (mute, volume, etc.) */ -}; - -static struct usb_feature_control_info audio_feature_info[] = { - { "Mute", USB_MIXER_INV_BOOLEAN }, - { "Volume", USB_MIXER_S16 }, - { "Tone Control - Bass", USB_MIXER_S8 }, - { "Tone Control - Mid", USB_MIXER_S8 }, - { "Tone Control - Treble", USB_MIXER_S8 }, - { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ - { "Auto Gain Control", USB_MIXER_BOOLEAN }, - { "Delay Control", USB_MIXER_U16 }, - { "Bass Boost", USB_MIXER_BOOLEAN }, - { "Loudness", USB_MIXER_BOOLEAN }, -}; - - -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); - kctl->private_data = NULL; -} - - -/* - * interface to ALSA control for feature/mixer units - */ - -/* - * retrieve the minimum and maximum values for the specified control - */ -static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) -{ - /* for failsafe */ - cval->min = default_min; - cval->max = cval->min + 1; - cval->res = 1; - cval->dBmin = cval->dBmax = 0; - - if (cval->val_type == USB_MIXER_BOOLEAN || - cval->val_type == USB_MIXER_INV_BOOLEAN) { - cval->initialized = 1; - } else { - int minchn = 0; - if (cval->cmask) { - int i; - for (i = 0; i < MAX_CHANNELS; i++) - if (cval->cmask & (1 << i)) { - minchn = i + 1; - break; - } - } - if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { - snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->id, cval->mixer->ctrlif, cval->control, cval->id); - return -EINVAL; - } - if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { - cval->res = 1; - } else { - int last_valid_res = cval->res; - - while (cval->res > 1) { - if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES, - (cval->control << 8) | minchn, cval->res / 2) < 0) - break; - cval->res /= 2; - } - if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) - cval->res = last_valid_res; - } - if (cval->res == 0) - cval->res = 1; - - /* Additional checks for the proper resolution - * - * Some devices report smaller resolutions than actually - * reacting. They don't return errors but simply clip - * to the lower aligned value. - */ - if (cval->min + cval->res < cval->max) { - int last_valid_res = cval->res; - int saved, test, check; - get_cur_mix_raw(cval, minchn, &saved); - for (;;) { - test = saved; - if (test < cval->max) - test += cval->res; - else - test -= cval->res; - if (test < cval->min || test > cval->max || - set_cur_mix_value(cval, minchn, 0, test) || - get_cur_mix_raw(cval, minchn, &check)) { - cval->res = last_valid_res; - break; - } - if (test == check) - break; - cval->res *= 2; - } - set_cur_mix_value(cval, minchn, 0, saved); - } - - cval->initialized = 1; - } - - /* USB descriptions contain the dB scale in 1/256 dB unit - * while ALSA TLV contains in 1/100 dB unit - */ - cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256; - cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256; - if (cval->dBmin > cval->dBmax) { - /* something is wrong; assume it's either from/to 0dB */ - if (cval->dBmin < 0) - cval->dBmax = 0; - else if (cval->dBmin > 0) - cval->dBmin = 0; - if (cval->dBmin > cval->dBmax) { - /* totally crap, return an error */ - return -EINVAL; - } - } - - return 0; -} - - -/* get a feature/mixer unit info */ -static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - - if (cval->val_type == USB_MIXER_BOOLEAN || - cval->val_type == USB_MIXER_INV_BOOLEAN) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = cval->channels; - if (cval->val_type == USB_MIXER_BOOLEAN || - cval->val_type == USB_MIXER_INV_BOOLEAN) { - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - } else { - if (! cval->initialized) - get_min_max(cval, 0); - uinfo->value.integer.min = 0; - uinfo->value.integer.max = - (cval->max - cval->min + cval->res - 1) / cval->res; - } - return 0; -} - -/* get the current value from feature/mixer unit */ -static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int c, cnt, val, err; - - ucontrol->value.integer.value[0] = cval->min; - if (cval->cmask) { - cnt = 0; - for (c = 0; c < MAX_CHANNELS; c++) { - if (!(cval->cmask & (1 << c))) - continue; - err = get_cur_mix_value(cval, c + 1, cnt, &val); - if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; - val = get_relative_value(cval, val); - ucontrol->value.integer.value[cnt] = val; - cnt++; - } - return 0; - } else { - /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &val); - if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; - val = get_relative_value(cval, val); - ucontrol->value.integer.value[0] = val; - } - return 0; -} - -/* put the current value to feature/mixer unit */ -static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int c, cnt, val, oval, err; - int changed = 0; - - if (cval->cmask) { - cnt = 0; - for (c = 0; c < MAX_CHANNELS; c++) { - if (!(cval->cmask & (1 << c))) - continue; - err = get_cur_mix_value(cval, c + 1, cnt, &oval); - if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; - val = ucontrol->value.integer.value[cnt]; - val = get_abs_value(cval, val); - if (oval != val) { - set_cur_mix_value(cval, c + 1, cnt, val); - changed = 1; - } - cnt++; - } - } else { - /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &oval); - if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; - val = ucontrol->value.integer.value[0]; - val = get_abs_value(cval, val); - if (val != oval) { - set_cur_mix_value(cval, 0, 0, val); - changed = 1; - } - } - return changed; -} - -static struct snd_kcontrol_new usb_feature_unit_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "", /* will be filled later manually */ - .info = mixer_ctl_feature_info, - .get = mixer_ctl_feature_get, - .put = mixer_ctl_feature_put, -}; - - -/* - * build a feature control - */ - -static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) -{ - return strlcat(kctl->id.name, str, sizeof(kctl->id.name)); -} - -static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, - unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid) -{ - unsigned int len = 0; - int mapped_name = 0; - int nameid = desc[desc[0] - 1]; - struct snd_kcontrol *kctl; - struct usb_mixer_elem_info *cval; - const struct usbmix_name_map *map; - - control++; /* change from zero-based to 1-based value */ - - if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) { - /* FIXME: not supported yet */ - return; - } - - map = find_map(state, unitid, control); - if (check_ignored_ctl(map)) - return; - - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - return; - } - cval->mixer = state->mixer; - cval->id = unitid; - cval->control = control; - cval->cmask = ctl_mask; - cval->val_type = audio_feature_info[control-1].type; - if (ctl_mask == 0) - cval->channels = 1; /* master channel */ - else { - int i, c = 0; - for (i = 0; i < 16; i++) - if (ctl_mask & (1 << i)) - c++; - cval->channels = c; - } - - /* get min/max values */ - get_min_max(cval, 0); - - kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); - if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - kfree(cval); - return; - } - kctl->private_free = usb_mixer_elem_free; - - len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); - mapped_name = len != 0; - if (! len && nameid) - len = snd_usb_copy_string_desc(state, nameid, - kctl->id.name, sizeof(kctl->id.name)); - - switch (control) { - case UAC_MUTE_CONTROL: - case UAC_VOLUME_CONTROL: - /* determine the control name. the rule is: - * - if a name id is given in descriptor, use it. - * - if the connected input can be determined, then use the name - * of terminal type. - * - if the connected output can be determined, use it. - * - otherwise, anonymous name. - */ - if (! len) { - len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); - if (! len) - len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); - if (! len) - len = snprintf(kctl->id.name, sizeof(kctl->id.name), - "Feature %d", unitid); - } - /* determine the stream direction: - * if the connected output is USB stream, then it's likely a - * capture stream. otherwise it should be playback (hopefully :) - */ - if (! mapped_name && ! (state->oterm.type >> 16)) { - if ((state->oterm.type & 0xff00) == 0x0100) { - len = append_ctl_name(kctl, " Capture"); - } else { - len = append_ctl_name(kctl, " Playback"); - } - } - append_ctl_name(kctl, control == UAC_MUTE_CONTROL ? - " Switch" : " Volume"); - if (control == UAC_VOLUME_CONTROL) { - kctl->tlv.c = mixer_vol_tlv; - kctl->vd[0].access |= - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; - check_mapped_dB(map, cval); - } - break; - - default: - if (! len) - strlcpy(kctl->id.name, audio_feature_info[control-1].name, - sizeof(kctl->id.name)); - break; - } - - /* volume control quirks */ - switch (state->chip->usb_id) { - case USB_ID(0x0471, 0x0101): - case USB_ID(0x0471, 0x0104): - case USB_ID(0x0471, 0x0105): - case USB_ID(0x0672, 0x1041): - /* quirk for UDA1321/N101. - * note that detection between firmware 2.1.1.7 (N101) - * and later 2.1.1.21 is not very clear from datasheets. - * I hope that the min value is -15360 for newer firmware --jk - */ - if (!strcmp(kctl->id.name, "PCM Playback Volume") && - cval->min == -15616) { - snd_printk(KERN_INFO - "set volume quirk for UDA1321/N101 chip\n"); - cval->max = -256; - } - break; - - case USB_ID(0x046d, 0x09a4): - if (!strcmp(kctl->id.name, "Mic Capture Volume")) { - snd_printk(KERN_INFO - "set volume quirk for QuickCam E3500\n"); - cval->min = 6080; - cval->max = 8768; - cval->res = 192; - } - break; - - } - - snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); - add_control_to_empty(state, kctl); -} - - - -/* - * parse a feature unit - * - * most of controlls are defined here. - */ -static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr) -{ - int channels, i, j; - struct usb_audio_term iterm; - unsigned int master_bits, first_ch_bits; - int err, csize; - struct uac_feature_unit_descriptor *ftr = _ftr; - - if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) { - snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); - return -EINVAL; - } - - /* parse the source unit */ - if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0) - return err; - - /* determine the input source type and name */ - if (check_input_term(state, ftr->bSourceID, &iterm) < 0) - return -EINVAL; - - channels = (ftr->bLength - 7) / csize - 1; - - master_bits = snd_usb_combine_bytes(ftr->controls, csize); - /* master configuration quirks */ - switch (state->chip->usb_id) { - case USB_ID(0x08bb, 0x2702): - snd_printk(KERN_INFO - "usbmixer: master volume quirk for PCM2702 chip\n"); - /* disable non-functional volume control */ - master_bits &= ~UAC_FU_VOLUME; - break; - } - if (channels > 0) - first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize); - else - first_ch_bits = 0; - /* check all control types */ - for (i = 0; i < 10; i++) { - unsigned int ch_bits = 0; - for (j = 0; j < channels; j++) { - unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize); - if (mask & (1 << i)) - ch_bits |= (1 << j); - } - if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ - build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid); - if (master_bits & (1 << i)) - build_feature_ctl(state, _ftr, 0, i, &iterm, unitid); - } - - return 0; -} - - -/* - * Mixer Unit - */ - -/* - * build a mixer unit control - * - * the callbacks are identical with feature unit. - * input channel number (zero based) is given in control field instead. - */ - -static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, - int in_pin, int in_ch, int unitid, - struct usb_audio_term *iterm) -{ - struct usb_mixer_elem_info *cval; - unsigned int input_pins = desc[4]; - unsigned int num_outs = desc[5 + input_pins]; - unsigned int i, len; - struct snd_kcontrol *kctl; - const struct usbmix_name_map *map; - - map = find_map(state, unitid, 0); - if (check_ignored_ctl(map)) - return; - - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (! cval) - return; - - cval->mixer = state->mixer; - cval->id = unitid; - cval->control = in_ch + 1; /* based on 1 */ - cval->val_type = USB_MIXER_S16; - for (i = 0; i < num_outs; i++) { - if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) { - cval->cmask |= (1 << i); - cval->channels++; - } - } - - /* get min/max values */ - get_min_max(cval, 0); - - kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); - if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - kfree(cval); - return; - } - kctl->private_free = usb_mixer_elem_free; - - len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); - if (! len) - len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0); - if (! len) - len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); - append_ctl_name(kctl, " Volume"); - - snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - add_control_to_empty(state, kctl); -} - - -/* - * parse a mixer unit - */ -static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc) -{ - struct usb_audio_term iterm; - int input_pins, num_ins, num_outs; - int pin, ich, err; - - if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) { - snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid); - return -EINVAL; - } - /* no bmControls field (e.g. Maya44) -> ignore */ - if (desc[0] <= 10 + input_pins) { - snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid); - return 0; - } - - num_ins = 0; - ich = 0; - for (pin = 0; pin < input_pins; pin++) { - err = parse_audio_unit(state, desc[5 + pin]); - if (err < 0) - return err; - err = check_input_term(state, desc[5 + pin], &iterm); - if (err < 0) - return err; - num_ins += iterm.channels; - for (; ich < num_ins; ++ich) { - int och, ich_has_controls = 0; - - for (och = 0; och < num_outs; ++och) { - if (check_matrix_bitmap(desc + 9 + input_pins, - ich, och, num_outs)) { - ich_has_controls = 1; - break; - } - } - if (ich_has_controls) - build_mixer_unit_ctl(state, desc, pin, ich, - unitid, &iterm); - } - } - return 0; -} - - -/* - * Processing Unit / Extension Unit - */ - -/* get callback for processing/extension unit */ -static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int err, val; - - err = get_cur_ctl_value(cval, cval->control << 8, &val); - if (err < 0 && cval->mixer->ignore_ctl_error) { - ucontrol->value.integer.value[0] = cval->min; - return 0; - } - if (err < 0) - return err; - val = get_relative_value(cval, val); - ucontrol->value.integer.value[0] = val; - return 0; -} - -/* put callback for processing/extension unit */ -static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int val, oval, err; - - err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } - val = ucontrol->value.integer.value[0]; - val = get_abs_value(cval, val); - if (val != oval) { - set_cur_ctl_value(cval, cval->control << 8, val); - return 1; - } - return 0; -} - -/* alsa control interface for processing/extension unit */ -static struct snd_kcontrol_new mixer_procunit_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "", /* will be filled later */ - .info = mixer_ctl_feature_info, - .get = mixer_ctl_procunit_get, - .put = mixer_ctl_procunit_put, -}; - - -/* - * predefined data for processing units - */ -struct procunit_value_info { - int control; - char *suffix; - int val_type; - int min_value; -}; - -struct procunit_info { - int type; - char *name; - struct procunit_value_info *values; -}; - -static struct procunit_value_info updown_proc_info[] = { - { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, - { 0 } -}; -static struct procunit_value_info prologic_proc_info[] = { - { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, - { 0 } -}; -static struct procunit_value_info threed_enh_proc_info[] = { - { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 }, - { 0 } -}; -static struct procunit_value_info reverb_proc_info[] = { - { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, - { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 }, - { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 }, - { 0 } -}; -static struct procunit_value_info chorus_proc_info[] = { - { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, - { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, - { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, - { 0 } -}; -static struct procunit_value_info dcr_proc_info[] = { - { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN }, - { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 }, - { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 }, - { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 }, - { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 }, - { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 }, - { 0 } -}; - -static struct procunit_info procunits[] = { - { USB_PROC_UPDOWN, "Up Down", updown_proc_info }, - { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info }, - { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info }, - { USB_PROC_REVERB, "Reverb", reverb_proc_info }, - { USB_PROC_CHORUS, "Chorus", chorus_proc_info }, - { USB_PROC_DCR, "DCR", dcr_proc_info }, - { 0 }, -}; -/* - * predefined data for extension units - */ -static struct procunit_value_info clock_rate_xu_info[] = { - { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 }, - { 0 } -}; -static struct procunit_value_info clock_source_xu_info[] = { - { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN }, - { 0 } -}; -static struct procunit_value_info spdif_format_xu_info[] = { - { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN }, - { 0 } -}; -static struct procunit_value_info soft_limit_xu_info[] = { - { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN }, - { 0 } -}; -static struct procunit_info extunits[] = { - { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info }, - { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info }, - { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info }, - { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info }, - { 0 } -}; -/* - * build a processing/extension unit - */ -static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name) -{ - int num_ins = dsc[6]; - struct usb_mixer_elem_info *cval; - struct snd_kcontrol *kctl; - int i, err, nameid, type, len; - struct procunit_info *info; - struct procunit_value_info *valinfo; - const struct usbmix_name_map *map; - static struct procunit_value_info default_value_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0 } - }; - static struct procunit_info default_info = { - 0, NULL, default_value_info - }; - - if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { - snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); - return -EINVAL; - } - - for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, dsc[7 + i])) < 0) - return err; - } - - type = combine_word(&dsc[4]); - for (info = list; info && info->type; info++) - if (info->type == type) - break; - if (! info || ! info->type) - info = &default_info; - - for (valinfo = info->values; valinfo->control; valinfo++) { - /* FIXME: bitmap might be longer than 8bit */ - if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) - continue; - map = find_map(state, unitid, valinfo->control); - if (check_ignored_ctl(map)) - continue; - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - return -ENOMEM; - } - cval->mixer = state->mixer; - cval->id = unitid; - cval->control = valinfo->control; - cval->val_type = valinfo->val_type; - cval->channels = 1; - - /* get min/max values */ - if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { - /* FIXME: hard-coded */ - cval->min = 1; - cval->max = dsc[15]; - cval->res = 1; - cval->initialized = 1; - } else { - if (type == USB_XU_CLOCK_RATE) { - /* E-Mu USB 0404/0202/TrackerPre - * samplerate control quirk - */ - cval->min = 0; - cval->max = 5; - cval->res = 1; - cval->initialized = 1; - } else - get_min_max(cval, valinfo->min_value); - } - - kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); - if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - kfree(cval); - return -ENOMEM; - } - kctl->private_free = usb_mixer_elem_free; - - if (check_mapped_name(map, kctl->id.name, - sizeof(kctl->id.name))) - /* nothing */ ; - else if (info->name) - strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); - else { - nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; - len = 0; - if (nameid) - len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); - if (! len) - strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); - } - append_ctl_name(kctl, " "); - append_ctl_name(kctl, valinfo->suffix); - - snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - if ((err = add_control_to_empty(state, kctl)) < 0) - return err; - } - return 0; -} - - -static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc) -{ - return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit"); -} - -static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc) -{ - return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit"); -} - - -/* - * Selector Unit - */ - -/* info callback for selector unit - * use an enumerator type for routing - */ -static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - char **itemlist = (char **)kcontrol->private_value; - - if (snd_BUG_ON(!itemlist)) - return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = cval->max; - if ((int)uinfo->value.enumerated.item >= cval->max) - uinfo->value.enumerated.item = cval->max - 1; - strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]); - return 0; -} - -/* get callback for selector unit */ -static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int val, err; - - err = get_cur_ctl_value(cval, 0, &val); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.enumerated.item[0] = 0; - return 0; - } - return err; - } - val = get_relative_value(cval, val); - ucontrol->value.enumerated.item[0] = val; - return 0; -} - -/* put callback for selector unit */ -static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_elem_info *cval = kcontrol->private_data; - int val, oval, err; - - err = get_cur_ctl_value(cval, 0, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } - val = ucontrol->value.enumerated.item[0]; - val = get_abs_value(cval, val); - if (val != oval) { - set_cur_ctl_value(cval, 0, val); - return 1; - } - return 0; -} - -/* alsa control interface for selector unit */ -static struct snd_kcontrol_new mixer_selectunit_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "", /* will be filled later */ - .info = mixer_ctl_selector_info, - .get = mixer_ctl_selector_get, - .put = mixer_ctl_selector_put, -}; - - -/* private free callback. - * free both private_data and private_value - */ -static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl) -{ - int i, num_ins = 0; - - if (kctl->private_data) { - struct usb_mixer_elem_info *cval = kctl->private_data; - num_ins = cval->max; - kfree(cval); - kctl->private_data = NULL; - } - if (kctl->private_value) { - char **itemlist = (char **)kctl->private_value; - for (i = 0; i < num_ins; i++) - kfree(itemlist[i]); - kfree(itemlist); - kctl->private_value = 0; - } -} - -/* - * parse a selector unit - */ -static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc) -{ - unsigned int num_ins = desc[4]; - unsigned int i, nameid, len; - int err; - struct usb_mixer_elem_info *cval; - struct snd_kcontrol *kctl; - const struct usbmix_name_map *map; - char **namelist; - - if (! num_ins || desc[0] < 5 + num_ins) { - snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid); - return -EINVAL; - } - - for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, desc[5 + i])) < 0) - return err; - } - - if (num_ins == 1) /* only one ? nonsense! */ - return 0; - - map = find_map(state, unitid, 0); - if (check_ignored_ctl(map)) - return 0; - - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - return -ENOMEM; - } - cval->mixer = state->mixer; - cval->id = unitid; - cval->val_type = USB_MIXER_U8; - cval->channels = 1; - cval->min = 1; - cval->max = num_ins; - cval->res = 1; - cval->initialized = 1; - - namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); - if (! namelist) { - snd_printk(KERN_ERR "cannot malloc\n"); - kfree(cval); - return -ENOMEM; - } -#define MAX_ITEM_NAME_LEN 64 - for (i = 0; i < num_ins; i++) { - struct usb_audio_term iterm; - len = 0; - namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); - if (! namelist[i]) { - snd_printk(KERN_ERR "cannot malloc\n"); - while (i--) - kfree(namelist[i]); - kfree(namelist); - kfree(cval); - return -ENOMEM; - } - len = check_mapped_selector_name(state, unitid, i, namelist[i], - MAX_ITEM_NAME_LEN); - if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0) - len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); - if (! len) - sprintf(namelist[i], "Input %d", i); - } - - kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); - if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - kfree(namelist); - kfree(cval); - return -ENOMEM; - } - kctl->private_value = (unsigned long)namelist; - kctl->private_free = usb_mixer_selector_elem_free; - - nameid = desc[desc[0] - 1]; - len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); - if (len) - ; - else if (nameid) - snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); - else { - len = get_term_name(state, &state->oterm, - kctl->id.name, sizeof(kctl->id.name), 0); - if (! len) - strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); - - if ((state->oterm.type & 0xff00) == 0x0100) - append_ctl_name(kctl, " Capture Source"); - else - append_ctl_name(kctl, " Playback Source"); - } - - snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", - cval->id, kctl->id.name, num_ins); - if ((err = add_control_to_empty(state, kctl)) < 0) - return err; - - return 0; -} - - -/* - * parse an audio unit recursively - */ - -static int parse_audio_unit(struct mixer_build *state, int unitid) -{ - unsigned char *p1; - - if (test_and_set_bit(unitid, state->unitbitmap)) - return 0; /* the unit already visited */ - - p1 = find_audio_control_unit(state, unitid); - if (!p1) { - snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); - return -EINVAL; - } - - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - return 0; /* NOP */ - case UAC_MIXER_UNIT: - return parse_audio_mixer_unit(state, unitid, p1); - case UAC_SELECTOR_UNIT: - return parse_audio_selector_unit(state, unitid, p1); - case UAC_FEATURE_UNIT: - return parse_audio_feature_unit(state, unitid, p1); - case UAC_PROCESSING_UNIT_V1: - return parse_audio_processing_unit(state, unitid, p1); - case UAC_EXTENSION_UNIT_V1: - return parse_audio_extension_unit(state, unitid, p1); - default: - snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); - return -EINVAL; - } -} - -static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) -{ - kfree(mixer->id_elems); - if (mixer->urb) { - kfree(mixer->urb->transfer_buffer); - usb_free_urb(mixer->urb); - } - usb_free_urb(mixer->rc_urb); - kfree(mixer->rc_setup_packet); - kfree(mixer); -} - -static int snd_usb_mixer_dev_free(struct snd_device *device) -{ - struct usb_mixer_interface *mixer = device->device_data; - snd_usb_mixer_free(mixer); - return 0; -} - -/* - * create mixer controls - * - * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers - */ -static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) -{ - struct uac_output_terminal_descriptor_v1 *desc; - struct mixer_build state; - int err; - const struct usbmix_ctl_map *map; - struct usb_host_interface *hostif; - - hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; - memset(&state, 0, sizeof(state)); - state.chip = mixer->chip; - state.mixer = mixer; - state.buffer = hostif->extra; - state.buflen = hostif->extralen; - - /* check the mapping table */ - for (map = usbmix_ctl_maps; map->id; map++) { - if (map->id == state.chip->usb_id) { - state.map = map->map; - state.selector_map = map->selector_map; - mixer->ignore_ctl_error = map->ignore_ctl_error; - break; - } - } - - desc = NULL; - while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) { - if (desc->bLength < 9) - continue; /* invalid descriptor? */ - set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); - if (err < 0) - return err; - } - return 0; -} - -void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) -{ - struct usb_mixer_elem_info *info; - - for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); -} - -static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, - int unitid, - struct usb_mixer_elem_info *cval) -{ - static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", - "S8", "U8", "S16", "U16"}; - snd_iprintf(buffer, " Unit: %i\n", unitid); - if (cval->elem_id) - snd_iprintf(buffer, " Control: name=\"%s\", index=%i\n", - cval->elem_id->name, cval->elem_id->index); - snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " - "channels=%i, type=\"%s\"\n", cval->id, - cval->control, cval->cmask, cval->channels, - val_types[cval->val_type]); - snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", - cval->min, cval->max, cval->dBmin, cval->dBmax); -} - -static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct snd_usb_audio *chip = entry->private_data; - struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *cval; - int unitid; - - list_for_each_entry(mixer, &chip->mixer_list, list) { - snd_iprintf(buffer, - "USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n", - chip->usb_id, mixer->ctrlif, - mixer->ignore_ctl_error); - snd_iprintf(buffer, "Card: %s\n", chip->card->longname); - for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (cval = mixer->id_elems[unitid]; cval; - cval = cval->next_id_elem) - snd_usb_mixer_dump_cval(buffer, unitid, cval); - } - } -} - -static void snd_usb_mixer_status_complete(struct urb *urb) -{ - struct usb_mixer_interface *mixer = urb->context; - - if (urb->status == 0) { - u8 *buf = urb->transfer_buffer; - int i; - - for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) { - snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n", - buf[0], buf[1]); - /* ignore any notifications not from the control interface */ - if ((buf[0] & 0x0f) != 0) - continue; - if (!(buf[0] & 0x40)) - snd_usb_mixer_notify_id(mixer, buf[1]); - else - snd_usb_mixer_rc_memory_change(mixer, buf[1]); - } - } - if (urb->status != -ENOENT && urb->status != -ECONNRESET) { - urb->dev = mixer->chip->dev; - usb_submit_urb(urb, GFP_ATOMIC); - } -} - -/* create the handler for the optional status interrupt endpoint */ -static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) -{ - struct usb_host_interface *hostif; - struct usb_endpoint_descriptor *ep; - void *transfer_buffer; - int buffer_length; - unsigned int epnum; - - hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; - /* we need one interrupt input endpoint */ - if (get_iface_desc(hostif)->bNumEndpoints < 1) - return 0; - ep = get_endpoint(hostif, 0); - if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep)) - return 0; - - epnum = usb_endpoint_num(ep); - buffer_length = le16_to_cpu(ep->wMaxPacketSize); - transfer_buffer = kmalloc(buffer_length, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) { - kfree(transfer_buffer); - return -ENOMEM; - } - usb_fill_int_urb(mixer->urb, mixer->chip->dev, - usb_rcvintpipe(mixer->chip->dev, epnum), - transfer_buffer, buffer_length, - snd_usb_mixer_status_complete, mixer, ep->bInterval); - usb_submit_urb(mixer->urb, GFP_KERNEL); - return 0; -} - -int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, - int ignore_error) -{ - static struct snd_device_ops dev_ops = { - .dev_free = snd_usb_mixer_dev_free - }; - struct usb_mixer_interface *mixer; - struct snd_info_entry *entry; - struct usb_host_interface *host_iface; - int err, protocol; - - strcpy(chip->card->mixername, "USB Mixer"); - - mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); - if (!mixer) - return -ENOMEM; - mixer->chip = chip; - mixer->ctrlif = ctrlif; - mixer->ignore_ctl_error = ignore_error; - mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), - GFP_KERNEL); - if (!mixer->id_elems) { - kfree(mixer); - return -ENOMEM; - } - - host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; - protocol = host_iface->desc.bInterfaceProtocol; - - /* FIXME! */ - if (protocol != UAC_VERSION_1) { - snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n", - protocol); - return 0; - } - - if ((err = snd_usb_mixer_controls(mixer)) < 0 || - (err = snd_usb_mixer_status_create(mixer)) < 0) - goto _error; - - snd_usb_mixer_apply_create_quirk(mixer); - - err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); - if (err < 0) - goto _error; - - if (list_empty(&chip->mixer_list) && - !snd_card_proc_new(chip->card, "usbmixer", &entry)) - snd_info_set_text_ops(entry, chip, snd_usb_mixer_proc_read); - - list_add(&mixer->list, &chip->mixer_list); - return 0; - -_error: - snd_usb_mixer_free(mixer); - return err; -} - -void snd_usb_mixer_disconnect(struct list_head *p) -{ - struct usb_mixer_interface *mixer; - - mixer = list_entry(p, struct usb_mixer_interface, list); - usb_kill_urb(mixer->urb); - usb_kill_urb(mixer->rc_urb); -} diff --git a/sound/usb/usbmixer.h b/sound/usb/usbmixer.h deleted file mode 100644 index 63101ae..0000000 --- a/sound/usb/usbmixer.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef __USBMIXER_H -#define __USBMIXER_H - -struct usb_mixer_interface { - struct snd_usb_audio *chip; - unsigned int ctrlif; - struct list_head list; - unsigned int ignore_ctl_error; - struct urb *urb; - /* array[MAX_ID_ELEMS], indexed by unit id */ - struct usb_mixer_elem_info **id_elems; - - /* Sound Blaster remote control stuff */ - const struct rc_config *rc_cfg; - u32 rc_code; - wait_queue_head_t rc_waitq; - struct urb *rc_urb; - struct usb_ctrlrequest *rc_setup_packet; - u8 rc_buffer[6]; - - u8 audigy2nx_leds[3]; - u8 xonar_u1_status; -}; - -#define MAX_CHANNELS 10 /* max logical channels */ - -struct usb_mixer_elem_info { - struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ - struct snd_ctl_elem_id *elem_id; - unsigned int id; - unsigned int control; /* CS or ICN (high byte) */ - unsigned int cmask; /* channel mask bitmap: 0 = master */ - int channels; - int val_type; - int min, max, res; - int dBmin, dBmax; - int cached; - int cache_val[MAX_CHANNELS]; - u8 initialized; -}; - -int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, - int ignore_error); -void snd_usb_mixer_disconnect(struct list_head *p); - -void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); - -int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, - int request, int validx, int value_set); - -#endif /* __USBMIXER_H */ diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c deleted file mode 100644 index d93fc89..0000000 --- a/sound/usb/usbmixer_maps.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Additional mixer mapping - * - * Copyright (c) 2002 by Takashi Iwai - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -struct usbmix_dB_map { - u32 min; - u32 max; -}; - -struct usbmix_name_map { - int id; - const char *name; - int control; - struct usbmix_dB_map *dB; -}; - -struct usbmix_selector_map { - int id; - int count; - const char **names; -}; - -struct usbmix_ctl_map { - u32 id; - const struct usbmix_name_map *map; - const struct usbmix_selector_map *selector_map; - int ignore_ctl_error; -}; - -/* - * USB control mappers for SB Exitigy - */ - -/* - * Topology of SB Extigy (see on the wide screen :) - -USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PU[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24] - ^ | | | | -USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PU[15]->+ | | | +->FU[25] > Dig_OUT[26] - ^ ^ | | | | -Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19] - | | | | -Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20] - | | | -Mic-IN[9] --+->FU[10]----------------------------+ | - || | - || +----------------------------------------------------+ - VV V - ++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13] -*/ - -static struct usbmix_name_map extigy_map[] = { - /* 1: IT pcm */ - { 2, "PCM Playback" }, /* FU */ - /* 3: IT pcm */ - /* 4: IT digital in */ - { 5, NULL }, /* DISABLED: this seems to be bogus on some firmware */ - { 6, "Digital In" }, /* FU */ - /* 7: IT line */ - { 8, "Line Playback" }, /* FU */ - /* 9: IT mic */ - { 10, "Mic Playback" }, /* FU */ - { 11, "Capture Source" }, /* SU */ - { 12, "Capture" }, /* FU */ - /* 13: OT pcm capture */ - /* 14: MU (w/o controls) */ - /* 15: PU (3D enh) */ - /* 16: MU (w/o controls) */ - { 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */ - { 17, "Channel Routing", 2 }, /* PU: mode select */ - { 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */ - { 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */ - { 18, "Master Playback" }, /* FU; others */ - /* 19: OT speaker */ - /* 20: OT headphone */ - { 21, NULL }, /* DISABLED: EU (for what?) */ - { 22, "Digital Out Playback" }, /* FU */ - { 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */ - /* 24: OT digital out */ - { 25, "IEC958 Optical Playback" }, /* FU */ - { 26, "IEC958 Optical Playback" }, /* OT */ - { 27, NULL }, /* DISABLED: EU (for what?) */ - /* 28: FU speaker (mute) */ - { 29, NULL }, /* Digital Input Playback Source? */ - { 0 } /* terminator */ -}; - -/* Sound Blaster MP3+ controls mapping - * The default mixer channels have totally misleading names, - * e.g. no Master and fake PCM volume - * Pavel Mihaylov - */ -static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */ -static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */ - -static struct usbmix_name_map mp3plus_map[] = { - /* 1: IT pcm */ - /* 2: IT mic */ - /* 3: IT line */ - /* 4: IT digital in */ - /* 5: OT digital out */ - /* 6: OT speaker */ - /* 7: OT pcm capture */ - { 8, "Capture Source" }, /* FU, default PCM Capture Source */ - /* (Mic, Input 1 = Line input, Input 2 = Optical input) */ - { 9, "Master Playback" }, /* FU, default Speaker 1 */ - /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */ - { 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 }, - /* FU, Mic Capture */ - { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */ - { 11, "Line Capture", .dB = &mp3plus_dB_2 }, - /* FU, default PCM Capture */ - { 12, "Digital In Playback" }, /* FU, default PCM 1 */ - { 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 }, - /* FU, default Mic Playback */ - { 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */ - /* 15: MU */ - { 0 } /* terminator */ -}; - -/* Topology of SB Audigy 2 NX - - +----------------------------->EU[27]--+ - | v - | +----------------------------------->SU[29]---->FU[22]-->Dig_OUT[24] - | | ^ -USB_IN[1]-+------------+ +->EU[17]->+->FU[11]-+ - | v | v | -Dig_IN[4]---+->FU[6]-->MU[16]->FU[18]-+->EU[21]->SU[31]----->FU[30]->Hph_OUT[20] - | ^ | | -Lin_IN[7]-+--->FU[8]---+ +->EU[23]->FU[28]------------->Spk_OUT[19] - | | v - +--->FU[12]------------------------------------->SU[14]--->USB_OUT[15] - | ^ - +->FU[13]--------------------------------------+ -*/ -static struct usbmix_name_map audigy2nx_map[] = { - /* 1: IT pcm playback */ - /* 4: IT digital in */ - { 6, "Digital In Playback" }, /* FU */ - /* 7: IT line in */ - { 8, "Line Playback" }, /* FU */ - { 11, "What-U-Hear Capture" }, /* FU */ - { 12, "Line Capture" }, /* FU */ - { 13, "Digital In Capture" }, /* FU */ - { 14, "Capture Source" }, /* SU */ - /* 15: OT pcm capture */ - /* 16: MU w/o controls */ - { 17, NULL }, /* DISABLED: EU (for what?) */ - { 18, "Master Playback" }, /* FU */ - /* 19: OT speaker */ - /* 20: OT headphone */ - { 21, NULL }, /* DISABLED: EU (for what?) */ - { 22, "Digital Out Playback" }, /* FU */ - { 23, NULL }, /* DISABLED: EU (for what?) */ - /* 24: OT digital out */ - { 27, NULL }, /* DISABLED: EU (for what?) */ - { 28, "Speaker Playback" }, /* FU */ - { 29, "Digital Out Source" }, /* SU */ - { 30, "Headphone Playback" }, /* FU */ - { 31, "Headphone Source" }, /* SU */ - { 0 } /* terminator */ -}; - -static struct usbmix_selector_map audigy2nx_selectors[] = { - { - .id = 14, /* Capture Source */ - .count = 3, - .names = (const char*[]) {"Line", "Digital In", "What-U-Hear"} - }, - { - .id = 29, /* Digital Out Source */ - .count = 3, - .names = (const char*[]) {"Front", "PCM", "Digital In"} - }, - { - .id = 31, /* Headphone Source */ - .count = 2, - .names = (const char*[]) {"Front", "Side"} - }, - { 0 } /* terminator */ -}; - -/* Creative SoundBlaster Live! 24-bit External */ -static struct usbmix_name_map live24ext_map[] = { - /* 2: PCM Playback Volume */ - { 5, "Mic Capture" }, /* FU, default PCM Capture Volume */ - { 0 } /* terminator */ -}; - -/* LineX FM Transmitter entry - needed to bypass controls bug */ -static struct usbmix_name_map linex_map[] = { - /* 1: IT pcm */ - /* 2: OT Speaker */ - { 3, "Master" }, /* FU: master volume - left / right / mute */ - { 0 } /* terminator */ -}; - -static struct usbmix_name_map maya44_map[] = { - /* 1: IT line */ - { 2, "Line Playback" }, /* FU */ - /* 3: IT line */ - { 4, "Line Playback" }, /* FU */ - /* 5: IT pcm playback */ - /* 6: MU */ - { 7, "Master Playback" }, /* FU */ - /* 8: OT speaker */ - /* 9: IT line */ - { 10, "Line Capture" }, /* FU */ - /* 11: MU */ - /* 12: OT pcm capture */ - { } -}; - -/* Section "justlink_map" below added by James Courtier-Dutton - * sourced from Maplin Electronics (http://www.maplin.co.uk), part number A56AK - * Part has 2 connectors that act as a single output. (TOSLINK Optical for digital out, and 3.5mm Jack for Analogue out.) - * The USB Mixer publishes a Microphone and extra Volume controls for it, but none exist on the device, - * so this map removes all unwanted sliders from alsamixer - */ - -static struct usbmix_name_map justlink_map[] = { - /* 1: IT pcm playback */ - /* 2: Not present */ - { 3, NULL}, /* IT mic (No mic input on device) */ - /* 4: Not present */ - /* 5: OT speacker */ - /* 6: OT pcm capture */ - { 7, "Master Playback" }, /* Mute/volume for speaker */ - { 8, NULL }, /* Capture Switch (No capture inputs on device) */ - { 9, NULL }, /* Capture Mute/volume (No capture inputs on device */ - /* 0xa: Not present */ - /* 0xb: MU (w/o controls) */ - { 0xc, NULL }, /* Mic feedback Mute/volume (No capture inputs on device) */ - { 0 } /* terminator */ -}; - -/* TerraTec Aureon 5.1 MkII USB */ -static struct usbmix_name_map aureon_51_2_map[] = { - /* 1: IT USB */ - /* 2: IT Mic */ - /* 3: IT Line */ - /* 4: IT SPDIF */ - /* 5: OT SPDIF */ - /* 6: OT Speaker */ - /* 7: OT USB */ - { 8, "Capture Source" }, /* SU */ - { 9, "Master Playback" }, /* FU */ - { 10, "Mic Capture" }, /* FU */ - { 11, "Line Capture" }, /* FU */ - { 12, "IEC958 In Capture" }, /* FU */ - { 13, "Mic Playback" }, /* FU */ - { 14, "Line Playback" }, /* FU */ - /* 15: MU */ - {} /* terminator */ -}; - -static struct usbmix_name_map scratch_live_map[] = { - /* 1: IT Line 1 (USB streaming) */ - /* 2: OT Line 1 (Speaker) */ - /* 3: IT Line 1 (Line connector) */ - { 4, "Line 1 In" }, /* FU */ - /* 5: OT Line 1 (USB streaming) */ - /* 6: IT Line 2 (USB streaming) */ - /* 7: OT Line 2 (Speaker) */ - /* 8: IT Line 2 (Line connector) */ - { 9, "Line 2 In" }, /* FU */ - /* 10: OT Line 2 (USB streaming) */ - /* 11: IT Mic (Line connector) */ - /* 12: OT Mic (USB streaming) */ - { 0 } /* terminator */ -}; - -/* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+" - * most importand difference is SU[8], it should be set to "Capture Source" - * to make alsamixer and PA working properly. - * FIXME: or mp3plus_map should use "Capture Source" too, - * so this maps can be merget - */ -static struct usbmix_name_map hercules_usb51_map[] = { - { 8, "Capture Source" }, /* SU, default "PCM Capture Source" */ - { 9, "Master Playback" }, /* FU, default "Speaker Playback" */ - { 10, "Mic Boost", 7 }, /* FU, default "Auto Gain Input" */ - { 11, "Line Capture" }, /* FU, default "PCM Capture" */ - { 13, "Mic Bypass Playback" }, /* FU, default "Mic Playback" */ - { 14, "Line Bypass Playback" }, /* FU, default "Line Playback" */ - { 0 } /* terminator */ -}; - -/* - * Control map entries - */ - -static struct usbmix_ctl_map usbmix_ctl_maps[] = { - { - .id = USB_ID(0x041e, 0x3000), - .map = extigy_map, - .ignore_ctl_error = 1, - }, - { - .id = USB_ID(0x041e, 0x3010), - .map = mp3plus_map, - }, - { - .id = USB_ID(0x041e, 0x3020), - .map = audigy2nx_map, - .selector_map = audigy2nx_selectors, - }, - { - .id = USB_ID(0x041e, 0x3040), - .map = live24ext_map, - }, - { - .id = USB_ID(0x041e, 0x3048), - .map = audigy2nx_map, - .selector_map = audigy2nx_selectors, - }, - { - /* Hercules DJ Console (Windows Edition) */ - .id = USB_ID(0x06f8, 0xb000), - .ignore_ctl_error = 1, - }, - { - /* Hercules DJ Console (Macintosh Edition) */ - .id = USB_ID(0x06f8, 0xd002), - .ignore_ctl_error = 1, - }, - { - /* Hercules Gamesurround Muse Pocket LT - * (USB 5.1 Channel Audio Adapter) - */ - .id = USB_ID(0x06f8, 0xc000), - .map = hercules_usb51_map, - }, - { - .id = USB_ID(0x08bb, 0x2702), - .map = linex_map, - .ignore_ctl_error = 1, - }, - { - .id = USB_ID(0x0a92, 0x0091), - .map = maya44_map, - }, - { - .id = USB_ID(0x0c45, 0x1158), - .map = justlink_map, - }, - { - .id = USB_ID(0x0ccd, 0x0028), - .map = aureon_51_2_map, - }, - { - .id = USB_ID(0x13e5, 0x0001), - .map = scratch_live_map, - .ignore_ctl_error = 1, - }, - { 0 } /* terminator */ -}; - -- cgit v1.1 From 99fc86450c439039d2ef88d06b222fd51a779176 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:24 +0100 Subject: ALSA: usb-mixer: parse descriptors with structs Introduce a number of new structs for mixer, selector, feature and processing units and some static inline helpers to access fields which have dynamic offsets. Use them in mixer.c to parse the descriptors. This is necessary for the upcoming audio v2 parsers. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 87 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 40 deletions(-) (limited to 'sound') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 4e7c2fd..994b038 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -860,13 +860,14 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) return strlcat(kctl->id.name, str, sizeof(kctl->id.name)); } -static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, +static void build_feature_ctl(struct mixer_build *state, void *raw_desc, unsigned int ctl_mask, int control, struct usb_audio_term *iterm, int unitid) { + struct uac_feature_unit_descriptor *desc = raw_desc; unsigned int len = 0; int mapped_name = 0; - int nameid = desc[desc[0] - 1]; + int nameid = uac_feature_unit_iFeature(desc); struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; @@ -1032,7 +1033,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void channels = (ftr->bLength - 7) / csize - 1; - master_bits = snd_usb_combine_bytes(ftr->controls, csize); + master_bits = snd_usb_combine_bytes(ftr->bmaControls, csize); /* master configuration quirks */ switch (state->chip->usb_id) { case USB_ID(0x08bb, 0x2702): @@ -1043,14 +1044,14 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void break; } if (channels > 0) - first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize); + first_ch_bits = snd_usb_combine_bytes(ftr->bmaControls + csize, csize); else first_ch_bits = 0; /* check all control types */ for (i = 0; i < 10; i++) { unsigned int ch_bits = 0; for (j = 0; j < channels; j++) { - unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize); + unsigned int mask = snd_usb_combine_bytes(ftr->bmaControls + csize * (j+1), csize); if (mask & (1 << i)) ch_bits |= (1 << j); } @@ -1075,13 +1076,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void * input channel number (zero based) is given in control field instead. */ -static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, +static void build_mixer_unit_ctl(struct mixer_build *state, + struct uac_mixer_unit_descriptor *desc, int in_pin, int in_ch, int unitid, struct usb_audio_term *iterm) { struct usb_mixer_elem_info *cval; - unsigned int input_pins = desc[4]; - unsigned int num_outs = desc[5 + input_pins]; + unsigned int num_outs = uac_mixer_unit_bNrChannels(desc); unsigned int i, len; struct snd_kcontrol *kctl; const struct usbmix_name_map *map; @@ -1099,7 +1100,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { - if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) { + if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), in_ch, i, num_outs)) { cval->cmask |= (1 << i); cval->channels++; } @@ -1132,18 +1133,19 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, /* * parse a mixer unit */ -static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc) +static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc) { + struct uac_mixer_unit_descriptor *desc = raw_desc; struct usb_audio_term iterm; int input_pins, num_ins, num_outs; int pin, ich, err; - if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) { + if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) { snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid); return -EINVAL; } /* no bmControls field (e.g. Maya44) -> ignore */ - if (desc[0] <= 10 + input_pins) { + if (desc->bLength <= 10 + input_pins) { snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid); return 0; } @@ -1151,10 +1153,10 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne num_ins = 0; ich = 0; for (pin = 0; pin < input_pins; pin++) { - err = parse_audio_unit(state, desc[5 + pin]); + err = parse_audio_unit(state, desc->baSourceID[pin]); if (err < 0) return err; - err = check_input_term(state, desc[5 + pin], &iterm); + err = check_input_term(state, desc->baSourceID[pin], &iterm); if (err < 0) return err; num_ins += iterm.channels; @@ -1162,7 +1164,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne int och, ich_has_controls = 0; for (och = 0; och < num_outs; ++och) { - if (check_matrix_bitmap(desc + 9 + input_pins, + if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), ich, och, num_outs)) { ich_has_controls = 1; break; @@ -1323,9 +1325,10 @@ static struct procunit_info extunits[] = { /* * build a processing/extension unit */ -static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name) +static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name) { - int num_ins = dsc[6]; + struct uac_processing_unit_descriptor *desc = raw_desc; + int num_ins = desc->bNrInPins; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; int i, err, nameid, type, len; @@ -1340,17 +1343,17 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned 0, NULL, default_value_info }; - if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { + if (desc->bLength < 13 || desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc)) { snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); return -EINVAL; } for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, dsc[7 + i])) < 0) + if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) return err; } - type = combine_word(&dsc[4]); + type = le16_to_cpu(desc->wProcessType); for (info = list; info && info->type; info++) if (info->type == type) break; @@ -1358,8 +1361,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned info = &default_info; for (valinfo = info->values; valinfo->control; valinfo++) { - /* FIXME: bitmap might be longer than 8bit */ - if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) + __u8 *controls = uac_processing_unit_bmControls(desc); + + if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) continue; map = find_map(state, unitid, valinfo->control); if (check_ignored_ctl(map)) @@ -1377,9 +1381,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned /* get min/max values */ if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { + __u8 *control_spec = uac_processing_unit_specific(desc); /* FIXME: hard-coded */ cval->min = 1; - cval->max = dsc[15]; + cval->max = control_spec[0]; cval->res = 1; cval->initialized = 1; } else { @@ -1409,7 +1414,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned else if (info->name) strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); else { - nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; + nameid = uac_processing_unit_iProcessing(desc); len = 0; if (nameid) len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); @@ -1428,14 +1433,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned } -static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc) +static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc) { - return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit"); + return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit"); } -static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc) +static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc) { - return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit"); + /* Note that we parse extension units with processing unit descriptors. + * That's ok as the layout is the same */ + return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit"); } @@ -1537,9 +1544,9 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl) /* * parse a selector unit */ -static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc) +static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc) { - unsigned int num_ins = desc[4]; + struct uac_selector_unit_descriptor *desc = raw_desc; unsigned int i, nameid, len; int err; struct usb_mixer_elem_info *cval; @@ -1547,17 +1554,17 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi const struct usbmix_name_map *map; char **namelist; - if (! num_ins || desc[0] < 5 + num_ins) { + if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) { snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid); return -EINVAL; } - for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, desc[5 + i])) < 0) + for (i = 0; i < desc->bNrInPins; i++) { + if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) return err; } - if (num_ins == 1) /* only one ? nonsense! */ + if (desc->bNrInPins == 1) /* only one ? nonsense! */ return 0; map = find_map(state, unitid, 0); @@ -1574,18 +1581,18 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi cval->val_type = USB_MIXER_U8; cval->channels = 1; cval->min = 1; - cval->max = num_ins; + cval->max = desc->bNrInPins; cval->res = 1; cval->initialized = 1; - namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); + namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL); if (! namelist) { snd_printk(KERN_ERR "cannot malloc\n"); kfree(cval); return -ENOMEM; } #define MAX_ITEM_NAME_LEN 64 - for (i = 0; i < num_ins; i++) { + for (i = 0; i < desc->bNrInPins; i++) { struct usb_audio_term iterm; len = 0; namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); @@ -1599,7 +1606,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi } len = check_mapped_selector_name(state, unitid, i, namelist[i], MAX_ITEM_NAME_LEN); - if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0) + if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0) len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); if (! len) sprintf(namelist[i], "Input %d", i); @@ -1615,7 +1622,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi kctl->private_value = (unsigned long)namelist; kctl->private_free = usb_mixer_selector_elem_free; - nameid = desc[desc[0] - 1]; + nameid = uac_selector_unit_iSelector(desc); len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (len) ; @@ -1634,7 +1641,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi } snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", - cval->id, kctl->id.name, num_ins); + cval->id, kctl->id.name, desc->bNrInPins); if ((err = add_control_to_empty(state, kctl)) < 0) return err; -- cgit v1.1 From 23caaf19b11eda7054348452e1618d4512a86907 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:25 +0100 Subject: ALSA: usb-mixer: Add support for Audio Class v2.0 USB Audio Class v2.0 compliant devices have different descriptors and a different way of setting/getting min/max/res/cur properties. This patch adds support for them. Signed-off-by: Daniel Mack Cc: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 322 ++++++++++++++++++++++++++++++++++++++++-------------- sound/usb/mixer.h | 3 + 2 files changed, 245 insertions(+), 80 deletions(-) (limited to 'sound') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 994b038..1deef62 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -197,6 +198,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid, /* * find an audio control unit with the given unit id + * this doesn't return any clock related units, so they need to be handled elsewhere */ static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit) { @@ -205,7 +207,7 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un p = NULL; while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, USB_DT_CS_INTERFACE)) != NULL) { - if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit) + if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit) return p; } return NULL; @@ -302,7 +304,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) * retrieve a mixer value */ -static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) +static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; @@ -324,6 +326,58 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali return -EINVAL; } +static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) +{ + unsigned char buf[14]; /* enough space for one range of 4 bytes */ + unsigned char *val; + int ret; + __u8 bRequest; + + bRequest = (request == UAC_GET_CUR) ? + UAC2_CS_CUR : UAC2_CS_RANGE; + + ret = snd_usb_ctl_msg(cval->mixer->chip->dev, + usb_rcvctrlpipe(cval->mixer->chip->dev, 0), + bRequest, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, cval->mixer->ctrlif | (cval->id << 8), + buf, sizeof(buf), 1000); + + if (ret < 0) { + snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", + request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type); + return ret; + } + + switch (request) { + case UAC_GET_CUR: + val = buf; + break; + case UAC_GET_MIN: + val = buf + sizeof(__u16); + break; + case UAC_GET_MAX: + val = buf + sizeof(__u16) * 2; + break; + case UAC_GET_RES: + val = buf + sizeof(__u16) * 3; + break; + default: + return -EINVAL; + } + + *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16))); + + return 0; +} + +static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) +{ + return (cval->mixer->protocol == UAC_VERSION_1) ? + get_ctl_value_v1(cval, request, validx, value_ret) : + get_ctl_value_v2(cval, request, validx, value_ret); +} + static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value) { return get_ctl_value(cval, UAC_GET_CUR, validx, value); @@ -348,8 +402,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval, err = get_cur_mix_raw(cval, channel, value); if (err < 0) { if (!cval->mixer->ignore_ctl_error) - snd_printd(KERN_ERR "cannot get current value for " - "control %d ch %d: err = %d\n", + snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, channel, err); return err; } @@ -367,8 +420,22 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) { unsigned char buf[2]; - int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; - int timeout = 10; + int val_len, timeout = 10; + + if (cval->mixer->protocol == UAC_VERSION_1) { + val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + } else { /* UAC_VERSION_2 */ + /* audio class v2 controls are always 2 bytes in size */ + val_len = sizeof(__u16); + + /* FIXME */ + if (request != UAC_SET_CUR) { + snd_printdd(KERN_WARNING "RANGE setting not yet supported\n"); + return -EINVAL; + } + + request = UAC2_CS_CUR; + } value_set = convert_bytes_value(cval, value_set); buf[0] = value_set & 0xff; @@ -564,46 +631,65 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm */ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { - unsigned char *p1; + void *p1; memset(term, 0, sizeof(*term)); while ((p1 = find_audio_control_unit(state, id)) != NULL) { + unsigned char *hdr = p1; term->id = id; - switch (p1[2]) { + switch (hdr[2]) { case UAC_INPUT_TERMINAL: - term->type = combine_word(p1 + 4); - term->channels = p1[7]; - term->chconfig = combine_word(p1 + 8); - term->name = p1[11]; + if (state->mixer->protocol == UAC_VERSION_1) { + struct uac_input_terminal_descriptor *d = p1; + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le16_to_cpu(d->wChannelConfig); + term->name = d->iTerminal; + } else { /* UAC_VERSION_2 */ + struct uac2_input_terminal_descriptor *d = p1; + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le32_to_cpu(d->bmChannelConfig); + term->name = d->iTerminal; + } return 0; - case UAC_FEATURE_UNIT: - id = p1[4]; + case UAC_FEATURE_UNIT: { + /* the header is the same for v1 and v2 */ + struct uac_feature_unit_descriptor *d = p1; + id = d->bUnitID; break; /* continue to parse */ - case UAC_MIXER_UNIT: - term->type = p1[2] << 16; /* virtual type */ - term->channels = p1[5 + p1[4]]; - term->chconfig = combine_word(p1 + 6 + p1[4]); - term->name = p1[p1[0] - 1]; + } + case UAC_MIXER_UNIT: { + struct uac_mixer_unit_descriptor *d = p1; + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->channels = uac_mixer_unit_bNrChannels(d); + term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol); + term->name = uac_mixer_unit_iMixer(d); return 0; - case UAC_SELECTOR_UNIT: + } + case UAC_SELECTOR_UNIT: { + struct uac_selector_unit_descriptor *d = p1; /* call recursively to retrieve the channel info */ - if (check_input_term(state, p1[5], term) < 0) + if (check_input_term(state, d->baSourceID[0], term) < 0) return -ENODEV; - term->type = p1[2] << 16; /* virtual type */ + term->type = d->bDescriptorSubtype << 16; /* virtual type */ term->id = id; - term->name = p1[9 + p1[0] - 1]; + term->name = uac_selector_unit_iSelector(d); return 0; + } case UAC_PROCESSING_UNIT_V1: - case UAC_EXTENSION_UNIT_V1: - if (p1[6] == 1) { - id = p1[7]; + case UAC_EXTENSION_UNIT_V1: { + struct uac_processing_unit_descriptor *d = p1; + if (d->bNrInPins) { + id = d->baSourceID[0]; break; /* continue to parse */ } - term->type = p1[2] << 16; /* virtual type */ - term->channels = p1[7 + p1[6]]; - term->chconfig = combine_word(p1 + 8 + p1[6]); - term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->channels = uac_processing_unit_bNrChannels(d); + term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); + term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); return 0; + } default: return -ENODEV; } @@ -850,6 +936,15 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = { .put = mixer_ctl_feature_put, }; +/* the read-only variant */ +static struct snd_kcontrol_new usb_feature_unit_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later manually */ + .info = mixer_ctl_feature_info, + .get = mixer_ctl_feature_get, + .put = NULL, +}; + /* * build a feature control @@ -862,7 +957,8 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) static void build_feature_ctl(struct mixer_build *state, void *raw_desc, unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid) + struct usb_audio_term *iterm, int unitid, + int read_only) { struct uac_feature_unit_descriptor *desc = raw_desc; unsigned int len = 0; @@ -906,7 +1002,11 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, /* get min/max values */ get_min_max(cval, 0); - kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (read_only) + kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); + else + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); kfree(cval); @@ -1016,24 +1116,34 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void struct usb_audio_term iterm; unsigned int master_bits, first_ch_bits; int err, csize; - struct uac_feature_unit_descriptor *ftr = _ftr; + struct uac_feature_unit_descriptor *hdr = _ftr; + __u8 *bmaControls; + + if (state->mixer->protocol == UAC_VERSION_1) { + csize = hdr->bControlSize; + channels = (hdr->bLength - 7) / csize - 1; + bmaControls = hdr->bmaControls; + } else { + struct uac2_feature_unit_descriptor *ftr = _ftr; + csize = 4; + channels = (hdr->bLength - 6) / 4; + bmaControls = ftr->bmaControls; + } - if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) { + if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) { snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); return -EINVAL; } /* parse the source unit */ - if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0) + if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) return err; /* determine the input source type and name */ - if (check_input_term(state, ftr->bSourceID, &iterm) < 0) + if (check_input_term(state, hdr->bSourceID, &iterm) < 0) return -EINVAL; - channels = (ftr->bLength - 7) / csize - 1; - - master_bits = snd_usb_combine_bytes(ftr->bmaControls, csize); + master_bits = snd_usb_combine_bytes(bmaControls, csize); /* master configuration quirks */ switch (state->chip->usb_id) { case USB_ID(0x08bb, 0x2702): @@ -1044,21 +1154,54 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void break; } if (channels > 0) - first_ch_bits = snd_usb_combine_bytes(ftr->bmaControls + csize, csize); + first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize); else first_ch_bits = 0; - /* check all control types */ - for (i = 0; i < 10; i++) { - unsigned int ch_bits = 0; - for (j = 0; j < channels; j++) { - unsigned int mask = snd_usb_combine_bytes(ftr->bmaControls + csize * (j+1), csize); - if (mask & (1 << i)) - ch_bits |= (1 << j); + + if (state->mixer->protocol == UAC_VERSION_1) { + /* check all control types */ + for (i = 0; i < 10; i++) { + unsigned int ch_bits = 0; + for (j = 0; j < channels; j++) { + unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); + if (mask & (1 << i)) + ch_bits |= (1 << j); + } + /* audio class v1 controls are never read-only */ + if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ + build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0); + if (master_bits & (1 << i)) + build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); + } + } else { /* UAC_VERSION_2 */ + for (i = 0; i < 30/2; i++) { + /* From the USB Audio spec v2.0: + bmaControls() is a (ch+1)-element array of 4-byte bitmaps, + each containing a set of bit pairs. If a Control is present, + it must be Host readable. If a certain Control is not + present then the bit pair must be set to 0b00. + If a Control is present but read-only, the bit pair must be + set to 0b01. If a Control is also Host programmable, the bit + pair must be set to 0b11. The value 0b10 is not allowed. */ + unsigned int ch_bits = 0; + unsigned int ch_read_only = 0; + + for (j = 0; j < channels; j++) { + unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); + if (mask & (1 << (i * 2))) { + ch_bits |= (1 << j); + if (~mask & (1 << ((i * 2) + 1))) + ch_read_only |= (1 << j); + } + } + + /* FIXME: the whole unit is read-only if any of the channels is marked read-only */ + if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ + build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only); + if (master_bits & (1 << i * 2)) + build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, + ~master_bits & (1 << ((i * 2) + 1))); } - if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ - build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid); - if (master_bits & (1 << i)) - build_feature_ctl(state, _ftr, 0, i, &iterm, unitid); } return 0; @@ -1100,7 +1243,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { - if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), in_ch, i, num_outs)) { + if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) { cval->cmask |= (1 << i); cval->channels++; } @@ -1164,7 +1307,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r int och, ich_has_controls = 0; for (och = 0; och < num_outs; ++och) { - if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), + if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), ich, och, num_outs)) { ich_has_controls = 1; break; @@ -1343,7 +1486,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw 0, NULL, default_value_info }; - if (desc->bLength < 13 || desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc)) { + if (desc->bLength < 13 || desc->bLength < 13 + num_ins || + desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) { snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); return -EINVAL; } @@ -1361,7 +1505,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw info = &default_info; for (valinfo = info->values; valinfo->control; valinfo++) { - __u8 *controls = uac_processing_unit_bmControls(desc); + __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol); if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) continue; @@ -1381,7 +1525,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw /* get min/max values */ if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { - __u8 *control_spec = uac_processing_unit_specific(desc); + __u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol); /* FIXME: hard-coded */ cval->min = 1; cval->max = control_spec[0]; @@ -1414,7 +1558,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw else if (info->name) strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); else { - nameid = uac_processing_unit_iProcessing(desc); + nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); @@ -1676,9 +1820,17 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) case UAC_FEATURE_UNIT: return parse_audio_feature_unit(state, unitid, p1); case UAC_PROCESSING_UNIT_V1: - return parse_audio_processing_unit(state, unitid, p1); + /* UAC2_EFFECT_UNIT has the same value */ + if (state->mixer->protocol == UAC_VERSION_1) + return parse_audio_processing_unit(state, unitid, p1); + else + return 0; /* FIXME - effect units not implemented yet */ case UAC_EXTENSION_UNIT_V1: - return parse_audio_extension_unit(state, unitid, p1); + /* UAC2_PROCESSING_UNIT_V2 has the same value */ + if (state->mixer->protocol == UAC_VERSION_1) + return parse_audio_extension_unit(state, unitid, p1); + else /* UAC_VERSION_2 */ + return parse_audio_processing_unit(state, unitid, p1); default: snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); return -EINVAL; @@ -1711,11 +1863,11 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) */ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) { - struct uac_output_terminal_descriptor_v1 *desc; struct mixer_build state; int err; const struct usbmix_ctl_map *map; struct usb_host_interface *hostif; + void *p; hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; memset(&state, 0, sizeof(state)); @@ -1734,18 +1886,35 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) } } - desc = NULL; - while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) { - if (desc->bLength < 9) - continue; /* invalid descriptor? */ - set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); - if (err < 0) - return err; + p = NULL; + while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) { + if (mixer->protocol == UAC_VERSION_1) { + struct uac_output_terminal_descriptor_v1 *desc = p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ + state.oterm.id = desc->bTerminalID; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0) + return err; + } else { /* UAC_VERSION_2 */ + struct uac2_output_terminal_descriptor *desc = p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ + state.oterm.id = desc->bTerminalID; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0) + return err; + } } + return 0; } @@ -1868,7 +2037,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, struct usb_mixer_interface *mixer; struct snd_info_entry *entry; struct usb_host_interface *host_iface; - int err, protocol; + int err; strcpy(chip->card->mixername, "USB Mixer"); @@ -1886,14 +2055,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, } host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; - protocol = host_iface->desc.bInterfaceProtocol; - - /* FIXME! */ - if (protocol != UAC_VERSION_1) { - snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n", - protocol); - return 0; - } + mixer->protocol = host_iface->desc.bInterfaceProtocol; if ((err = snd_usb_mixer_controls(mixer)) < 0 || (err = snd_usb_mixer_status_create(mixer)) < 0) diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 63101ae..1301238 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -10,6 +10,9 @@ struct usb_mixer_interface { /* array[MAX_ID_ELEMS], indexed by unit id */ struct usb_mixer_elem_info **id_elems; + /* the usb audio specification version this interface complies to */ + int protocol; + /* Sound Blaster remote control stuff */ const struct rc_config *rc_cfg; u32 rc_code; -- cgit v1.1 From da3b062e306452ffb74cf5e9e5128f9f1e0502ab Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 18 Mar 2010 09:39:59 +0100 Subject: ASoC: SIU driver shall select FW_LOADER The SIU ASoC driver must load firmware to program the DSP, therefore it has to select FW_LOADER in its Kconfig entry. Signed-off-by: Guennadi Liakhovetski Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/sh/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 1066749..f07f6d8 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -32,6 +32,7 @@ config SND_SOC_SH4_SIU select DMA_ENGINE select DMADEVICES select SH_DMAE + select FW_LOADER ## ## Boards -- cgit v1.1 From 44f497b4e0bba6ce1b73a107cc13636393344252 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 19 Mar 2010 11:10:19 +0200 Subject: ASoC: tlv320dac33: Fix DSP modes To make DSP_A mode working correctly the data delay should be configured to 0. DSP_B mode thus can not be used with DAC33, so remove it. Signed-off-by: Peter Ujfalusi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index f9f367d..00d6f36 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1038,11 +1038,7 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_DSP_A: aictrl_a |= DAC33_AFMT_DSP; aictrl_b &= ~DAC33_DATA_DELAY_MASK; - aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */ - break; - case SND_SOC_DAIFMT_DSP_B: - aictrl_a |= DAC33_AFMT_DSP; - aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */ + aictrl_b |= DAC33_DATA_DELAY(0); break; case SND_SOC_DAIFMT_RIGHT_J: aictrl_a |= DAC33_AFMT_RIGHT_J; -- cgit v1.1 From fdb6b1e195757a66670801702e4b5fcc66ed3d72 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 19 Mar 2010 11:10:20 +0200 Subject: ASoC: tlv320dac33: Internal clocking changes During validation of the internal clocking setup it has been found that the following settings were not configured in an optimal way: ASRC_CTRL_A: SRCLKDIV was incorrect, instad of divide ratio 3, ratio of 2 has to be used (as the comment stated) DAC_CTRL_A: Fs = Fsref is the desired configuration instead of Fs = Fsref / 1.5 Signed-off-by: Peter Ujfalusi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 00d6f36..d50f169 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -778,7 +778,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) if (dac33->fifo_mode) { /* Generic for all FIFO modes */ /* 50-51 : ASRC Control registers */ - dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */ + dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCLKDIV(1)); dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */ /* Write registers 0x34 and 0x35 (MSB, LSB) */ @@ -1062,7 +1062,7 @@ static void dac33_init_chip(struct snd_soc_codec *codec) { /* 44-46: DAC Control Registers */ /* A : DAC sample rate Fsref/1.5 */ - dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1)); + dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0)); /* B : DAC src=normal, not muted */ dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT | DAC33_DACSRCL_LEFT); -- cgit v1.1 From 6937c947d31186750f72c9f8c942bbcc6fe63585 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Mar 2010 12:25:35 +0000 Subject: ASoC: Bail out of wm_hubs DC servo if calibration fails We're keeping track of the number of times we've iterated but never actually using this to bail out if the chip looks stuck. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm_hubs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 0ad9f5d..486bdd2 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -74,7 +74,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) msleep(1); reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); dev_dbg(codec->dev, "DC servo: %x\n", reg); - } while (reg & WM8993_DCS_DATAPATH_BUSY); + } while (reg & WM8993_DCS_DATAPATH_BUSY && count < 400); if (reg & WM8993_DCS_DATAPATH_BUSY) dev_err(codec->dev, "Timed out waiting for DC Servo\n"); -- cgit v1.1 From 8727b909bb2348d29e62c599cd7a5d610da3760f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 28 Feb 2010 10:42:38 +0800 Subject: ASoC: pxa-pcm-lib: initialize DMA channel to -1 This fixes a warning ("pxa_free_dma: trying to free channel 0 which is already freed") when a device was opened but the hw_params() call failed. Signed-off-by: Daniel Mack Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/arm/pxa2xx-pcm-lib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 743ac6a..fd51fa8 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -205,6 +205,7 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) if (!rtd->dma_desc_array) goto err1; + rtd->dma_ch = -1; runtime->private_data = rtd; return 0; -- cgit v1.1 From e3d2530a6cea80987f77b75d8784a00f3aaf22ff Mon Sep 17 00:00:00 2001 From: Kunal Gangakhedkar Date: Sat, 20 Mar 2010 23:08:01 +0530 Subject: ALSA: hda - Add PCI quirk for HP dv6-1110ax. Adding this PCI quirk fixes the board config detection. This also fixes jack sensing by using "hp_detect=1" via properly detected board config. Signed-off-by: Kunal Gangakhedkar Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8c416bb..c4be3fa 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1730,6 +1730,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP HDX", STAC_HP_HDX), /* HDX16 */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, "HP dv6", STAC_HP_DV5), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, + "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, "HP", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, -- cgit v1.1 From 025f206c9e0f96cc41567b01c07fb852d8900da1 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 21 Mar 2010 18:34:43 -0400 Subject: ALSA: hda: Fix 0 dB offset for HP laptops using CX20551 (Waikiki) BugLink: https://launchpad.net/bugs/420578 The OR has verified that his hardware distorts because of the 0 dB offset not corresponding to the highest PCM level. Fix this by capping said PCM level to 0 dB similarly to what we do for CX20549 (Venice). Reported-by: Mike Pontillo Tested-by: Mike Pontillo Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 194a28c..61682e1 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1591,6 +1591,21 @@ static int patch_cxt5047(struct hda_codec *codec) #endif } spec->vmaster_nid = 0x13; + + switch (codec->subsystem_id >> 16) { + case 0x103c: + /* HP laptops have really bad sound over 0 dB on NID 0x10. + * Fix max PCM level to 0 dB (originally it has 0x1e steps + * with 0 dB offset 0x17) + */ + snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + break; + } + return 0; } -- cgit v1.1 From e933e9e5238b79870b04718024416a6dcf602a27 Mon Sep 17 00:00:00 2001 From: Derek Kelly Date: Mon, 22 Mar 2010 08:04:19 +0100 Subject: ALSA: hda - Add support of Nvidia GT220 HDMI This patch adds the device id for Nvidia GT220 cards to the nvhdmi driver. I have tested it and confirmed it to be working. Original patch download link: https://gist.github.com/324070/ Signed-off-by: Derek Kelly Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_nvhdmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 70669a2..9e47717 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -554,6 +554,8 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000b, .name = "GT21x HDMI", .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de000a, .name = "GT220 HDMI", + .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000d, .name = "GT240 HDMI", .patch = patch_nvhdmi_8ch_89 }, {} /* terminator */ @@ -568,6 +570,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0067"); MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_ALIAS("snd-hda-codec-id:10de000c"); MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000a"); MODULE_ALIAS("snd-hda-codec-id:10de000d"); MODULE_LICENSE("GPL"); -- cgit v1.1 From ea823c08912cfb6d4af2fa8b6dd5d8deb2fb486a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Mar 2010 08:07:55 +0100 Subject: ALSA: hda - Sort codec entry list of Nvidia HDMI Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_nvhdmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 9e47717..3c10c0b 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -538,8 +538,6 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) * patch entries */ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, - { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0003, .name = "MCP77/78 HDMI", @@ -550,14 +548,16 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de000c, .name = "MCP89 HDMI", + { .id = 0x10de000a, .name = "GT220 HDMI", .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000b, .name = "GT21x HDMI", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de000a, .name = "GT220 HDMI", + { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000d, .name = "GT240 HDMI", .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, + { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, {} /* terminator */ }; @@ -566,12 +566,12 @@ MODULE_ALIAS("snd-hda-codec-id:10de0003"); MODULE_ALIAS("snd-hda-codec-id:10de0005"); MODULE_ALIAS("snd-hda-codec-id:10de0006"); MODULE_ALIAS("snd-hda-codec-id:10de0007"); -MODULE_ALIAS("snd-hda-codec-id:10de0067"); -MODULE_ALIAS("snd-hda-codec-id:10de8001"); -MODULE_ALIAS("snd-hda-codec-id:10de000c"); -MODULE_ALIAS("snd-hda-codec-id:10de000b"); MODULE_ALIAS("snd-hda-codec-id:10de000a"); +MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000c"); MODULE_ALIAS("snd-hda-codec-id:10de000d"); +MODULE_ALIAS("snd-hda-codec-id:10de0067"); +MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec"); -- cgit v1.1 From bae84e70d66fe46c12231082cf1c4848ea22f3ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Mar 2010 08:30:20 +0100 Subject: ALSA: hda - Fix access-after-free in patch_realtek.c alc_free_kctls() has to be called after all jobs done in alc_build_controls(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4ec5763..053d53d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2532,8 +2532,6 @@ static int alc_build_controls(struct hda_codec *codec) return err; } - alc_free_kctls(codec); /* no longer needed */ - /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); if (!kctl) @@ -2602,6 +2600,9 @@ static int alc_build_controls(struct hda_codec *codec) } } } + + alc_free_kctls(codec); /* no longer needed */ + return 0; } -- cgit v1.1 From 3cc4e53f86dab635166929bfa47cc68d59b28c26 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 12 Feb 2010 14:39:36 +0000 Subject: ASoC: Remove BROKEN from i.MX audio after dependencies merged Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/imx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index c7d0fd9..7174b4c 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -1,6 +1,6 @@ config SND_IMX_SOC tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC && BROKEN + depends on ARCH_MXC select SND_PCM select FIQ select SND_SOC_AC97_BUS -- cgit v1.1 From 6407d474e6ae6a798fa5ba40b32f508a52de80ff Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 22 Mar 2010 08:55:35 -0700 Subject: ALSA: usb: fix usb build error when PM is not enabled Fix build errors when CONFIG_PM is not enabled: sound/usb/card.c:629: error: 'usb_audio_suspend' undeclared here (not in a function) sound/usb/card.c:630: error: 'usb_audio_resume' undeclared here (not in a function) Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai --- sound/usb/card.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 0bd62a1..da1346b 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -606,6 +606,9 @@ static int usb_audio_resume(struct usb_interface *intf) return 0; } +#else +#define usb_audio_suspend NULL +#define usb_audio_resume NULL #endif /* CONFIG_PM */ static struct usb_device_id usb_audio_ids [] = { -- cgit v1.1 From 306ff3e473a970f88680e8355c0900fcab0357e2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 22 Mar 2010 15:12:07 +0100 Subject: ALSA: ua101: remove experimental status Now that the EHCI driver copes with small iso packets without blowing up, take the snd-ua101 driver out of the alpha-test stage. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/usb/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index c570ae3..3273b47 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -22,8 +22,7 @@ config SND_USB_AUDIO will be called snd-usb-audio. config SND_USB_UA101 - tristate "Edirol UA-101/UA-1000 driver (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Edirol UA-101/UA-1000 driver" select SND_PCM select SND_RAWMIDI help -- cgit v1.1 From a8462bde78fdb77c8ede61e1af99617905a78ccf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Mar 2010 14:58:34 +0300 Subject: ASoC: wm8994: playback => capture Sparse caught that initialize "playback" two times instead of initializing "capture". Signed-off-by: Dan Carpenter Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 29f3771..d10d651 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3401,7 +3401,7 @@ struct snd_soc_dai wm8994_dai[] = { .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, - .playback = { + .capture = { .stream_name = "AIF3 Capture", .channels_min = 2, .channels_max = 2, -- cgit v1.1 From fca5bca48759c21eddc0667a4582a227d7b0165a Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Thu, 25 Mar 2010 11:29:14 +0100 Subject: ALSA: usbaudio: Add basic support for M-Audio Fast Track Ultra series This adds basic support for M-Audio's Fast Track Ultra series of USB audio interfaces. It is a refactored version of the patch Clemens Ladisch posted some time ago. Neither playback nor capturing work properly at 44100 Hz (don't know why). The other sampling rates work properly. There's no support for the DSP mixer, yet. Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 2 ++ sound/usb/quirks-table.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/urb.c | 5 +++++ 3 files changed, 61 insertions(+) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index b1309cd..7b84b61 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -317,6 +317,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) break; case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */ + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ /* doesn't set the sample rate attribute, but supports it */ fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; break; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 81c5f8a..91ddef3 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1826,6 +1826,60 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE(0x0763, 0x2080), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Fast Track Ultra 8", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + /* interface 3 (MIDI) is standard compliant */ + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0763, 0x2081), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Fast Track Ultra 8R", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + /* interface 3 (MIDI) is standard compliant */ + { + .ifnum = -1 + } + } + } +}, /* Casio devices */ { diff --git a/sound/usb/urb.c b/sound/usb/urb.c index ad50d43..e2b7c478 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -918,6 +918,11 @@ void snd_usb_init_substream(struct snd_usb_stream *as, case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; break; + case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */ + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ + subs->ops.prepare_sync = prepare_playback_sync_urb; + subs->ops.retire_sync = retire_playback_sync_urb; + break; } } -- cgit v1.1 From 6a4f2ccb467e00281470cde2dee08fe5ecde62d1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 25 Mar 2010 15:00:15 +0100 Subject: ALSA: hda - Don't set invalid connection index in Realtek initialiaiton Skip initialization of connections of DAC widgets that aren't used, which resulted in invalid verb parameters. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 053d53d..9a23444 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10043,8 +10043,11 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; - else + else { + if (spec->multiout.num_dacs >= dac_idx) + return; idx = spec->multiout.dac_nids[dac_idx] - 2; + } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } -- cgit v1.1 From 36db0456582484aa78809376f1e41ec2873fc9d5 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 29 Mar 2010 16:02:50 +1100 Subject: ALSA: usb - use of kmalloc/kfree requires the include of slab.h Signed-off-by: Stephen Rothwell Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 1 + sound/usb/format.c | 1 + sound/usb/helper.c | 1 + sound/usb/mixer_quirks.c | 1 + sound/usb/quirks.c | 1 + 5 files changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7b84b61..ef07a6d 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/sound/usb/format.c b/sound/usb/format.c index fcadedd..b87cf87 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/sound/usb/helper.c b/sound/usb/helper.c index ba7dba4..d48d6f8 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -16,6 +16,7 @@ */ #include +#include #include #include "usbaudio.h" diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 56b6659..e7df1e5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -26,6 +26,7 @@ */ #include +#include #include #include diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d4ced64..136e5b4 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -15,6 +15,7 @@ */ #include +#include #include #include -- cgit v1.1 From 9ec8ddad59fadd8021adfea4cb716a49b0e232e9 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 28 Mar 2010 02:34:40 -0400 Subject: ALSA: hda: Use LPIB for ga-ma770-ud3 board BugLink: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=575669 The OR states that position_fix=1 is necessary to work around glitching during volume adjustments using PulseAudio. Reported-by: Carlos Laviola Tested-by: Carlos Laviola Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8b29156..4bb9067 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2269,6 +2269,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), -- cgit v1.1 From 5dbd5ec6e1cf2e49128025d80813a275744a7ac5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Mar 2010 09:16:24 +0200 Subject: ALSA: hda - Fix invalid bit values passed to snd_hda_codec_amp_stereo() The mask and value parameters passed to snd_hda_codec_amp_stereo() should be 8-bit values for mute and volume. Passing AMP_IN_MUTE() is wrong, which is found in many places in patch_realtek.c as a left-over from the conversion to snd_hda_codec_amp_stereo(). Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a23444..bc55c1e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12459,11 +12459,11 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, 0x15); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc268_acer_lc_unsol_event(struct hda_codec *codec, @@ -13482,11 +13482,11 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, 0x15); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -13511,11 +13511,11 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec) /* Check port replicator headphone socket */ present |= snd_hda_jack_detect(codec, 0x1a); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -13646,11 +13646,11 @@ static void alc269_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, nid); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } /* unsolicited event for HP jack sensing */ @@ -17115,9 +17115,9 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) @@ -17128,13 +17128,13 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) @@ -17145,13 +17145,13 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc662_f5z_speaker_automute(struct hda_codec *codec) @@ -17190,14 +17190,14 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) if (present1 || present2) { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), AMP_IN_MUTE(0)); + HDA_AMP_MUTE, HDA_AMP_MUTE); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), AMP_IN_MUTE(0)); + HDA_AMP_MUTE, HDA_AMP_MUTE); } else { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), 0); + HDA_AMP_MUTE, 0); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), 0); + HDA_AMP_MUTE, 0); } } -- cgit v1.1 From 9966ddafe1b4f2a9a09ea748b42ef69417b23ff1 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 29 Mar 2010 19:01:48 +1100 Subject: ALSA: usb pcm: use of kmalloc requires the include of slab.h Signed-off-by: Stephen Rothwell Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 630e220..2bf0d77 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include -- cgit v1.1 From 6694635d3ae1b038d7a0e38b80637db867c7c8e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Mar 2010 17:21:45 +0200 Subject: ALSA: hda - Fix ADC/MUX assignment of ALC269 codec ALC269 codec has a few different variants, and each of them may have different ADC and MUX widgets. For example, one model has ADC 0x08 with MUX 0x23 while others has ADC 0x09 or ADC 0x07 with MUX 022 or 0x24. The difference of ADC appears usually as the capability of the digital mic pin (0x12), and the current driver sometimes misses the internal mic pin due to the mismatching ADC. This patch adds a bit more clever way to find the matching ADC instead of the static list. Now the driver checks all active input pins and fills only the ADC/MUX's that contain all of them. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 95 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bc55c1e..22aea7b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4984,6 +4984,69 @@ static void set_capture_mixer(struct hda_codec *codec) } } +/* fill adc_nids (and capsrc_nids) containing all active input pins */ +static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, + int num_nids) +{ + struct alc_spec *spec = codec->spec; + int n; + hda_nid_t fallback_adc = 0, fallback_cap = 0; + + for (n = 0; n < num_nids; n++) { + hda_nid_t adc, cap; + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int nconns, i, j; + + adc = nids[n]; + if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN) + continue; + cap = adc; + nconns = snd_hda_get_connections(codec, cap, conn, + ARRAY_SIZE(conn)); + if (nconns == 1) { + cap = conn[0]; + nconns = snd_hda_get_connections(codec, cap, conn, + ARRAY_SIZE(conn)); + } + if (nconns <= 0) + continue; + if (!fallback_adc) { + fallback_adc = adc; + fallback_cap = cap; + } + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (!nid) + continue; + for (j = 0; j < nconns; j++) { + if (conn[j] == nid) + break; + } + if (j >= nconns) + break; + } + if (i >= AUTO_PIN_LAST) { + int num_adcs = spec->num_adc_nids; + spec->private_adc_nids[num_adcs] = adc; + spec->private_capsrc_nids[num_adcs] = cap; + spec->num_adc_nids++; + spec->adc_nids = spec->private_adc_nids; + if (adc != cap) + spec->capsrc_nids = spec->private_capsrc_nids; + } + } + if (!spec->num_adc_nids) { + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", fallback_adc); + spec->private_adc_nids[0] = fallback_adc; + spec->adc_nids = spec->private_adc_nids; + if (fallback_adc != fallback_cap) { + spec->private_capsrc_nids[0] = fallback_cap; + spec->capsrc_nids = spec->private_adc_nids; + } + } +} + #ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) @@ -13333,9 +13396,9 @@ static hda_nid_t alc269vb_capsrc_nids[1] = { 0x22, }; -/* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24), - * not a mux! - */ +static hda_nid_t alc269_adc_candidates[] = { + 0x08, 0x09, 0x07, +}; #define alc269_modes alc260_modes #define alc269_capture_source alc880_lg_lw_capture_source @@ -13842,7 +13905,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err; static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - hda_nid_t real_capsrc_nids; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, alc269_ignore); @@ -13866,18 +13928,19 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { add_verb(spec, alc269vb_init_verbs); - real_capsrc_nids = alc269vb_capsrc_nids[0]; alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); } else { add_verb(spec, alc269_init_verbs); - real_capsrc_nids = alc269_capsrc_nids[0]; alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); } spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + fillup_priv_adc_nids(codec, alc269_adc_candidates, + sizeof(alc269_adc_candidates)); + /* set default input source */ - snd_hda_codec_write_cache(codec, real_capsrc_nids, + snd_hda_codec_write_cache(codec, spec->capsrc_nids[0], 0, AC_VERB_SET_CONNECT_SEL, spec->input_mux->items[0].index); @@ -14156,14 +14219,16 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_playback = &alc269_pcm_digital_playback; spec->stream_digital_capture = &alc269_pcm_digital_capture; - if (!is_alc269vb) { - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; - } else { - spec->adc_nids = alc269vb_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); - spec->capsrc_nids = alc269vb_capsrc_nids; + if (!spec->adc_nids) { /* wasn't filled automatically? use default */ + if (!is_alc269vb) { + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->capsrc_nids = alc269_capsrc_nids; + } else { + spec->adc_nids = alc269vb_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); + spec->capsrc_nids = alc269vb_capsrc_nids; + } } if (!spec->cap_mixer) -- cgit v1.1 From 7b7b9042263f5cafb6ce85b3764375a8de7e22da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 30 Mar 2010 02:52:29 +0900 Subject: ALSA: usb - update gfp/slab.h includes Implicit slab.h inclusion via percpu.h is about to go away. Make sure gfp.h or slab.h is included as necessary. Signed-off-by: Tejun Heo Signed-off-by: Takashi Iwai --- sound/usb/caiaq/input.c | 1 + sound/usb/urb.c | 1 + 2 files changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 27ed0bc..8bbfbfd 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -16,6 +16,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include diff --git a/sound/usb/urb.c b/sound/usb/urb.c index e2b7c478..5570a2b 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -15,6 +15,7 @@ * */ +#include #include #include #include -- cgit v1.1 From fb48e3c6a4d8888aff61fbf567aadac7d206e973 Mon Sep 17 00:00:00 2001 From: Graham Gower Date: Thu, 25 Mar 2010 10:52:12 +1030 Subject: ASoC: Fix passing platform_data to ac97 bus users and fix a leak [The issue is an attempt to write the pdata without the AC97 device allocated when using ac97.c - also added a comment in soc-core.c for the special case for ac97. -- broonie] Signed-off-by: Graham Gower Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/ac97.c | 15 +++++++++------ sound/soc/soc-core.c | 3 ++- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index a1bbe16..bcfa532 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -80,9 +80,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, static int ac97_soc_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_card *card = socdev->card; struct snd_soc_codec *codec; struct snd_ac97_bus *ac97_bus; struct snd_ac97_template ac97_template; + int i; int ret = 0; printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION); @@ -102,12 +104,6 @@ static int ac97_soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); - ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); - if (ret < 0) { - printk(KERN_ERR "ASoC: failed to init gen ac97 glue\n"); - goto err; - } - /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) @@ -123,6 +119,13 @@ static int ac97_soc_probe(struct platform_device *pdev) if (ret < 0) goto bus_err; + for (i = 0; i < card->num_links; i++) { + if (card->dai_link[i].codec_dai->ac97_control) { + snd_ac97_dev_add_pdata(codec->ac97, + card->dai_link[i].cpu_dai->ac97_pdata); + } + } + return 0; bus_err: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c8b0556..d0efd5e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1548,7 +1548,8 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) mutex_unlock(&codec->mutex); return ret; } - if (card->dai_link[i].codec_dai->ac97_control) { + /* Check for codec->ac97 to handle the ac97.c fun */ + if (card->dai_link[i].codec_dai->ac97_control && codec->ac97) { snd_ac97_dev_add_pdata(codec->ac97, card->dai_link[i].cpu_dai->ac97_pdata); } -- cgit v1.1 From 1f85d72d2c9c9a1d6d32cf325936bc224ad5d591 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Mar 2010 07:48:05 +0200 Subject: ALSA: hda - Add missing printk argument in previous patch Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 22aea7b..ca93c4c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5037,7 +5037,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, } if (!spec->num_adc_nids) { printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", fallback_adc); + " using fallback 0x%x\n", + codec->chip_name, fallback_adc); spec->private_adc_nids[0] = fallback_adc; spec->adc_nids = spec->private_adc_nids; if (fallback_adc != fallback_cap) { -- cgit v1.1 From 5a0e3ad6af8660be21ca98a971cd00f331318c05 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Mar 2010 17:04:11 +0900 Subject: include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo Guess-its-ok-by: Christoph Lameter Cc: Ingo Molnar Cc: Lee Schermerhorn --- sound/aoa/codecs/onyx.c | 1 + sound/aoa/codecs/tas.c | 1 + sound/aoa/codecs/toonie.c | 1 + sound/aoa/core/gpio-pmf.c | 1 + sound/aoa/fabrics/layout.c | 1 + sound/aoa/soundbus/i2sbus/control.c | 1 + sound/aoa/soundbus/i2sbus/core.c | 1 + sound/aoa/soundbus/i2sbus/pcm.c | 1 + sound/arm/pxa2xx-pcm-lib.c | 1 + sound/core/control_compat.c | 1 + sound/core/hrtimer.c | 1 + sound/core/info.c | 1 + sound/core/jack.c | 1 + sound/core/misc.c | 1 + sound/core/oss/route.c | 1 - sound/core/pcm_compat.c | 1 + sound/core/pcm_memory.c | 1 + sound/core/seq/oss/seq_oss_init.c | 1 + sound/core/seq/oss/seq_oss_midi.c | 1 + sound/core/seq/oss/seq_oss_readq.c | 1 + sound/core/seq/oss/seq_oss_synth.c | 1 + sound/core/seq/oss/seq_oss_timer.c | 1 + sound/core/seq/oss/seq_oss_writeq.c | 1 + sound/core/seq/seq_compat.c | 1 + sound/core/seq/seq_system.c | 1 + sound/drivers/ml403-ac97cr.c | 1 + sound/drivers/mtpav.c | 1 - sound/drivers/mts64.c | 1 + sound/drivers/opl3/opl3_oss.c | 1 - sound/drivers/opl3/opl3_synth.c | 1 + sound/drivers/opl4/opl4_lib.c | 1 + sound/drivers/pcsp/pcsp_lib.c | 1 + sound/drivers/portman2x4.c | 1 + sound/drivers/vx/vx_hwdep.c | 1 + sound/i2c/other/tea575x-tuner.c | 1 + sound/isa/cmi8330.c | 1 - sound/isa/cs423x/cs4236.c | 1 - sound/isa/es18xx.c | 1 - sound/isa/gus/interwave.c | 1 - sound/isa/msnd/msnd_midi.c | 1 + sound/isa/opl3sa2.c | 1 - sound/isa/opti9xx/miro.c | 1 - sound/isa/opti9xx/opti92x-ad1848.c | 1 - sound/isa/sb/emu8000_pcm.c | 1 + sound/isa/sb/sb16.c | 1 - sound/isa/sb/sb8.c | 1 - sound/isa/wavefront/wavefront.c | 1 - sound/isa/wavefront/wavefront_fx.c | 1 + sound/isa/wavefront/wavefront_synth.c | 1 + sound/mips/hal2.c | 1 + sound/mips/sgio2audio.c | 2 +- sound/oss/ad1848.c | 1 + sound/oss/dmabuf.c | 1 + sound/oss/kahlua.c | 1 + sound/oss/mpu401.c | 1 + sound/oss/msnd.c | 1 - sound/oss/msnd_pinnacle.c | 2 +- sound/oss/opl3.c | 1 + sound/oss/sb_card.c | 1 + sound/oss/sb_common.c | 1 + sound/oss/sb_midi.c | 1 + sound/oss/sb_mixer.c | 2 ++ sound/oss/soundcard.c | 1 - sound/oss/uart401.c | 1 + sound/oss/v_midi.c | 1 + sound/oss/vidc.c | 1 + sound/oss/vwsnd.c | 1 + sound/oss/waveartist.c | 1 + sound/pci/ac97/ac97_proc.c | 1 - sound/pci/als4000.c | 1 - sound/pci/aw2/aw2-saa7146.c | 1 - sound/pci/ca0106/ca0106_mixer.c | 1 - sound/pci/ca0106/ca0106_proc.c | 1 - sound/pci/cs5530.c | 1 + sound/pci/cs5535audio/cs5535audio_pcm.c | 1 - sound/pci/cs5535audio/cs5535audio_pm.c | 1 - sound/pci/ctxfi/ctatc.c | 1 + sound/pci/ctxfi/ctpcm.c | 1 + sound/pci/echoaudio/darla20.c | 2 +- sound/pci/echoaudio/darla24.c | 2 +- sound/pci/echoaudio/echo3g.c | 2 +- sound/pci/echoaudio/gina20.c | 2 +- sound/pci/echoaudio/gina24.c | 2 +- sound/pci/echoaudio/indigo.c | 2 +- sound/pci/echoaudio/indigodj.c | 2 +- sound/pci/echoaudio/indigodjx.c | 2 +- sound/pci/echoaudio/indigoio.c | 2 +- sound/pci/echoaudio/indigoiox.c | 2 +- sound/pci/echoaudio/layla20.c | 2 +- sound/pci/echoaudio/layla24.c | 2 +- sound/pci/echoaudio/mia.c | 2 +- sound/pci/echoaudio/mona.c | 2 +- sound/pci/emu10k1/memory.c | 1 + sound/pci/hda/hda_beep.c | 1 + sound/pci/hda/hda_eld.c | 1 + sound/pci/ice1712/ak4xxx.c | 1 + sound/pci/ice1712/amp.c | 1 - sound/pci/ice1712/vt1720_mobo.c | 1 - sound/pci/ice1712/wtm.c | 1 - sound/pci/lx6464es/lx6464es.c | 1 + sound/pci/mixart/mixart.c | 1 + sound/pci/mixart/mixart_hwdep.c | 1 + sound/pci/oxygen/oxygen_lib.c | 1 + sound/pci/rme32.c | 2 +- sound/pci/rme96.c | 1 - sound/pci/rme9652/hdsp.c | 1 - sound/pci/rme9652/rme9652.c | 1 - sound/pci/sis7019.c | 1 + sound/pcmcia/pdaudiocf/pdaudiocf_core.c | 1 + sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c | 1 - sound/pcmcia/vx/vxpocket.c | 1 + sound/ppc/burgundy.c | 1 - sound/ppc/keywest.c | 1 - sound/ppc/snd_ps3.c | 2 +- sound/sh/sh_dac_audio.c | 1 + sound/soc/au1x/psc-ac97.c | 1 + sound/soc/au1x/psc-i2s.c | 1 + sound/soc/blackfin/bf5xx-ac97-pcm.c | 2 +- sound/soc/blackfin/bf5xx-ac97.c | 1 + sound/soc/blackfin/bf5xx-i2s-pcm.c | 2 +- sound/soc/blackfin/bf5xx-tdm-pcm.c | 2 +- sound/soc/codecs/ac97.c | 1 + sound/soc/codecs/ad1836.c | 1 + sound/soc/codecs/ad1938.c | 1 + sound/soc/codecs/ad1980.c | 1 + sound/soc/codecs/ad73311.c | 1 + sound/soc/codecs/ads117x.c | 1 + sound/soc/codecs/ak4104.c | 1 + sound/soc/codecs/ak4535.c | 1 + sound/soc/codecs/ak4642.c | 1 + sound/soc/codecs/ak4671.c | 1 + sound/soc/codecs/cs4270.c | 1 + sound/soc/codecs/cx20442.c | 1 + sound/soc/codecs/da7210.c | 1 + sound/soc/codecs/pcm3008.c | 1 + sound/soc/codecs/ssm2602.c | 1 + sound/soc/codecs/stac9766.c | 1 + sound/soc/codecs/tlv320aic23.c | 1 + sound/soc/codecs/tlv320aic26.c | 1 + sound/soc/codecs/tlv320aic3x.c | 1 + sound/soc/codecs/tlv320dac33.c | 1 + sound/soc/codecs/tpa6130a2.c | 1 + sound/soc/codecs/twl4030.c | 1 + sound/soc/codecs/uda134x.c | 1 + sound/soc/codecs/wm2000.c | 1 + sound/soc/codecs/wm8350.c | 1 + sound/soc/codecs/wm8400.c | 1 + sound/soc/codecs/wm8510.c | 1 + sound/soc/codecs/wm8523.c | 1 + sound/soc/codecs/wm8580.c | 1 + sound/soc/codecs/wm8711.c | 1 + sound/soc/codecs/wm8727.c | 1 + sound/soc/codecs/wm8728.c | 1 + sound/soc/codecs/wm8731.c | 1 + sound/soc/codecs/wm8750.c | 1 + sound/soc/codecs/wm8753.c | 1 + sound/soc/codecs/wm8776.c | 1 + sound/soc/codecs/wm8900.c | 1 + sound/soc/codecs/wm8903.c | 1 + sound/soc/codecs/wm8904.c | 1 + sound/soc/codecs/wm8940.c | 1 + sound/soc/codecs/wm8955.c | 1 + sound/soc/codecs/wm8960.c | 1 + sound/soc/codecs/wm8961.c | 1 + sound/soc/codecs/wm8971.c | 1 + sound/soc/codecs/wm8974.c | 1 + sound/soc/codecs/wm8978.c | 1 + sound/soc/codecs/wm8988.c | 1 + sound/soc/codecs/wm8990.c | 1 + sound/soc/codecs/wm8993.c | 1 + sound/soc/codecs/wm8994.c | 1 + sound/soc/codecs/wm9081.c | 1 + sound/soc/codecs/wm9705.c | 1 + sound/soc/codecs/wm9712.c | 1 + sound/soc/codecs/wm9713.c | 1 + sound/soc/davinci/davinci-i2s.c | 1 + sound/soc/davinci/davinci-mcasp.c | 1 + sound/soc/fsl/fsl_dma.c | 1 + sound/soc/fsl/fsl_ssi.c | 1 + sound/soc/fsl/mpc5200_dma.c | 1 + sound/soc/fsl/mpc8610_hpcd.c | 1 + sound/soc/fsl/soc-of-simple.c | 1 + sound/soc/imx/imx-pcm-dma-mx2.c | 1 + sound/soc/imx/imx-pcm-fiq.c | 1 + sound/soc/imx/imx-ssi.c | 1 + sound/soc/omap/mcpdm.c | 1 + sound/soc/omap/omap-pcm.c | 1 + sound/soc/pxa/pxa-ssp.c | 1 + sound/soc/s6000/s6000-i2s.c | 1 + sound/soc/sh/dma-sh7760.c | 1 + sound/soc/sh/fsi.c | 1 + sound/soc/sh/siu_dai.c | 1 + sound/soc/sh/siu_pcm.c | 1 - sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 1 + sound/soc/txx9/txx9aclc-ac97.c | 1 + sound/soc/txx9/txx9aclc.c | 1 + sound/sound_firmware.c | 1 - sound/sparc/cs4231.c | 1 - sound/sparc/dbri.c | 1 + sound/synth/emux/emux_proc.c | 1 - sound/usb/caiaq/audio.c | 1 + sound/usb/caiaq/device.c | 1 + sound/usb/caiaq/midi.c | 1 + sound/usb/usx2y/us122l.c | 1 + sound/usb/usx2y/usX2Yhwdep.c | 1 + sound/usb/usx2y/usb_stream.c | 1 + sound/usb/usx2y/usbusx2y.c | 1 + sound/usb/usx2y/usbusx2yaudio.c | 1 + sound/usb/usx2y/usx2yhwdeppcm.c | 1 + 210 files changed, 176 insertions(+), 56 deletions(-) (limited to 'sound') diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 84bb07d..91852e4 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -33,6 +33,7 @@ */ #include #include +#include MODULE_AUTHOR("Johannes Berg "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index 1dd66dd..fd2188c 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -66,6 +66,7 @@ #include #include #include +#include MODULE_AUTHOR("Johannes Berg "); MODULE_LICENSE("GPL"); diff --git a/sound/aoa/codecs/toonie.c b/sound/aoa/codecs/toonie.c index f13827e..69d2cb6 100644 --- a/sound/aoa/codecs/toonie.c +++ b/sound/aoa/codecs/toonie.c @@ -11,6 +11,7 @@ */ #include #include +#include MODULE_AUTHOR("Johannes Berg "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("toonie codec driver for snd-aoa"); diff --git a/sound/aoa/core/gpio-pmf.c b/sound/aoa/core/gpio-pmf.c index 1dd0c28..6776d1c 100644 --- a/sound/aoa/core/gpio-pmf.c +++ b/sound/aoa/core/gpio-pmf.c @@ -6,6 +6,7 @@ * GPL v2, can be found in COPYING. */ +#include #include #include #include "../aoa.h" diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 7a437da..1cd9b30 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../aoa.h" #include "../soundbus/soundbus.h" diff --git a/sound/aoa/soundbus/i2sbus/control.c b/sound/aoa/soundbus/i2sbus/control.c index 87beb4a..47f854c 100644 --- a/sound/aoa/soundbus/i2sbus/control.c +++ b/sound/aoa/soundbus/i2sbus/control.c @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 4e3b819..9d6f3b1 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index 59bacd3..be83899 100644 --- a/sound/aoa/soundbus/i2sbus/pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index fd51fa8..8808b82 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -4,6 +4,7 @@ * published by the Free Software Foundation. */ +#include #include #include diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 368dc9c..4268744 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -21,6 +21,7 @@ /* this file included from control.c */ #include +#include struct snd_ctl_elem_list32 { u32 offset; diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 7f4d744..7730575 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include diff --git a/sound/core/info.c b/sound/core/info.c index d749a0d..cc4a53d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/core/jack.c b/sound/core/jack.c index f705eec..14b8a4e 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -20,6 +20,7 @@ */ #include +#include #include #include diff --git a/sound/core/misc.c b/sound/core/misc.c index 3da4f92..2c41825 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index 0dcc287..bbe25d8 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -19,7 +19,6 @@ * */ -#include #include #include #include diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 08bfed5..5fb2e28 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -21,6 +21,7 @@ /* This file included from pcm_native.c */ #include +#include static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream, s32 __user *src) diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index d6d49d6..917e405 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index d0d721c..6857122 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -29,6 +29,7 @@ #include "seq_oss_event.h" #include #include +#include /* * common variables diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 9dfb2f7..677dc84 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -28,6 +28,7 @@ #include #include "../seq_lock.h" #include +#include /* diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index f5de79f..73661c4 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -25,6 +25,7 @@ #include #include "../seq_lock.h" #include +#include /* * constants diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 945a27c..ee44ab9 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -24,6 +24,7 @@ #include "seq_oss_midi.h" #include "../seq_lock.h" #include +#include /* * constants diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c index c440fda..ab59cbf 100644 --- a/sound/core/seq/oss/seq_oss_timer.c +++ b/sound/core/seq/oss/seq_oss_timer.c @@ -23,6 +23,7 @@ #include "seq_oss_timer.h" #include "seq_oss_event.h" #include +#include /* */ diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c index 2174248..d50338b 100644 --- a/sound/core/seq/oss/seq_oss_writeq.c +++ b/sound/core/seq/oss/seq_oss_writeq.c @@ -27,6 +27,7 @@ #include "../seq_lock.h" #include "../seq_clientmgr.h" #include +#include /* diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index c956fe4..81f7c10 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -21,6 +21,7 @@ /* This file included from seq.c */ #include +#include struct snd_seq_port_info32 { struct snd_seq_addr addr; /* client/port numbers */ diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 77884e6..c38b90c 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -20,6 +20,7 @@ */ #include +#include #include #include "seq_system.h" #include "seq_timer.h" diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index 1950ffc..a1282c1 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -39,6 +39,7 @@ #include #include +#include #include #include diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 2f8f295..da03597 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 9284829..8539ab0 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index a54b1dc..ade3ca5 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -19,7 +19,6 @@ */ #include "opl3_voice.h" -#include static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure); static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg); diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index 6d57b64..301acb6 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c @@ -19,6 +19,7 @@ * */ +#include #include #include diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 01997f2..f07e38d 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -20,6 +20,7 @@ #include "opl4_local.h" #include #include +#include #include #include diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index e1145ac..d77ffa9 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 60158e2..f2b0ba2 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c index 46df881..f7a6fbd 100644 --- a/sound/drivers/vx/vx_hwdep.c +++ b/sound/drivers/vx/vx_hwdep.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index c4c6ef7..ee538f1 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 8246aae..fe79a16 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index cc15d1d..999dc1e 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 9a43baa..fb4d6b3 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -80,7 +80,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 534a6ec..c7b80e4 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c index 4be562b..7874956 100644 --- a/sound/isa/msnd/msnd_midi.c +++ b/sound/isa/msnd/msnd_midi.c @@ -25,6 +25,7 @@ */ #include +#include #include #include #include diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 0481a55..265abcc 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index 5913717..8c24102 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 4d2d040..c35dc68 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index 91dc3d8..ccedbfe 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -20,6 +20,7 @@ #include "emu8000_local.h" #include +#include #include #include diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 519c363..4d1c5a3 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c index 3cd57ee..81284a8 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index a34ae7b..711670e 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index 2bb1cee..657e2d6 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 5d4ff48..4fb7b19 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index 9a88cdf..453d343 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index 6aff217..717604c 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -25,11 +25,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index d12bd98..24793c5 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index 1bfcf7e..bcc3e8e 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -26,6 +26,7 @@ #define SAMPLE_ROUNDUP 0 #include +#include #include "sound_config.h" #define DMAP_FREE_ON_CLOSE 0 diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c index 24d152c..52d06a3 100644 --- a/sound/oss/kahlua.c +++ b/sound/oss/kahlua.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "sound_config.h" diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c index 0af9d24..25e4609 100644 --- a/sound/oss/mpu401.c +++ b/sound/oss/mpu401.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c index 21eb6dc..c0cc951 100644 --- a/sound/oss/msnd.c +++ b/sound/oss/msnd.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c index bf27e00..a1e3f96 100644 --- a/sound/oss/msnd_pinnacle.c +++ b/sound/oss/msnd_pinnacle.c @@ -35,12 +35,12 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include "sound_config.h" diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 7781c13..938c48c 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -24,6 +24,7 @@ */ #include +#include #include #include diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c index 7de18b5..84ef4d0 100644 --- a/sound/oss/sb_card.c +++ b/sound/oss/sb_card.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "sound_config.h" #include "sb_mixer.h" diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c index ce4db49..7d42c54 100644 --- a/sound/oss/sb_common.c +++ b/sound/oss/sb_common.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "sound_config.h" #include "sound_firmware.h" diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c index 8b79670..f139028 100644 --- a/sound/oss/sb_midi.c +++ b/sound/oss/sb_midi.c @@ -12,6 +12,7 @@ */ #include +#include #include "sound_config.h" diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c index fad1a4f..2039d31 100644 --- a/sound/oss/sb_mixer.c +++ b/sound/oss/sb_mixer.c @@ -16,6 +16,8 @@ * Stanislav Voronyi : Support for AWE 3DSE device (Jun 7 1999) */ +#include + #include "sound_config.h" #define __SB_MIXER_C__ diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index fde7c12..2d9c513 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c index a446b82..8e514a6 100644 --- a/sound/oss/uart401.c +++ b/sound/oss/uart401.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "sound_config.h" diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c index 103940f..f0b4151 100644 --- a/sound/oss/v_midi.c +++ b/sound/oss/v_midi.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "sound_config.h" diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c index a4127ba..ac39a53 100644 --- a/sound/oss/vidc.c +++ b/sound/oss/vidc.c @@ -17,6 +17,7 @@ * We currently support a mixer device, but it is currently non-functional. */ +#include #include #include #include diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c index 6713110..20b3b32 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -149,6 +149,7 @@ #include #include #include +#include #include diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c index 2c63bb9..e688dde 100644 --- a/sound/oss/waveartist.c +++ b/sound/oss/waveartist.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 73b17d5..6320bf0 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -22,7 +22,6 @@ * */ -#include #include #include diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index d75cf7b..6cf1de8 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -68,7 +68,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c index 296123a..8afd8b5 100644 --- a/sound/pci/aw2/aw2-saa7146.c +++ b/sound/pci/aw2/aw2-saa7146.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 8f443a9..85fd315 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 0470461..ba96428 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 207479a..bc07e27 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 0f48a87..f16bc8a 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -23,7 +23,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 564c33b..a3301cc 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 480cb1e..1bff80c 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -24,6 +24,7 @@ #include "ctdaio.h" #include "cttimer.h" #include +#include #include #include #include diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index d0dc227..85ab43e 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -17,6 +17,7 @@ #include "ctpcm.h" #include "cttimer.h" +#include #include /* Hardware descriptions for playback */ diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index a65bafe..fe7ad64 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -40,9 +40,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index 0a6c50b..d1fd34b 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -44,9 +44,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index f514279..1dffdc5 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -51,9 +51,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index 2364f8a..050e54a 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -44,9 +44,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index 616b558..5748fc6 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -50,9 +50,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 776175c..4ae5e35 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 8816b0b..3550715 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c index b1e3652..19b191f 100644 --- a/sound/pci/echoaudio/indigodjx.c +++ b/sound/pci/echoaudio/indigodjx.c @@ -42,10 +42,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index 1035125..a9fcedf 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -43,9 +43,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c index 60b7cb2..bcdfac6 100644 --- a/sound/pci/echoaudio/indigoiox.c +++ b/sound/pci/echoaudio/indigoiox.c @@ -43,10 +43,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 8c3f5c5..d3a98c5 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -49,9 +49,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index ed1cc0a..2a1dca6d 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -51,9 +51,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index cc2bbfc..9cdf14c 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -50,9 +50,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 3e7e0182..1047be4 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -48,9 +48,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 6a47672..ffb1ddb 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -22,6 +22,7 @@ */ #include +#include #include #include diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e4581a42..29714c8 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include "hda_beep.h" diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index dcd2244..d8da18a 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include "hda_codec.h" diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c index 03391da..90d560c 100644 --- a/sound/pci/ice1712/ak4xxx.c +++ b/sound/pci/ice1712/ak4xxx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 6da21a2..e328cfb 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 7f9674b..4c551e1 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index 5af9e84..e618f78 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 0cca560..ef9af3f 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7e8e7da..55e9315 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 4cf4cd8..bf2696a 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "mixart.h" diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 9c5e645..fad03d6 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index d5e1c6e..3c04524 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -70,10 +70,10 @@ #include +#include #include #include #include -#include #include #include diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 9d5252b..d19dc05 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 52c6eb5..b92adef 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 44a3e2d..c492af5 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 7e3e8fb..9cc1b5a 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c index 5d2afa0..9dce0bd 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include "pdaudiocf.h" diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c index 0d668f4..43f995a 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 7be3b33..cfd1438 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "vxpocket.h" #include diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 1f72e1c..00e2d51 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include "pmac.h" diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index d06f780..8f064c7 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "pmac.h" diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 53c81a5..2f12da4 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -20,10 +20,10 @@ #include #include +#include #include #include #include -#include #include #include diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 76d9ad2..68e0dee 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 340311d..a61ccd2 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index 0cf2ca6..495be6e 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 67cbfe7..5e7aacf 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index e693229..523b7fc 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index c6c6a4a7..1d2a1ad 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index 5e03bb2..6bac1ac 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index a1bbe16..fd101d4 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 3c80137..11b62de 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c index c233810..240cd15 100644 --- a/sound/soc/codecs/ad1938.c +++ b/sound/soc/codecs/ad1938.c @@ -27,6 +27,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 39c0f758..0420727 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index d2fcc60..475807b 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index cc96411..f8e75ed 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index b68d99f..bdeb10d 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index ff96656..352d1d0 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 3ef16bb..729859c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 82fca28..926797a 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index dfbeb2d..81a62d1 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index e000cdf..9f169c4 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -14,6 +14,7 @@ */ #include +#include #include #include diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index cf2975a..366daf1 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 2afcd0a..5a5f187 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index d2ff1cd..29d0906 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 81b8c9d..3293629 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index da589d8..776b79c 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 357b609..b5b7d6a 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index e4b946a..4a6d56c 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index d50f169..d1e0e81 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 958d49c..569ad87 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 6f5d4af..520ffd6 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 3e99fe5..a8dcd5a 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 217b026..a34cbcf 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index df2c6d9..2e0772f 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b432f4d..6acc885 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index af8cb69..9000b1d 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index d3a61d7..19cd472 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index d077df6..8cc9042 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 24a3560..8ca3812 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index 63a254e..1072621 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 3fb653b..07adc37 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 5a2619d..e7c6bf1 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 475c67a..2916ed4 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index c2444e7..613199a 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 44e7d9d..60b1b3e 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index dbc368c..b7fd96a 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 3595bd5..fa5f99f 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 593e47d..c6f0abc 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 31e39ff..0c04b47 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 615dab2..c8d7a80 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index d07bcc1..f1e63e0 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index d2342c5..50634ab7 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index d9540d5..a65b781 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index ee637af..69708c4 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 28bb59e..526f56b 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 2862e4d..bb18c3e 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 056b787..831f473 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index bf022f6..03e8b1a 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 29f3771..8d1c637 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index c468497..3a184fc 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index ec54c6d..8793341 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index e237bf6..2f48a8a 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ceb86b4..2fca514 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 6362ca0..62af7e0 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index ab6518d..6c80cc3 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index b1a3a27..410c749 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 93f0f38..762c1b8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 30ed568..d639e55 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -8,6 +8,7 @@ #include #include +#include #include diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index ef67d1c..83de1c8 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -9,6 +9,7 @@ * express or implied. */ +#include #include #include #include diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c index 8bc5cd9..3bc13fd 100644 --- a/sound/soc/fsl/soc-of-simple.c +++ b/sound/soc/fsl/soc-of-simple.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 19452e4..86668ab 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c index d9cb984..f96a373 100644 --- a/sound/soc/imx/imx-pcm-fiq.c +++ b/sound/soc/imx/imx-pcm-fiq.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 56f46a7..6546b06 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c index ad8df6c..1dab4c1 100644 --- a/sound/soc/omap/mcpdm.c +++ b/sound/soc/omap/mcpdm.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 825db38..ba8acbb 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 9e95e51..d5fc52d 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c index c5cda18..0664fac 100644 --- a/sound/soc/s6000/s6000-i2s.c +++ b/sound/soc/s6000/s6000-i2s.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index baddb12..0d8bdf0 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 993abb7..8dc966f 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index 5452d19..d86ee1b 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index ba7f8d0..8f85719 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c8b0556..2320153 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6c33510..7c28f40 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 0f83bdb..612e18b 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index efed64b..49cc7ea 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c index 96deaef..340a0bc 100644 --- a/sound/sound_firmware.c +++ b/sound/sound_firmware.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include "oss/sound_firmware.h" diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 8d13d93..7dcc0651 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 1d2e51b..2eab6ce 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 687e6a1..58a32a1 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 86b2c3b..4328cad 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index a3f02dd..afc5aeb 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c index 538e8c0..2f218c7 100644 --- a/sound/usb/caiaq/midi.c +++ b/sound/usb/caiaq/midi.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 44deb21..9ca9a13 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -16,6 +16,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index 1879b72..04aafb4 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index 12ae034..c400ade 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -17,6 +17,7 @@ */ #include +#include #include "usb_stream.h" diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index c42350e..cbd37f2 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -133,6 +133,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 74a67a8..5d37d1c 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -32,6 +32,7 @@ #include +#include #include #include #include diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index 9ed6c39..2a528e5 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -51,6 +51,7 @@ */ #include +#include #include "usbusx2yaudio.c" #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1) -- cgit v1.1 From b8e80cf386419453678b01bef830f53445ebb15d Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Tue, 30 Mar 2010 13:29:28 -0400 Subject: ALSA: hda: Fix 0 dB offset for Lenovo Thinkpad models using AD1981 BugLink: https://launchpad.net/bugs/551606 The OR's hardware distorts at PCM 100% because it does not correspond to 0 dB. Fix this in patch_ad1981() for all models using the Thinkpad quirk. Reported-by: Jane Silber Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index e6d1bdf..af34606 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1896,6 +1896,14 @@ static int patch_ad1981(struct hda_codec *codec) case AD1981_THINKPAD: spec->mixers[0] = ad1981_thinkpad_mixers; spec->input_mux = &ad1981_thinkpad_capture_source; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1981_TOSHIBA: spec->mixers[0] = ad1981_hp_mixers; -- cgit v1.1 From b5442a75deee293d10c2ab8f4a77013973c4c9e0 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 28 Mar 2010 22:29:29 +0200 Subject: ASoC: OMAP: Fix capture pointer handling for OMAP1510 to work correctly with recent ALSA PCM code With recent (2.6.34) chnages in PCM handling, capture stopped working on my OMAP1510 based Amstrad Delta videophone. Using 2.6.34-rc2, I was able to correct the problem in 3 different ways: 1. reverting commit 7b3a177b0d4f92b3431b8dca777313a07533a710, 2. enabling additional jiffies check with echo 4 >/proc/asound/card0/pcm0c0/xrun_debug 3. applying the patch below. Since I wasn't able to reproduce the problem on my i686 PC, I guess the problem is probably machine specific. The patch reuses the method for software emulation of missing hardware pointer, already implemented for playback on OMAP1510. It's possible that event if a hardware pointer is available for capture on this machine, its behaviour may be not compatible with what upper layer expects. If you think the problem may be more general and should be solved differently, on a higher level, I can try to work more on it if you give me a hint. If the patch gets accepted, I suggest it goes as a fix in the current release cycle. Created and tested against linux-2.6.34-rc2. Signed-off-by: Janusz Krzysztofik Acked-by: Jarkko Nikula Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/omap/omap-pcm.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 825db38..bdd1097 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -60,12 +60,11 @@ static void omap_pcm_dma_irq(int ch, u16 stat, void *data) struct omap_runtime_data *prtd = runtime->private_data; unsigned long flags; - if ((cpu_is_omap1510()) && - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) { + if ((cpu_is_omap1510())) { /* * OMAP1510 doesn't fully support DMA progress counter * and there is no software emulation implemented yet, - * so have to maintain our own playback progress counter + * so have to maintain our own progress counters * that can be used by omap_pcm_pointer() instead. */ spin_lock_irqsave(&prtd->lock, flags); @@ -189,8 +188,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) dma_params.frame_count = runtime->periods; omap_set_dma_params(prtd->dma_ch, &dma_params); - if ((cpu_is_omap1510()) && - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + if ((cpu_is_omap1510())) omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ); else @@ -248,14 +246,15 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) dma_addr_t ptr; snd_pcm_uframes_t offset; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (cpu_is_omap1510()) { + offset = prtd->period_index * runtime->period_size; + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ptr = omap_get_dma_dst_pos(prtd->dma_ch); offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); - } else if (!(cpu_is_omap1510())) { + } else { ptr = omap_get_dma_src_pos(prtd->dma_ch); offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); - } else - offset = prtd->period_index * runtime->period_size; + } if (offset >= runtime->buffer_size) offset = 0; -- cgit v1.1 From 3815595e78d2baae6feb866e737f92d8ef48b337 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 4 Apr 2010 12:14:03 +0200 Subject: ALSA: hda - Add MSI blacklist for Aopen MZ915-M The device needs MSI disablement. Added to the quirk list. Reported-by: Harald Dunkel Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4bb9067..f8fd586 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2362,6 +2362,7 @@ static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ + SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ {} }; -- cgit v1.1 From 3fa49e3ad9ac20b15edfb0c51bbad36e45a84b17 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Mar 2010 15:24:40 +0100 Subject: ASoC: Avoid wraparound in wm_hubs DC servo correction If the correction wraps around then a substantial offset would be introduced. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm_hubs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 486bdd2..3729a12 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -113,13 +113,15 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) /* HPOUT1L */ reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) & WM8993_DCS_INTEG_CHAN_0_MASK;; - reg += hubs->dcs_codes; + if (reg + hubs->dcs_codes > 0 && reg + hubs->dcs_codes < 0xff) + reg += hubs->dcs_codes; dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT; /* HPOUT1R */ reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) & WM8993_DCS_INTEG_CHAN_1_MASK; - reg += hubs->dcs_codes; + if (reg + hubs->dcs_codes > 0 && reg + hubs->dcs_codes < 0xff) + reg += hubs->dcs_codes; dcs_cfg |= reg; /* Do it */ -- cgit v1.1 From 8437f7006b9cfa249791e2fd57596683d4561843 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Mar 2010 17:09:45 +0100 Subject: ASoC: Support second DC servo readback method for wm_hubs More recent Wolfson hubs devices add the ability to read back the DC servo calibration information from the register used to write offsets, and later still ones remove the old readback registers. Add support for the new scheme, and use it for WM8994 device revisions that support it. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm8994.c | 3 ++- sound/soc/codecs/wm_hubs.c | 41 ++++++++++++++++++++++++++++++----------- sound/soc/codecs/wm_hubs.h | 1 + 3 files changed, 33 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index d10d651..c80218f 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3730,11 +3730,12 @@ static int wm8994_codec_probe(struct platform_device *pdev) case 3: wm8994->hubs.dcs_codes = -5; wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.dcs_readback_mode = 1; break; default: + wm8994->hubs.dcs_readback_mode = 1; break; } - /* Remember if AIFnLRCLK is configured as a GPIO. This should be * configured on init - if a system wants to do this dynamically diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 3729a12..2b5c092 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -86,7 +86,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) static void calibrate_dc_servo(struct snd_soc_codec *codec) { struct wm_hubs_data *hubs = codec->private_data; - u16 reg, dcs_cfg; + u16 reg, reg_l, reg_r, dcs_cfg; /* Set for 32 series updates */ snd_soc_update_bits(codec, WM8993_DC_SERVO_1, @@ -110,19 +110,38 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Applying %d code DC servo correction\n", hubs->dcs_codes); + /* Different chips in the family support different + * readback methods. + */ + switch (hubs->dcs_readback_mode) { + case 0: + reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) + & WM8993_DCS_INTEG_CHAN_0_MASK;; + reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) + & WM8993_DCS_INTEG_CHAN_1_MASK; + break; + case 1: + reg = snd_soc_read(codec, WM8993_DC_SERVO_3); + reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) + >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; + reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; + break; + default: + WARN(1, "Unknown DCS readback method"); + break; + } + /* HPOUT1L */ - reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) & - WM8993_DCS_INTEG_CHAN_0_MASK;; - if (reg + hubs->dcs_codes > 0 && reg + hubs->dcs_codes < 0xff) - reg += hubs->dcs_codes; - dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + if (reg_l + hubs->dcs_codes > 0 && + reg_l + hubs->dcs_codes < 0xff) + reg_l += hubs->dcs_codes; + dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; /* HPOUT1R */ - reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) & - WM8993_DCS_INTEG_CHAN_1_MASK; - if (reg + hubs->dcs_codes > 0 && reg + hubs->dcs_codes < 0xff) - reg += hubs->dcs_codes; - dcs_cfg |= reg; + if (reg_r + hubs->dcs_codes > 0 && + reg_r + hubs->dcs_codes < 0xff) + reg_r += hubs->dcs_codes; + dcs_cfg |= reg_r; /* Do it */ snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 420104f..e51c166 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -21,6 +21,7 @@ extern const unsigned int wm_hubs_spkmix_tlv[]; /* This *must* be the first element of the codec->private_data struct */ struct wm_hubs_data { int dcs_codes; + int dcs_readback_mode; int hp_startup_mode; }; -- cgit v1.1 From ae9d8607fe24253efc9f14b696f51cfd683801be Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Mar 2010 16:34:42 +0100 Subject: ASoC: Don't do runtime wm_hubs DC servo updates if using offset correction If we need to offset correct the DC servo then don't use runtime recalibration since that is likely to introduce further offsets which will be evident on powerdown. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm_hubs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2b5c092..e81ba6d 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -162,10 +162,16 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm_hubs_data *hubs = codec->private_data; int ret; ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + /* If we're applying an offset correction then updating the + * callibration would be likely to introduce further offsets. */ + if (hubs->dcs_codes) + return ret; + /* Only need to do this if the outputs are active */ if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1) & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA)) -- cgit v1.1 From 4dcc93d0ede49fae32dd0ee41c685db1be14c529 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Mar 2010 17:18:41 +0100 Subject: ASoC: Don't use DCS_DATAPATH_BUSY for WM hubs devices The DCS_DATAPATH_BUSY bit used to monitor the completion of DC servo operations has been deprecated and with some more recente revisions may perform incorrectly, especially when only analogue bypass paths are in use. Switch to using readback from the DC servo command register instead, which is supported for all devices. Without this unacceptably long timeouts may be observed in some circumstances. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm_hubs.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index e81ba6d..e1f225a 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -62,21 +62,27 @@ static const char *speaker_mode_text[] = { static const struct soc_enum speaker_mode = SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text); -static void wait_for_dc_servo(struct snd_soc_codec *codec) +static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) { unsigned int reg; int count = 0; + unsigned int val; + + val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1; + + /* Trigger the command */ + snd_soc_write(codec, WM8993_DC_SERVO_0, val); dev_dbg(codec->dev, "Waiting for DC servo...\n"); do { count++; msleep(1); - reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); + reg = snd_soc_read(codec, WM8993_DC_SERVO_0); dev_dbg(codec->dev, "DC servo: %x\n", reg); - } while (reg & WM8993_DCS_DATAPATH_BUSY && count < 400); + } while (reg & op && count < 400); - if (reg & WM8993_DCS_DATAPATH_BUSY) + if (reg & op) dev_err(codec->dev, "Timed out waiting for DC Servo\n"); } @@ -92,18 +98,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8993_DC_SERVO_1, WM8993_DCS_SERIES_NO_01_MASK, 32 << WM8993_DCS_SERIES_NO_01_SHIFT); - - /* Enable the DC servo. Write all bits to avoid triggering startup - * or write calibration. - */ - snd_soc_update_bits(codec, WM8993_DC_SERVO_0, - 0xFFFF, - WM8993_DCS_ENA_CHAN_0 | - WM8993_DCS_ENA_CHAN_1 | - WM8993_DCS_TRIG_SERIES_1 | - WM8993_DCS_TRIG_SERIES_0); - - wait_for_dc_servo(codec); + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); /* Apply correction to DC servo result */ if (hubs->dcs_codes) { @@ -145,13 +141,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) /* Do it */ snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); - snd_soc_update_bits(codec, WM8993_DC_SERVO_0, - WM8993_DCS_TRIG_DAC_WR_0 | - WM8993_DCS_TRIG_DAC_WR_1, - WM8993_DCS_TRIG_DAC_WR_0 | - WM8993_DCS_TRIG_DAC_WR_1); - - wait_for_dc_servo(codec); + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_DAC_WR_0 | + WM8993_DCS_TRIG_DAC_WR_1); } } -- cgit v1.1 From d522ffbfb9fccf6eca283cd2e8b03cf3d21fb616 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 30 Mar 2010 14:29:14 +0100 Subject: ASoC: Only do WM8994 bias off transition from standby Otherwise we may try to power down multiple times when the using idle bias off and the driver is removed. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- sound/soc/codecs/wm8994.c | 53 ++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index c80218f..f8355ac 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3007,34 +3007,39 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: - /* Switch over to startup biases */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM8994_BIAS_SRC | WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - WM8994_VMID_RAMP_MASK, - WM8994_BIAS_SRC | WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - (1 << WM8994_VMID_RAMP_SHIFT)); - - /* Disable main biases */ - snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, - WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, 0); + if (codec->bias_level == SND_SOC_BIAS_STANDBY) { + /* Switch over to startup biases */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + WM8994_VMID_RAMP_MASK, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + (1 << WM8994_VMID_RAMP_SHIFT)); - /* Discharge line */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_1, - WM8994_LINEOUT1_DISCH | - WM8994_LINEOUT2_DISCH, - WM8994_LINEOUT1_DISCH | - WM8994_LINEOUT2_DISCH); + /* Disable main biases */ + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, + WM8994_BIAS_ENA | + WM8994_VMID_SEL_MASK, 0); - msleep(5); + /* Discharge line */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH); - /* Switch off startup biases */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM8994_BIAS_SRC | WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - WM8994_VMID_RAMP_MASK, 0); + msleep(5); + /* Switch off startup biases */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + WM8994_VMID_RAMP_MASK, 0); + } break; } codec->bias_level = level; -- cgit v1.1 From d12841827a6de120199609dadb6ff4ec99bd90ea Mon Sep 17 00:00:00 2001 From: Tony Vroon Date: Mon, 5 Apr 2010 16:30:43 +0100 Subject: ALSA: hda - Enable amplifiers on Acer Inspire 6530G After more tests it appears that EAPD needs to be enabled on both the 0x14 and 0x15 NIDs to enable the main speaker and headphone amplifiers. The maximum volume setting is now equal to what the machine achieves under other operating systems. Disabling Front or LFE playback triggers EAPD and disables the amplifier. As such, these two playback switches have been removed from the mixer. Signed-off-by: Tony Vroon Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ca93c4c..5472062 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1621,6 +1621,11 @@ static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { */ static struct hda_verb alc888_acer_aspire_6530g_verbs[] = { +/* Route to built-in subwoofer as well as speakers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* Bias voltage on for external mic port */ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, /* Front Mic: set to PIN_IN (empty by default) */ @@ -1632,10 +1637,12 @@ static struct hda_verb alc888_acer_aspire_6530g_verbs[] = { /* Enable speaker output */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, /* Enable headphone output */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, { } }; @@ -8462,9 +8469,7 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LFE Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), -- cgit v1.1 From 5f712b2b73a9fc87fcc52124cfe8adefaa0c92f5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 22 Mar 2010 10:11:15 +0100 Subject: ALSA: ASoC: move dma_data from snd_soc_dai to snd_soc_pcm_stream This fixes a memory corruption when ASoC devices are used in full-duplex mode. Specifically for pxa-ssp code, where this pointer is dynamically allocated for each direction and destroyed upon each stream start. All other platforms are fixed blindly, I couldn't even compile-test them. Sorry for any breakage I may have caused. [Note that this is a backported version for 2.6.34. Upstream commit is fd23b7dee] Signed-off-by: Daniel Mack Reported-by: Sven Neumann Reported-by: Michael Hirsch Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/atmel/atmel-pcm.c | 2 +- sound/soc/atmel/atmel_ssc_dai.c | 6 +++--- sound/soc/davinci/davinci-i2s.c | 3 ++- sound/soc/davinci/davinci-mcasp.c | 3 ++- sound/soc/davinci/davinci-pcm.c | 4 +++- sound/soc/imx/imx-pcm-dma-mx2.c | 8 ++++++-- sound/soc/imx/imx-ssi.c | 7 +++++-- sound/soc/omap/omap-mcbsp.c | 4 +++- sound/soc/omap/omap-mcpdm.c | 3 ++- sound/soc/omap/omap-pcm.c | 4 +++- sound/soc/pxa/pxa-ssp.c | 23 +++++++++++----------- sound/soc/pxa/pxa2xx-ac97.c | 17 ++++++++++++----- sound/soc/pxa/pxa2xx-i2s.c | 7 +++++-- sound/soc/pxa/pxa2xx-pcm.c | 4 +++- sound/soc/s3c24xx/s3c-ac97.c | 21 +++++++++++--------- sound/soc/s3c24xx/s3c-dma.c | 4 +++- sound/soc/s3c24xx/s3c-i2s-v2.c | 13 ++++++++----- sound/soc/s3c24xx/s3c-pcm.c | 7 +++++-- sound/soc/s3c24xx/s3c24xx-i2s.c | 19 ++++++++++--------- sound/soc/s6000/s6000-i2s.c | 3 ++- sound/soc/s6000/s6000-pcm.c | 40 ++++++++++++++++++++++++++++----------- 21 files changed, 131 insertions(+), 71 deletions(-) (limited to 'sound') diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 9ef6b96..3e6628c8 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -180,7 +180,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - prtd->params = rtd->dai->cpu_dai->dma_data; + prtd->params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); prtd->params->dma_intr_handler = atmel_pcm_dma_irq; prtd->dma_buffer = runtime->dma_addr; diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index e588e63..0b59806 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -363,12 +363,12 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, ssc_p->dma_params[dir] = dma_params; /* - * The cpu_dai->dma_data field is only used to communicate the - * appropriate DMA parameters to the pcm driver hw_params() + * The snd_soc_pcm_stream->dma_data field is only used to communicate + * the appropriate DMA parameters to the pcm driver hw_params() * function. It should not be used for other purposes * as it is common to all substreams. */ - rtd->dai->cpu_dai->dma_data = dma_params; + snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream, dma_params); channels = params_channels(params); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 6362ca0..4aad7ecc 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -585,7 +585,8 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; davinci_i2s_dai.private_data = dev; - davinci_i2s_dai.dma_data = dev->dma_params; + davinci_i2s_dai.capture.dma_data = dev->dma_params; + davinci_i2s_dai.playback.dma_data = dev->dma_params; ret = snd_soc_register_dai(&davinci_i2s_dai); if (ret != 0) goto err_free_mem; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index ab6518d..c056bfb 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -917,7 +917,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->channel = res->start; davinci_mcasp_dai[pdata->op_mode].private_data = dev; - davinci_mcasp_dai[pdata->op_mode].dma_data = dev->dma_params; + davinci_mcasp_dai[pdata->op_mode].capture.dma_data = dev->dma_params; + davinci_mcasp_dai[pdata->op_mode].playback.dma_data = dev->dma_params; davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev; ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]); diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 80c7fdf..2dc406f 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -649,8 +649,10 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_hardware *ppcm; int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data; + struct davinci_pcm_dma_params *pa; struct davinci_pcm_dma_params *params; + + pa = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); if (!pa) return -ENODEV; params = &pa[substream->stream]; diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 19452e4..c78c000 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -83,11 +83,13 @@ static void snd_imx_dma_err_callback(int channel, void *data, int err) static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; + struct imx_pcm_dma_params *dma_params; struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; int ret; + dma_params = snd_soc_get_dma_data(rtd->dai->cpu_dai, substream); + iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH); if (iprtd->dma < 0) { pr_err("Failed to claim the audio DMA\n"); @@ -192,10 +194,12 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; + struct imx_pcm_dma_params *dma_params; struct imx_pcm_runtime_data *iprtd = runtime->private_data; int err; + dma_params = snd_soc_get_dma_data(rtd->dai->cpu_dai, substream); + iprtd->substream = substream; iprtd->buf = (unsigned int *)substream->dma_buffer.area; iprtd->period_cnt = 0; diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 56f46a7..28e55c7 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -234,17 +234,20 @@ static int imx_ssi_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct imx_ssi *ssi = cpu_dai->private_data; + struct imx_pcm_dma_params *dma_data; u32 reg, sccr; /* Tx/Rx config */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { reg = SSI_STCCR; - cpu_dai->dma_data = &ssi->dma_params_tx; + dma_data = &ssi->dma_params_tx; } else { reg = SSI_SRCCR; - cpu_dai->dma_data = &ssi->dma_params_rx; + dma_data = &ssi->dma_params_rx; } + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; /* DAI data (word) size */ diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index e814a95..8ad9dc9 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -297,7 +297,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; omap_mcbsp_dai_dma_params[id][substream->stream].data_type = OMAP_DMA_DATA_TYPE_S16; - cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; + + snd_soc_dai_set_dma_data(cpu_dai, substream, + &omap_mcbsp_dai_dma_params[id][substream->stream]); if (mcbsp_data->configured) { /* McBSP already configured by another stream */ diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 25f19e4..b7f4f7e 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -150,7 +150,8 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, int stream = substream->stream; int channels, err, link_mask = 0; - cpu_dai->dma_data = &omap_mcpdm_dai_dma_params[stream]; + snd_soc_dai_set_dma_data(cpu_dai, substream, + &omap_mcpdm_dai_dma_params[stream]); channels = params_channels(params); switch (channels) { diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index bdd1097..3945644 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -99,9 +99,11 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct omap_runtime_data *prtd = runtime->private_data; - struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data; + struct omap_pcm_dma_data *dma_data; int err = 0; + dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); + /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ if (!dma_data) diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 9e95e51..6959c51 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -121,10 +121,9 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, ssp_disable(ssp); } - if (cpu_dai->dma_data) { - kfree(cpu_dai->dma_data); - cpu_dai->dma_data = NULL; - } + kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); + snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + return ret; } @@ -141,10 +140,8 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, clk_disable(ssp->clk); } - if (cpu_dai->dma_data) { - kfree(cpu_dai->dma_data); - cpu_dai->dma_data = NULL; - } + kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); + snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); } #ifdef CONFIG_PM @@ -569,19 +566,23 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf; + struct pxa2xx_pcm_dma_params *dma_data; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); /* generate correct DMA params */ - if (cpu_dai->dma_data) - kfree(cpu_dai->dma_data); + kfree(dma_data); /* Network mode with one active slot (ttsa == 1) can be used * to force 16-bit frame width on the wire (for S16_LE), even * with two channels. Use 16-bit DMA transfers for this case. */ - cpu_dai->dma_data = ssp_get_dma_params(ssp, + dma_data = ssp_get_dma_params(ssp, ((chn == 2) && (ttsa != 1)) || (width == 32), substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_dma_data(dai, substream, dma_data); + /* we can only change the settings if the port is not in use */ if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index e9ae7b3..d314115 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -122,11 +122,14 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct pxa2xx_pcm_dma_params *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; + dma_data = &pxa2xx_ac97_pcm_stereo_out; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; + dma_data = &pxa2xx_ac97_pcm_stereo_in; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); return 0; } @@ -137,11 +140,14 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct pxa2xx_pcm_dma_params *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; + dma_data = &pxa2xx_ac97_pcm_aux_mono_out; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; + dma_data = &pxa2xx_ac97_pcm_aux_mono_in; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); return 0; } @@ -156,7 +162,8 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; + snd_soc_dai_set_dma_data(cpu_dai, substream, + &pxa2xx_ac97_pcm_mic_mono_in); return 0; } diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 6b8f655..c1a52757 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -164,6 +164,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct pxa2xx_pcm_dma_params *dma_data; BUG_ON(IS_ERR(clk_i2s)); clk_enable(clk_i2s); @@ -171,9 +172,11 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, pxa_i2s_wait(); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; + dma_data = &pxa2xx_i2s_pcm_stereo_out; else - cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; + dma_data = &pxa2xx_i2s_pcm_stereo_in; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); /* is port used by another stream */ if (!(SACR0 & SACR0_ENB)) { diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index d38e395..adc7e6f 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -25,9 +25,11 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; + struct pxa2xx_pcm_dma_params *dma; int ret; + dma = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); + /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ if (!dma) diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c index ee8ed9d7..ecf4fd0 100644 --- a/sound/soc/s3c24xx/s3c-ac97.c +++ b/sound/soc/s3c24xx/s3c-ac97.c @@ -224,11 +224,14 @@ static int s3c_ac97_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct s3c_dma_params *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &s3c_ac97_pcm_out; + dma_data = &s3c_ac97_pcm_out; else - cpu_dai->dma_data = &s3c_ac97_pcm_in; + dma_data = &s3c_ac97_pcm_in; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); return 0; } @@ -238,8 +241,8 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->channel; + struct s3c_dma_params *dma_data = + snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -265,7 +268,7 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED); + s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); return 0; } @@ -280,7 +283,7 @@ static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; else - cpu_dai->dma_data = &s3c_ac97_mic_in; + snd_soc_dai_set_dma_data(cpu_dai, substream, &s3c_ac97_mic_in); return 0; } @@ -290,8 +293,8 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->channel; + struct s3c_dma_params *dma_data = + snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK; @@ -311,7 +314,7 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED); + s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); return 0; } diff --git a/sound/soc/s3c24xx/s3c-dma.c b/sound/soc/s3c24xx/s3c-dma.c index 7725e26..1b61c23 100644 --- a/sound/soc/s3c24xx/s3c-dma.c +++ b/sound/soc/s3c24xx/s3c-dma.c @@ -145,10 +145,12 @@ static int s3c_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data; unsigned long totbytes = params_buffer_bytes(params); + struct s3c_dma_params *dma = + snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); int ret = 0; + pr_debug("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g. diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index e994d83..8851594 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -339,14 +339,17 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai_link *dai = rtd->dai; struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai); + struct s3c_dma_params *dma_data; u32 iismod; pr_debug("Entered %s\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dai->cpu_dai->dma_data = i2s->dma_playback; + dma_data = i2s->dma_playback; else - dai->cpu_dai->dma_data = i2s->dma_capture; + dma_data = i2s->dma_capture; + + snd_soc_dai_set_dma_data(dai->cpu_dai, substream, dma_data); /* Working copies of register */ iismod = readl(i2s->regs + S3C2412_IISMOD); @@ -394,8 +397,8 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); unsigned long irqs; int ret = 0; - int channel = ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->channel; + struct s3c_dma_params *dma_data = + snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); pr_debug("Entered %s\n", __func__); @@ -431,7 +434,7 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, * of the auto reload mechanism of S3C24XX. * This call won't bother S3C64XX. */ - s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED); + s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); break; diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c index a98f40c..326f0a9 100644 --- a/sound/soc/s3c24xx/s3c-pcm.c +++ b/sound/soc/s3c24xx/s3c-pcm.c @@ -178,6 +178,7 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai_link *dai = rtd->dai; struct s3c_pcm_info *pcm = to_info(dai->cpu_dai); + struct s3c_dma_params *dma_data; void __iomem *regs = pcm->regs; struct clk *clk; int sclk_div, sync_div; @@ -187,9 +188,11 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, dev_dbg(pcm->dev, "Entered %s\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dai->cpu_dai->dma_data = pcm->dma_playback; + dma_data = pcm->dma_playback; else - dai->cpu_dai->dma_data = pcm->dma_capture; + dma_data = pcm->dma_capture; + + snd_soc_dai_set_dma_data(dai->cpu_dai, substream, dma_data); /* Strictly check for sample size */ switch (params_format(params)) { diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 0bc5950..c3ac890 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -242,14 +242,17 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c_dma_params *dma_data; u32 iismod; pr_debug("Entered %s\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; + dma_data = &s3c24xx_i2s_pcm_stereo_out; else - rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in; + dma_data = &s3c24xx_i2s_pcm_stereo_in; + + snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream, dma_data); /* Working copies of register */ iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); @@ -258,13 +261,11 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: iismod &= ~S3C2410_IISMOD_16BIT; - ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->dma_size = 1; + dma_data->dma_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: iismod |= S3C2410_IISMOD_16BIT; - ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->dma_size = 2; + dma_data->dma_size = 2; break; default: return -EINVAL; @@ -280,8 +281,8 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c_dma_params *) - rtd->dai->cpu_dai->dma_data)->channel; + struct s3c_dma_params *dma_data = + snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); pr_debug("Entered %s\n", __func__); @@ -300,7 +301,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, else s3c24xx_snd_txctrl(1); - s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED); + s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c index c5cda18..fa23854 100644 --- a/sound/soc/s6000/s6000-i2s.c +++ b/sound/soc/s6000/s6000-i2s.c @@ -518,7 +518,8 @@ static int __devinit s6000_i2s_probe(struct platform_device *pdev) s6000_i2s_dai.dev = &pdev->dev; s6000_i2s_dai.private_data = dev; - s6000_i2s_dai.dma_data = &dev->dma_params; + s6000_i2s_dai.capture.dma_data = &dev->dma_params; + s6000_i2s_dai.playback.dma_data = &dev->dma_params; dev->sifbase = sifmem->start; dev->scbbase = mmio; diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index 1d61109..9c7f7f0 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -58,13 +58,15 @@ static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct s6000_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; int channel; unsigned int period_size; unsigned int dma_offset; dma_addr_t dma_pos; dma_addr_t src, dst; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + period_size = snd_pcm_lib_period_bytes(substream); dma_offset = prtd->period * period_size; dma_pos = runtime->dma_addr + dma_offset; @@ -101,7 +103,8 @@ static irqreturn_t s6000_pcm_irq(int irq, void *data) { struct snd_pcm *pcm = data; struct snd_soc_pcm_runtime *runtime = pcm->private_data; - struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *params = + snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); struct s6000_runtime_data *prtd; unsigned int has_xrun; int i, ret = IRQ_NONE; @@ -172,11 +175,13 @@ static int s6000_pcm_start(struct snd_pcm_substream *substream) { struct s6000_runtime_data *prtd = substream->runtime->private_data; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; unsigned long flags; int srcinc; u32 dma; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + spin_lock_irqsave(&prtd->lock, flags); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -212,10 +217,12 @@ static int s6000_pcm_stop(struct snd_pcm_substream *substream) { struct s6000_runtime_data *prtd = substream->runtime->private_data; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; unsigned long flags; u32 channel; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) channel = par->dma_out; else @@ -236,9 +243,11 @@ static int s6000_pcm_stop(struct snd_pcm_substream *substream) static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; int ret; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + ret = par->trigger(substream, cmd, 0); if (ret < 0) return ret; @@ -275,13 +284,15 @@ static int s6000_pcm_prepare(struct snd_pcm_substream *substream) static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; struct snd_pcm_runtime *runtime = substream->runtime; struct s6000_runtime_data *prtd = runtime->private_data; unsigned long flags; unsigned int offset; dma_addr_t count; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + spin_lock_irqsave(&prtd->lock, flags); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -305,11 +316,12 @@ static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream) static int s6000_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; struct snd_pcm_runtime *runtime = substream->runtime; struct s6000_runtime_data *prtd; int ret; + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware); ret = snd_pcm_hw_constraint_step(runtime, 0, @@ -364,7 +376,7 @@ static int s6000_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par; int ret; ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); @@ -373,6 +385,8 @@ static int s6000_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } + par = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + if (par->same_rate) { spin_lock(&par->lock); if (par->rate == -1 || @@ -392,7 +406,8 @@ static int s6000_pcm_hw_params(struct snd_pcm_substream *substream, static int s6000_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *par = + snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); spin_lock(&par->lock); par->in_use &= ~(1 << substream->stream); @@ -417,7 +432,8 @@ static struct snd_pcm_ops s6000_pcm_ops = { static void s6000_pcm_free(struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *runtime = pcm->private_data; - struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *params = + snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); free_irq(params->irq, pcm); snd_pcm_lib_preallocate_free_for_all(pcm); @@ -429,9 +445,11 @@ static int s6000_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *runtime = pcm->private_data; - struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data; + struct s6000_pcm_dma_params *params; int res; + params = snd_soc_dai_get_dma_data(soc_runtime->dai->cpu_dai, substream); + if (!card->dev->dma_mask) card->dev->dma_mask = &s6000_pcm_dmamask; if (!card->dev->coherent_dma_mask) -- cgit v1.1 From f9700d5a4575e7fb343df10a1d29d425e4b81082 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 5 Apr 2010 23:25:13 +0200 Subject: ALSA: hda - Fix a wrong array range check in patch_realtek.c The commit 6a4f2ccb467e00281470cde2dee08fe5ecde62d1 introduced a wrong comparision for the array range check, which effectively skips the whole initialization of DAC connections. Fixed now. Reference: bko#15689 https://bugzilla.kernel.org/show_bug.cgi?id=15689 Reported-by: Adrian Ulrich Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5472062..c7730db 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10110,13 +10110,12 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, int idx; alc_set_pin_output(codec, nid, pin_type); + if (dac_idx >= spec->multiout.num_dacs) + return; if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; - else { - if (spec->multiout.num_dacs >= dac_idx) - return; + else idx = spec->multiout.dac_nids[dac_idx] - 2; - } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } -- cgit v1.1 From ca4c2adaf2d7efcc43e16c8010cf4c30def75058 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 16 Apr 2010 10:32:54 +0200 Subject: ALSA: usb/mixer - use get_iface_desc() rather than direct structure Signed-off-by: Jaroslav Kysela --- sound/usb/mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1deef62..21613fe 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2055,7 +2055,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, } host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; - mixer->protocol = host_iface->desc.bInterfaceProtocol; + mixer->protocol = get_iface_desc(host_iface)->bInterfaceProtocol; if ((err = snd_usb_mixer_controls(mixer)) < 0 || (err = snd_usb_mixer_status_create(mixer)) < 0) -- cgit v1.1