diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/Kconfig | 1 | ||||
-rw-r--r-- | sound/usb/usbaudio.c | 310 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 11 | ||||
-rw-r--r-- | sound/usb/usbmidi.c | 128 | ||||
-rw-r--r-- | sound/usb/usbmixer.c | 588 | ||||
-rw-r--r-- | sound/usb/usbmixer_maps.c | 126 | ||||
-rw-r--r-- | sound/usb/usbquirks.h | 298 | ||||
-rw-r--r-- | sound/usb/usx2y/usbusx2y.c | 13 | ||||
-rw-r--r-- | sound/usb/usx2y/usbusx2yaudio.c | 6 |
9 files changed, 1121 insertions, 360 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 9329e99..f05d02f 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -6,6 +6,7 @@ menu "USB devices" config SND_USB_AUDIO tristate "USB Audio/MIDI driver" depends on SND && USB + select SND_HWDEP select SND_RAWMIDI select SND_PCM help diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 84b0bbd..a756950 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -98,7 +98,7 @@ MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); #define MAX_PACKS 10 #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ #define MAX_URBS 5 /* max. 20ms long packets */ -#define SYNC_URBS 2 /* always two urbs for sync */ +#define SYNC_URBS 4 /* always four urbs for sync */ #define MIN_PACKS_URB 1 /* minimum 1 packet per urb */ typedef struct snd_usb_substream snd_usb_substream_t; @@ -177,7 +177,7 @@ struct snd_usb_substream { unsigned int nurbs; /* # urbs */ snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ - char syncbuf[SYNC_URBS * MAX_PACKS * 4]; /* sync buffer; it's so small - let's get static */ + char syncbuf[SYNC_URBS * 4]; /* sync buffer; it's so small - let's get static */ char *tmpbuf; /* temporary buffer for playback */ u64 formats; /* format bitmasks (all or'ed) */ @@ -251,17 +251,13 @@ static int prepare_capture_sync_urb(snd_usb_substream_t *subs, { unsigned char *cp = urb->transfer_buffer; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; - int i, offs; - urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) { - urb->iso_frame_desc[i].length = 3; - urb->iso_frame_desc[i].offset = offs; - cp[0] = subs->freqn >> 2; - cp[1] = subs->freqn >> 10; - cp[2] = subs->freqn >> 18; - } + 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; } @@ -277,18 +273,14 @@ static int prepare_capture_sync_urb_hs(snd_usb_substream_t *subs, { unsigned char *cp = urb->transfer_buffer; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; - int i, offs; - urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) { - urb->iso_frame_desc[i].length = 4; - urb->iso_frame_desc[i].offset = offs; - cp[0] = subs->freqn; - cp[1] = subs->freqn >> 8; - cp[2] = subs->freqn >> 16; - cp[3] = subs->freqn >> 24; - } + 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; } @@ -418,15 +410,11 @@ static int prepare_playback_sync_urb(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb) { - int i, offs; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; - urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) { - urb->iso_frame_desc[i].length = 3; - urb->iso_frame_desc[i].offset = offs; - } + urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].offset = 0; return 0; } @@ -440,15 +428,11 @@ static int prepare_playback_sync_urb_hs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb) { - int i, offs; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; - urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) { - urb->iso_frame_desc[i].length = 4; - urb->iso_frame_desc[i].offset = offs; - } + urb->iso_frame_desc[0].length = 4; + urb->iso_frame_desc[0].offset = 0; return 0; } @@ -462,31 +446,17 @@ static int retire_playback_sync_urb(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb) { - int i; - unsigned int f, found; - unsigned char *cp = urb->transfer_buffer; + unsigned int f; unsigned long flags; - found = 0; - for (i = 0; i < urb->number_of_packets; i++, cp += 4) { - if (urb->iso_frame_desc[i].status || - urb->iso_frame_desc[i].actual_length < 3) - continue; - f = combine_triple(cp) << 2; -#if 0 - if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) { - snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n", - f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1), - subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1)); - continue; + 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); } -#endif - found = f; - } - if (found) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = found; - spin_unlock_irqrestore(&subs->lock, flags); } return 0; @@ -502,22 +472,17 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb) { - int i; - unsigned int found; - unsigned char *cp = urb->transfer_buffer; + unsigned int f; unsigned long flags; - found = 0; - for (i = 0; i < urb->number_of_packets; i++, cp += 4) { - if (urb->iso_frame_desc[i].status || - urb->iso_frame_desc[i].actual_length < 4) - continue; - found = combine_quad(cp) & 0x0fffffff; - } - if (found) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = found; - spin_unlock_irqrestore(&subs->lock, 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; @@ -600,6 +565,8 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, /* set the buffer pointer */ urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride; subs->hwptr += offs; + if (subs->hwptr == runtime->buffer_size) + subs->hwptr = 0; } spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer_length = offs * stride; @@ -892,10 +859,8 @@ static void release_urb_ctx(snd_urb_ctx_t *u) usb_free_urb(u->urb); u->urb = NULL; } - if (u->buf) { - kfree(u->buf); - u->buf = NULL; - } + kfree(u->buf); + u->buf = NULL; } /* @@ -913,10 +878,8 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force) release_urb_ctx(&subs->dataurb[i]); for (i = 0; i < SYNC_URBS; i++) release_urb_ctx(&subs->syncurb[i]); - if (subs->tmpbuf) { - kfree(subs->tmpbuf); - subs->tmpbuf = NULL; - } + kfree(subs->tmpbuf); + subs->tmpbuf = NULL; subs->nurbs = 0; } @@ -1039,22 +1002,19 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by snd_urb_ctx_t *u = &subs->syncurb[i]; u->index = i; u->subs = subs; - u->packets = nrpacks; - u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); + u->packets = 1; + u->urb = usb_alloc_urb(1, GFP_KERNEL); if (! u->urb) { release_substream_urbs(subs, 0); return -ENOMEM; } - u->urb->transfer_buffer = subs->syncbuf + i * nrpacks * 4; - u->urb->transfer_buffer_length = nrpacks * 4; + u->urb->transfer_buffer = subs->syncbuf + i * 4; + u->urb->transfer_buffer_length = 4; u->urb->dev = subs->dev; u->urb->pipe = subs->syncpipe; u->urb->transfer_flags = URB_ISO_ASAP; - u->urb->number_of_packets = u->packets; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) - u->urb->interval = 8; - else - u->urb->interval = 1; + u->urb->number_of_packets = 1; + u->urb->interval = 1 << subs->syncinterval; u->urb->context = u; u->urb->complete = snd_usb_complete_callback(snd_complete_sync_urb); } @@ -1272,7 +1232,17 @@ static int set_format(snd_usb_substream_t *subs, struct audioformat *fmt) subs->syncpipe = usb_rcvisocpipe(dev, ep); else subs->syncpipe = usb_sndisocpipe(dev, ep); - subs->syncinterval = get_endpoint(alts, 1)->bRefresh; + 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 */ @@ -1990,10 +1960,11 @@ static void proc_dump_substream_status(snd_usb_substream_t *subs, snd_info_buffe 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\n", + 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)); + : get_high_speed_hz(subs->freqm), + subs->freqm >> 16, subs->freqm & 0xffff); } else { snd_iprintf(buffer, " Status: Stop\n"); } @@ -2183,17 +2154,15 @@ static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audiofor /* * check if the device uses big-endian samples */ -static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp) +static int is_big_endian_format(snd_usb_audio_t *chip, struct audioformat *fp) { - /* M-Audio */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763) { - /* Quattro: captured data only */ - if (le16_to_cpu(dev->descriptor.idProduct) == 0x2001 && - fp->endpoint & USB_DIR_IN) - return 1; - /* Audiophile USB */ - if (le16_to_cpu(dev->descriptor.idProduct) == 0x2003) + 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 */ + return 1; } return 0; } @@ -2207,7 +2176,7 @@ static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp) * @format: the format tag (wFormatTag) * @fmt: the format type descriptor */ -static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat *fp, +static int parse_audio_format_i_type(snd_usb_audio_t *chip, struct audioformat *fp, int format, unsigned char *fmt) { int pcm_format; @@ -2220,12 +2189,12 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat switch (format) { case 0: /* some devices don't define this correctly... */ snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", - dev->devnum, fp->iface, fp->altsetting); + chip->dev->devnum, fp->iface, fp->altsetting); /* fall-through */ case USB_AUDIO_FORMAT_PCM: if (sample_width > sample_bytes * 8) { snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", - dev->devnum, fp->iface, fp->altsetting, + chip->dev->devnum, fp->iface, fp->altsetting, sample_width, sample_bytes); } /* check the format byte size */ @@ -2234,13 +2203,13 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat pcm_format = SNDRV_PCM_FORMAT_S8; break; case 2: - if (is_big_endian_format(dev, fp)) + 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(dev, fp)) + 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; @@ -2250,14 +2219,14 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat break; default: snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", - dev->devnum, fp->iface, fp->altsetting, sample_width, sample_bytes); + chip->dev->devnum, fp->iface, + fp->altsetting, sample_width, sample_bytes); break; } break; case USB_AUDIO_FORMAT_PCM8: /* Dallas DS4201 workaround */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x04fa && - le16_to_cpu(dev->descriptor.idProduct) == 0x4201) + if (chip->usb_id == USB_ID(0x04fa, 0x4201)) pcm_format = SNDRV_PCM_FORMAT_S8; else pcm_format = SNDRV_PCM_FORMAT_U8; @@ -2273,7 +2242,7 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat break; default: snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", - dev->devnum, fp->iface, fp->altsetting, format); + chip->dev->devnum, fp->iface, fp->altsetting, format); break; } return pcm_format; @@ -2290,13 +2259,13 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat * @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(struct usb_device *dev, struct audioformat *fp, +static int parse_audio_format_rates(snd_usb_audio_t *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 FORMAT_TYPE desc\n", - dev->devnum, fp->iface, fp->altsetting); + chip->dev->devnum, fp->iface, fp->altsetting); return -1; } @@ -2343,7 +2312,7 @@ static int parse_audio_format_rates(struct usb_device *dev, struct audioformat * /* * parse the format type I and III descriptors */ -static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp, +static int parse_audio_format_i(snd_usb_audio_t *chip, struct audioformat *fp, int format, unsigned char *fmt) { int pcm_format; @@ -2355,7 +2324,7 @@ static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp, */ pcm_format = SNDRV_PCM_FORMAT_S16_LE; } else { - pcm_format = parse_audio_format_i_type(dev, fp, format, fmt); + pcm_format = parse_audio_format_i_type(chip, fp, format, fmt); if (pcm_format < 0) return -1; } @@ -2363,16 +2332,16 @@ static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp, fp->channels = fmt[4]; if (fp->channels < 1) { snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", - dev->devnum, fp->iface, fp->altsetting, fp->channels); + chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); return -1; } - return parse_audio_format_rates(dev, fp, fmt, 7); + return parse_audio_format_rates(chip, fp, fmt, 7); } /* * prase the format type II descriptor */ -static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp, +static int parse_audio_format_ii(snd_usb_audio_t *chip, struct audioformat *fp, int format, unsigned char *fmt) { int brate, framesize; @@ -2387,7 +2356,7 @@ static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp, break; default: snd_printd(KERN_INFO "%d:%u:%d : unknown format tag 0x%x is detected. processed as MPEG.\n", - dev->devnum, fp->iface, fp->altsetting, format); + chip->dev->devnum, fp->iface, fp->altsetting, format); fp->format = SNDRV_PCM_FORMAT_MPEG; break; } @@ -2396,10 +2365,10 @@ static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp, framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */ snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); fp->frame_size = framesize; - return parse_audio_format_rates(dev, fp, fmt, 8); /* fmt[8..] sample rates */ + return parse_audio_format_rates(chip, fp, fmt, 8); /* fmt[8..] sample rates */ } -static int parse_audio_format(struct usb_device *dev, struct audioformat *fp, +static int parse_audio_format(snd_usb_audio_t *chip, struct audioformat *fp, int format, unsigned char *fmt, int stream) { int err; @@ -2407,29 +2376,30 @@ static int parse_audio_format(struct usb_device *dev, struct audioformat *fp, switch (fmt[3]) { case USB_FORMAT_TYPE_I: case USB_FORMAT_TYPE_III: - err = parse_audio_format_i(dev, fp, format, fmt); + err = parse_audio_format_i(chip, fp, format, fmt); break; case USB_FORMAT_TYPE_II: - err = parse_audio_format_ii(dev, fp, format, fmt); + err = parse_audio_format_ii(chip, fp, format, fmt); break; default: snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", - dev->devnum, fp->iface, fp->altsetting, fmt[3]); + 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 */ + /* FIXME: temporary hack for extigy/audigy 2 nx */ /* extigy apparently supports sample rates other than 48k * but not in ordinary way. so we enable only 48k atm. */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && - le16_to_cpu(dev->descriptor.idProduct) == 0x3000) { + if (chip->usb_id == USB_ID(0x041e, 0x3000) || + chip->usb_id == USB_ID(0x041e, 0x3020)) { if (fmt[3] == USB_FORMAT_TYPE_I && stream == SNDRV_PCM_STREAM_PLAYBACK && - fp->rates != SNDRV_PCM_RATE_48000) + fp->rates != SNDRV_PCM_RATE_48000 && + fp->rates != SNDRV_PCM_RATE_96000) return -1; /* use 48k only */ } #endif @@ -2528,40 +2498,35 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no) /* some quirks for attributes here */ - /* workaround for AudioTrak Optoplay */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x0a92 && - le16_to_cpu(dev->descriptor.idProduct) == 0x0053) { + 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 &= ~EP_CS_ATTR_SAMPLE_RATE; - } - - /* workaround for M-Audio Audiophile USB */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763 && - le16_to_cpu(dev->descriptor.idProduct) == 0x2003) { + 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 |= 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... */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x047f && - le16_to_cpu(dev->descriptor.idProduct) == 0x0ca1) || - /* Griffin iMic (note that there is an older model 77d:223) */ - (le16_to_cpu(dev->descriptor.idVendor) == 0x077d && - le16_to_cpu(dev->descriptor.idProduct) == 0x07af)) { fp->ep_attr &= ~EP_ATTR_MASK; if (stream == SNDRV_PCM_STREAM_PLAYBACK) fp->ep_attr |= EP_ATTR_ADAPTIVE; else fp->ep_attr |= EP_ATTR_SYNC; + break; } /* ok, let's parse further... */ - if (parse_audio_format(dev, fp, format, fmt, stream) < 0) { + if (parse_audio_format(chip, fp, format, fmt, stream) < 0) { kfree(fp->rate_table); kfree(fp); continue; @@ -2587,7 +2552,7 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no) * disconnect streams * called from snd_usb_audio_disconnect() */ -static void snd_usb_stream_disconnect(struct list_head *head, struct usb_driver *driver) +static void snd_usb_stream_disconnect(struct list_head *head) { int idx; snd_usb_stream_t *as; @@ -2796,7 +2761,7 @@ static int create_ua700_ua25_quirk(snd_usb_audio_t *chip, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = &ua25_ep }; - if (le16_to_cpu(chip->dev->descriptor.idProduct) == 0x002b) + if (chip->usb_id == USB_ID(0x0582, 0x002b)) return snd_usb_create_midi_interface(chip, iface, &ua700_quirk); else @@ -2959,6 +2924,25 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac return 0; } +static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) +{ +#if 0 + /* TODO: enable this when high speed synchronization actually works */ + 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; + } +#endif + return 0; +} + /* * audio-interface quirks @@ -3015,8 +2999,8 @@ static void proc_audio_usbid_read(snd_info_entry_t *entry, snd_info_buffer_t *bu snd_usb_audio_t *chip = entry->private_data; if (! chip->shutdown) snd_iprintf(buffer, "%04x:%04x\n", - le16_to_cpu(chip->dev->descriptor.idVendor), - le16_to_cpu(chip->dev->descriptor.idProduct)); + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); } static void snd_usb_audio_create_proc(snd_usb_audio_t *chip) @@ -3086,8 +3070,11 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, 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); @@ -3097,8 +3084,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, strcpy(card->driver, "USB-Audio"); sprintf(component, "USB%04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); + USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); /* retrieve the device string as shortname */ @@ -3110,8 +3096,8 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, card->shortname, sizeof(card->shortname)) <= 0) { /* no name available from anywhere, so use ID */ sprintf(card->shortname, "USB Device %#04x:%#04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); } } @@ -3142,8 +3128,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, snd_usb_audio_create_proc(chip); - snd_card_set_dev(card, &dev->dev); - *rchip = chip; return 0; } @@ -3169,21 +3153,28 @@ static void *snd_usb_audio_probe(struct usb_device *dev, snd_usb_audio_t *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 (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && - le16_to_cpu(dev->descriptor.idProduct) == 0x3000) { + if (id == USB_ID(0x041e, 0x3000)) { if (snd_usb_extigy_boot_quirk(dev, intf) < 0) goto __err_val; config = dev->actconfig; } + /* 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; + } /* * found a config. now register to ALSA @@ -3213,11 +3204,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev, } for (i = 0; i < SNDRV_CARDS; i++) if (enable[i] && ! usb_chip[i] && - (vid[i] == -1 || vid[i] == le16_to_cpu(dev->descriptor.idVendor)) && - (pid[i] == -1 || pid[i] == le16_to_cpu(dev->descriptor.idProduct))) { + (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) { @@ -3281,15 +3273,19 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) snd_card_disconnect(card); /* release the pcm resources */ list_for_each(p, &chip->pcm_list) { - snd_usb_stream_disconnect(p, &usb_audio_driver); + snd_usb_stream_disconnect(p); } /* release the midi resources */ list_for_each(p, &chip->midi_list) { - snd_usbmidi_disconnect(p, &usb_audio_driver); + snd_usbmidi_disconnect(p); + } + /* release mixer resources */ + list_for_each(p, &chip->mixer_list) { + snd_usb_mixer_disconnect(p); } usb_chip[chip->index] = NULL; up(®ister_mutex); - snd_card_free_in_thread(card); + snd_card_free(card); } else { up(®ister_mutex); } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index eecbf19..aedb42a 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -118,6 +118,11 @@ /* 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) +#define USB_ID_PRODUCT(id) ((u16)(id)) + /* */ @@ -127,6 +132,7 @@ struct snd_usb_audio { int index; struct usb_device *dev; snd_card_t *card; + u32 usb_id; int shutdown; int num_interfaces; @@ -136,7 +142,7 @@ struct snd_usb_audio { struct list_head midi_list; /* list of midi interfaces */ int next_midi_device; - unsigned int ignore_ctl_error; /* for mixer */ + struct list_head mixer_list; /* list of mixer interfaces */ }; /* @@ -219,11 +225,12 @@ void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsub 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(snd_usb_audio_t *chip, int ctrlif); +void snd_usb_mixer_disconnect(struct list_head *p); int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *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, struct usb_driver *driver); +void snd_usbmidi_disconnect(struct list_head *p); /* * retrieve usb_interface descriptor from the host interface diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 5d32857..bee7006 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -912,7 +912,7 @@ static void snd_usbmidi_free(snd_usb_midi_t* umidi) /* * Unlinks all URBs (must be done before the usb_device is deleted). */ -void snd_usbmidi_disconnect(struct list_head* p, struct usb_driver *driver) +void snd_usbmidi_disconnect(struct list_head* p) { snd_usb_midi_t* umidi; int i; @@ -955,88 +955,87 @@ static snd_rawmidi_substream_t* snd_usbmidi_find_substream(snd_usb_midi_t* umidi * such as internal control or synthesizer ports. */ static struct { - __u16 vendor; - __u16 product; + u32 id; int port; const char *name_format; } snd_usbmidi_port_names[] = { /* Roland UA-100 */ - {0x0582, 0x0000, 2, "%s Control"}, + { USB_ID(0x0582, 0x0000), 2, "%s Control" }, /* Roland SC-8850 */ - {0x0582, 0x0003, 0, "%s Part A"}, - {0x0582, 0x0003, 1, "%s Part B"}, - {0x0582, 0x0003, 2, "%s Part C"}, - {0x0582, 0x0003, 3, "%s Part D"}, - {0x0582, 0x0003, 4, "%s MIDI 1"}, - {0x0582, 0x0003, 5, "%s MIDI 2"}, + { USB_ID(0x0582, 0x0003), 0, "%s Part A" }, + { USB_ID(0x0582, 0x0003), 1, "%s Part B" }, + { USB_ID(0x0582, 0x0003), 2, "%s Part C" }, + { USB_ID(0x0582, 0x0003), 3, "%s Part D" }, + { USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" }, + { USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" }, /* Roland U-8 */ - {0x0582, 0x0004, 0, "%s MIDI"}, - {0x0582, 0x0004, 1, "%s Control"}, + { USB_ID(0x0582, 0x0004), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x0004), 1, "%s Control" }, /* Roland SC-8820 */ - {0x0582, 0x0007, 0, "%s Part A"}, - {0x0582, 0x0007, 1, "%s Part B"}, - {0x0582, 0x0007, 2, "%s MIDI"}, + { USB_ID(0x0582, 0x0007), 0, "%s Part A" }, + { USB_ID(0x0582, 0x0007), 1, "%s Part B" }, + { USB_ID(0x0582, 0x0007), 2, "%s MIDI" }, /* Roland SK-500 */ - {0x0582, 0x000b, 0, "%s Part A"}, - {0x0582, 0x000b, 1, "%s Part B"}, - {0x0582, 0x000b, 2, "%s MIDI"}, + { USB_ID(0x0582, 0x000b), 0, "%s Part A" }, + { USB_ID(0x0582, 0x000b), 1, "%s Part B" }, + { USB_ID(0x0582, 0x000b), 2, "%s MIDI" }, /* Roland SC-D70 */ - {0x0582, 0x000c, 0, "%s Part A"}, - {0x0582, 0x000c, 1, "%s Part B"}, - {0x0582, 0x000c, 2, "%s MIDI"}, + { USB_ID(0x0582, 0x000c), 0, "%s Part A" }, + { USB_ID(0x0582, 0x000c), 1, "%s Part B" }, + { USB_ID(0x0582, 0x000c), 2, "%s MIDI" }, /* Edirol UM-880 */ - {0x0582, 0x0014, 8, "%s Control"}, + { USB_ID(0x0582, 0x0014), 8, "%s Control" }, /* Edirol SD-90 */ - {0x0582, 0x0016, 0, "%s Part A"}, - {0x0582, 0x0016, 1, "%s Part B"}, - {0x0582, 0x0016, 2, "%s MIDI 1"}, - {0x0582, 0x0016, 3, "%s MIDI 2"}, + { USB_ID(0x0582, 0x0016), 0, "%s Part A" }, + { USB_ID(0x0582, 0x0016), 1, "%s Part B" }, + { USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" }, + { USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" }, /* Edirol UM-550 */ - {0x0582, 0x0023, 5, "%s Control"}, + { USB_ID(0x0582, 0x0023), 5, "%s Control" }, /* Edirol SD-20 */ - {0x0582, 0x0027, 0, "%s Part A"}, - {0x0582, 0x0027, 1, "%s Part B"}, - {0x0582, 0x0027, 2, "%s MIDI"}, + { USB_ID(0x0582, 0x0027), 0, "%s Part A" }, + { USB_ID(0x0582, 0x0027), 1, "%s Part B" }, + { USB_ID(0x0582, 0x0027), 2, "%s MIDI" }, /* Edirol SD-80 */ - {0x0582, 0x0029, 0, "%s Part A"}, - {0x0582, 0x0029, 1, "%s Part B"}, - {0x0582, 0x0029, 2, "%s MIDI 1"}, - {0x0582, 0x0029, 3, "%s MIDI 2"}, + { USB_ID(0x0582, 0x0029), 0, "%s Part A" }, + { USB_ID(0x0582, 0x0029), 1, "%s Part B" }, + { USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" }, + { USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" }, /* Edirol UA-700 */ - {0x0582, 0x002b, 0, "%s MIDI"}, - {0x0582, 0x002b, 1, "%s Control"}, + { USB_ID(0x0582, 0x002b), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x002b), 1, "%s Control" }, /* Roland VariOS */ - {0x0582, 0x002f, 0, "%s MIDI"}, - {0x0582, 0x002f, 1, "%s External MIDI"}, - {0x0582, 0x002f, 2, "%s Sync"}, + { USB_ID(0x0582, 0x002f), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x002f), 1, "%s External MIDI" }, + { USB_ID(0x0582, 0x002f), 2, "%s Sync" }, /* Edirol PCR */ - {0x0582, 0x0033, 0, "%s MIDI"}, - {0x0582, 0x0033, 1, "%s 1"}, - {0x0582, 0x0033, 2, "%s 2"}, + { USB_ID(0x0582, 0x0033), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x0033), 1, "%s 1" }, + { USB_ID(0x0582, 0x0033), 2, "%s 2" }, /* BOSS GS-10 */ - {0x0582, 0x003b, 0, "%s MIDI"}, - {0x0582, 0x003b, 1, "%s Control"}, + { USB_ID(0x0582, 0x003b), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x003b), 1, "%s Control" }, /* Edirol UA-1000 */ - {0x0582, 0x0044, 0, "%s MIDI"}, - {0x0582, 0x0044, 1, "%s Control"}, + { USB_ID(0x0582, 0x0044), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x0044), 1, "%s Control" }, /* Edirol UR-80 */ - {0x0582, 0x0048, 0, "%s MIDI"}, - {0x0582, 0x0048, 1, "%s 1"}, - {0x0582, 0x0048, 2, "%s 2"}, + { USB_ID(0x0582, 0x0048), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x0048), 1, "%s 1" }, + { USB_ID(0x0582, 0x0048), 2, "%s 2" }, /* Edirol PCR-A */ - {0x0582, 0x004d, 0, "%s MIDI"}, - {0x0582, 0x004d, 1, "%s 1"}, - {0x0582, 0x004d, 2, "%s 2"}, + { USB_ID(0x0582, 0x004d), 0, "%s MIDI" }, + { USB_ID(0x0582, 0x004d), 1, "%s 1" }, + { USB_ID(0x0582, 0x004d), 2, "%s 2" }, /* M-Audio MidiSport 8x8 */ - {0x0763, 0x1031, 8, "%s Control"}, - {0x0763, 0x1033, 8, "%s Control"}, + { USB_ID(0x0763, 0x1031), 8, "%s Control" }, + { USB_ID(0x0763, 0x1033), 8, "%s Control" }, /* MOTU Fastlane */ - {0x07fd, 0x0001, 0, "%s MIDI A"}, - {0x07fd, 0x0001, 1, "%s MIDI B"}, + { USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" }, + { USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" }, /* Emagic Unitor8/AMT8/MT4 */ - {0x086a, 0x0001, 8, "%s Broadcast"}, - {0x086a, 0x0002, 8, "%s Broadcast"}, - {0x086a, 0x0003, 4, "%s Broadcast"}, + { USB_ID(0x086a, 0x0001), 8, "%s Broadcast" }, + { USB_ID(0x086a, 0x0002), 8, "%s Broadcast" }, + { USB_ID(0x086a, 0x0003), 4, "%s Broadcast" }, }; static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, @@ -1044,7 +1043,6 @@ static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, snd_rawmidi_substream_t** rsubstream) { int i; - __u16 vendor, product; const char *name_format; snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number); @@ -1055,11 +1053,8 @@ static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, /* TODO: read port name from jack descriptor */ name_format = "%s MIDI %d"; - vendor = le16_to_cpu(umidi->chip->dev->descriptor.idVendor); - product = le16_to_cpu(umidi->chip->dev->descriptor.idProduct); for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) { - if (snd_usbmidi_port_names[i].vendor == vendor && - snd_usbmidi_port_names[i].product == product && + if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id && snd_usbmidi_port_names[i].port == number) { name_format = snd_usbmidi_port_names[i].name_format; break; @@ -1226,9 +1221,12 @@ static int snd_usbmidi_detect_endpoints(snd_usb_midi_t* umidi, struct usb_endpoint_descriptor* epd; int i, out_eps = 0, in_eps = 0; - if (le16_to_cpu(umidi->chip->dev->descriptor.idVendor) == 0x0582) + if (USB_ID_VENDOR(umidi->chip->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; diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 5f19069..e73c1c9 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -35,10 +35,11 @@ #include <linux/usb.h> #include <sound/core.h> #include <sound/control.h> +#include <sound/hwdep.h> +#include <sound/info.h> #include "usbaudio.h" - /* */ @@ -50,6 +51,31 @@ typedef struct usb_audio_term usb_audio_term_t; typedef struct usb_mixer_elem_info usb_mixer_elem_info_t; +struct usb_mixer_interface { + snd_usb_audio_t *chip; + unsigned int ctrlif; + struct list_head list; + unsigned int ignore_ctl_error; + struct urb *urb; + usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */ + + /* Sound Blaster remote control stuff */ + enum { + RC_NONE, + RC_EXTIGY, + RC_AUDIGY2NX, + } rc_type; + unsigned long rc_hwdep_open; + 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]; +}; + + struct usb_audio_term { int id; int type; @@ -62,26 +88,26 @@ struct usbmix_name_map; struct usb_mixer_build { snd_usb_audio_t *chip; + struct usb_mixer_interface *mixer; unsigned char *buffer; unsigned int buflen; - unsigned int ctrlif; - unsigned short vendor; - unsigned short product; - DECLARE_BITMAP(unitbitmap, 32*32); + DECLARE_BITMAP(unitbitmap, 256); usb_audio_term_t oterm; const struct usbmix_name_map *map; + const struct usbmix_selector_map *selector_map; }; struct usb_mixer_elem_info { - snd_usb_audio_t *chip; - unsigned int ctrlif; + struct usb_mixer_interface *mixer; + usb_mixer_elem_info_t *next_id_elem; /* list of controls with same id */ + snd_ctl_elem_id_t *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; - unsigned int initialized: 1; + u8 initialized; }; @@ -187,6 +213,21 @@ static int check_ignored_ctl(mixer_build_t *state, int unitid, int control) return 0; } +/* get the mapped selector source name */ +static int check_mapped_selector_name(mixer_build_t *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 */ @@ -301,16 +342,18 @@ static int get_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, i int timeout = 10; while (timeout-- > 0) { - if (snd_usb_ctl_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 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->ctrlif | (cval->id << 8), + validx, cval->mixer->ctrlif | (cval->id << 8), buf, val_len, 100) >= 0) { *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); return 0; } } - snd_printdd(KERN_ERR "cannot get ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type); + 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; } @@ -339,13 +382,15 @@ static int set_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, i buf[0] = value_set & 0xff; buf[1] = (value_set >> 8) & 0xff; while (timeout -- > 0) - if (snd_usb_ctl_msg(cval->chip->dev, usb_sndctrlpipe(cval->chip->dev, 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->ctrlif | (cval->id << 8), + validx, cval->mixer->ctrlif | (cval->id << 8), buf, val_len, 100) >= 0) return 0; - snd_printdd(KERN_ERR "cannot set ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d, data = 0x%x/0x%x\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]); + 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; } @@ -385,16 +430,22 @@ static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_ou * if failed, give up and free the control instance. */ -static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl) +static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl) { + usb_mixer_elem_info_t *cval = kctl->private_data; int err; - while (snd_ctl_find_id(card, &kctl->id)) + + while (snd_ctl_find_id(state->chip->card, &kctl->id)) kctl->id.index++; - if ((err = snd_ctl_add(card, kctl)) < 0) { + if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); snd_ctl_free_one(kctl); + return 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; } @@ -572,10 +623,8 @@ static struct usb_feature_control_info audio_feature_info[] = { /* private_free callback */ static void usb_mixer_elem_free(snd_kcontrol_t *kctl) { - if (kctl->private_data) { - kfree(kctl->private_data); - kctl->private_data = NULL; - } + kfree(kctl->private_data); + kctl->private_data = NULL; } @@ -608,7 +657,8 @@ static int get_min_max(usb_mixer_elem_info_t *cval, int default_min) } if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, 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->ctrlif, cval->control, cval->id); + 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, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { @@ -668,7 +718,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &val); if (err < 0) { - if (cval->chip->ignore_ctl_error) { + if (cval->mixer->ignore_ctl_error) { ucontrol->value.integer.value[0] = cval->min; return 0; } @@ -684,7 +734,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t /* master channel */ err = get_cur_mix_value(cval, 0, &val); if (err < 0) { - if (cval->chip->ignore_ctl_error) { + if (cval->mixer->ignore_ctl_error) { ucontrol->value.integer.value[0] = cval->min; return 0; } @@ -710,7 +760,7 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &oval); if (err < 0) { - if (cval->chip->ignore_ctl_error) + if (cval->mixer->ignore_ctl_error) return 0; return err; } @@ -727,7 +777,7 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t } else { /* master channel */ err = get_cur_mix_value(cval, 0, &oval); - if (err < 0 && cval->chip->ignore_ctl_error) + if (err < 0 && cval->mixer->ignore_ctl_error) return 0; if (err < 0) return err; @@ -779,8 +829,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return; } - cval->chip = state->chip; - cval->ctrlif = state->ctrlif; + cval->mixer = state->mixer; cval->id = unitid; cval->control = control; cval->cmask = ctl_mask; @@ -855,16 +904,21 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, /* 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 (((state->vendor == 0x471 && (state->product == 0x104 || state->product == 0x105 || state->product == 0x101)) || - (state->vendor == 0x672 && state->product == 0x1041)) && !strcmp(kctl->id.name, "PCM Playback Volume") && - cval->min == -15616) { - snd_printk("USB Audio: using volume control quirk for the UDA1321/N101 chip\n"); - cval->max = -256; + 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): + if (!strcmp(kctl->id.name, "PCM Playback Volume") && + cval->min == -15616) { + snd_printk("using volume control quirk for the UDA1321/N101 chip\n"); + cval->max = -256; + } } 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->chip->card, kctl); + add_control_to_empty(state, kctl); } @@ -947,8 +1001,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, if (! cval) return; - cval->chip = state->chip; - cval->ctrlif = state->ctrlif; + cval->mixer = state->mixer; cval->id = unitid; cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; @@ -979,7 +1032,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, 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->chip->card, kctl); + add_control_to_empty(state, kctl); } @@ -1042,7 +1095,7 @@ static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int err, val; err = get_cur_ctl_value(cval, cval->control << 8, &val); - if (err < 0 && cval->chip->ignore_ctl_error) { + if (err < 0 && cval->mixer->ignore_ctl_error) { ucontrol->value.integer.value[0] = cval->min; return 0; } @@ -1061,7 +1114,7 @@ static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t err = get_cur_ctl_value(cval, cval->control << 8, &oval); if (err < 0) { - if (cval->chip->ignore_ctl_error) + if (cval->mixer->ignore_ctl_error) return 0; return err; } @@ -1179,9 +1232,6 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char } type = combine_word(&dsc[4]); - if (! type) - return 0; /* undefined? */ - for (info = list; info && info->type; info++) if (info->type == type) break; @@ -1199,8 +1249,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; } - cval->chip = state->chip; - cval->ctrlif = state->ctrlif; + cval->mixer = state->mixer; cval->id = unitid; cval->control = valinfo->control; cval->val_type = valinfo->val_type; @@ -1241,7 +1290,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char 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->chip->card, kctl)) < 0) + if ((err = add_control_to_empty(state, kctl)) < 0) return err; } return 0; @@ -1289,7 +1338,7 @@ static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t err = get_cur_ctl_value(cval, 0, &val); if (err < 0) { - if (cval->chip->ignore_ctl_error) { + if (cval->mixer->ignore_ctl_error) { ucontrol->value.enumerated.item[0] = 0; return 0; } @@ -1308,7 +1357,7 @@ static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t err = get_cur_ctl_value(cval, 0, &oval); if (err < 0) { - if (cval->chip->ignore_ctl_error) + if (cval->mixer->ignore_ctl_error) return 0; return err; } @@ -1386,8 +1435,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; } - cval->chip = state->chip; - cval->ctrlif = state->ctrlif; + cval->mixer = state->mixer; cval->id = unitid; cval->val_type = USB_MIXER_U8; cval->channels = 1; @@ -1415,7 +1463,9 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned kfree(cval); return -ENOMEM; } - if (check_input_term(state, desc[5 + i], &iterm) >= 0) + 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); @@ -1450,7 +1500,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", cval->id, kctl->id.name, num_ins); - if ((err = add_control_to_empty(state->chip->card, kctl)) < 0) + if ((err = add_control_to_empty(state, kctl)) < 0) return err; return 0; @@ -1493,41 +1543,55 @@ static int parse_audio_unit(mixer_build_t *state, int unitid) } } +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); + } + if (mixer->rc_urb) + usb_free_urb(mixer->rc_urb); + kfree(mixer->rc_setup_packet); + kfree(mixer); +} + +static int snd_usb_mixer_dev_free(snd_device_t *device) +{ + struct usb_mixer_interface *mixer = device->device_data; + snd_usb_mixer_free(mixer); + return 0; +} + /* * create mixer controls * * walk through all OUTPUT_TERMINAL descriptors to search for mixers */ -int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) +static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) { unsigned char *desc; mixer_build_t state; int err; const struct usbmix_ctl_map *map; - struct usb_device_descriptor *dev = &chip->dev->descriptor; - struct usb_host_interface *hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; - - strcpy(chip->card->mixername, "USB Mixer"); + struct usb_host_interface *hostif; + hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; memset(&state, 0, sizeof(state)); - state.chip = chip; + state.chip = mixer->chip; + state.mixer = mixer; state.buffer = hostif->extra; state.buflen = hostif->extralen; - state.ctrlif = ctrlif; - state.vendor = le16_to_cpu(dev->idVendor); - state.product = le16_to_cpu(dev->idProduct); /* check the mapping table */ - for (map = usbmix_ctl_maps; map->vendor; map++) { - if (map->vendor == state.vendor && map->product == state.product) { + for (map = usbmix_ctl_maps; map->id; map++) { + if (map->id == state.chip->usb_id) { state.map = map->map; - chip->ignore_ctl_error = map->ignore_ctl_error; + state.selector_map = map->selector_map; + mixer->ignore_ctl_error = map->ignore_ctl_error; break; } } -#ifdef IGNORE_CTL_ERROR - chip->ignore_ctl_error = 1; -#endif desc = NULL; while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) { @@ -1543,3 +1607,393 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) } return 0; } + +static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, + int unitid) +{ + usb_mixer_elem_info_t *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_memory_change(struct usb_mixer_interface *mixer, + int unitid) +{ + if (mixer->rc_type == RC_NONE) + 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; + 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 pt_regs *regs) +{ + 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_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 ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN || + (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + return 0; + + epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + 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; +} + +static void snd_usb_soundblaster_remote_complete(struct urb *urb, + struct pt_regs *regs) +{ + struct usb_mixer_interface *mixer = urb->context; + /* + * format of remote control data: + * Extigy: xx 00 + * Audigy 2 NX: 06 80 xx 00 00 00 + */ + int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2; + u32 code; + + if (urb->status < 0 || urb->actual_length <= offset) + return; + code = mixer->rc_buffer[offset]; + /* the Mute button actually changes the mixer control */ + if (code == 13) + snd_usb_mixer_notify_id(mixer, 18); + mixer->rc_code = code; + wmb(); + wake_up(&mixer->rc_waitq); +} + +static int snd_usb_sbrc_hwdep_open(snd_hwdep_t *hw, struct file *file) +{ + struct usb_mixer_interface *mixer = hw->private_data; + + if (test_and_set_bit(0, &mixer->rc_hwdep_open)) + return -EBUSY; + return 0; +} + +static int snd_usb_sbrc_hwdep_release(snd_hwdep_t *hw, struct file *file) +{ + struct usb_mixer_interface *mixer = hw->private_data; + + clear_bit(0, &mixer->rc_hwdep_open); + smp_mb__after_clear_bit(); + return 0; +} + +static long snd_usb_sbrc_hwdep_read(snd_hwdep_t *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(snd_hwdep_t *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) +{ + snd_hwdep_t *hwdep; + int err, len; + + switch (mixer->chip->usb_id) { + case USB_ID(0x041e, 0x3000): + mixer->rc_type = RC_EXTIGY; + len = 2; + break; + case USB_ID(0x041e, 0x3020): + mixer->rc_type = RC_AUDIGY2NX; + len = 6; + break; + default: + return 0; + } + + 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.open = snd_usb_sbrc_hwdep_open; + hwdep->ops.release = snd_usb_sbrc_hwdep_release; + hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; + + 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 = 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; +} + +static int snd_audigy2nx_led_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_audigy2nx_led_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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 snd_kcontrol_new_t 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) { + 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(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + static const struct { + int unitid; + const char *name; + } jacks[] = { + {4, "dig in "}, + {7, "line in"}, + {19, "spk out"}, + {20, "hph out"}, + }; + 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); + for (i = 0; i < ARRAY_SIZE(jacks); ++i) { + snd_iprintf(buffer, "%s: ", jacks[i].name); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + 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) + snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); + else + snd_iprintf(buffer, "?\n"); + } +} + +int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) +{ + static snd_device_ops_t dev_ops = { + .dev_free = snd_usb_mixer_dev_free + }; + struct usb_mixer_interface *mixer; + int err; + + strcpy(chip->card->mixername, "USB Mixer"); + + mixer = kcalloc(1, sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + mixer->chip = chip; + mixer->ctrlif = ctrlif; +#ifdef IGNORE_CTL_ERROR + mixer->ignore_ctl_error = 1; +#endif + mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL); + if (!mixer->id_elems) { + kfree(mixer); + return -ENOMEM; + } + + if ((err = snd_usb_mixer_controls(mixer)) < 0 || + (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)) { + snd_info_entry_t *entry; + + 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, 1024, + snd_audigy2nx_proc_read); + } + + err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); + if (err < 0) + goto _error; + 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); + if (mixer->urb) + usb_kill_urb(mixer->urb); + if (mixer->rc_urb) + usb_kill_urb(mixer->rc_urb); +} diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c index c69b4b0..f05500b0 100644 --- a/sound/usb/usbmixer_maps.c +++ b/sound/usb/usbmixer_maps.c @@ -26,10 +26,16 @@ struct usbmix_name_map { int control; }; +struct usbmix_selector_map { + int id; + int count; + const char **names; +}; + struct usbmix_ctl_map { - int vendor; - int product; + u32 id; const struct usbmix_name_map *map; + const struct usbmix_selector_map *selector_map; int ignore_ctl_error; }; @@ -91,6 +97,96 @@ static struct usbmix_name_map extigy_map[] = { { 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 <bin@bash.info> + */ +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 Input 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", 2 }, */ /* FU, Mic Capture */ + { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */ + { 11, "Line Capture" }, /* FU, default PCM Capture */ + { 12, "Digital In Playback" }, /* FU, default PCM 1 */ + /* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */ + { 14, "Line Playback" }, /* 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 */ +}; + /* LineX FM Transmitter entry - needed to bypass controls bug */ static struct usbmix_name_map linex_map[] = { /* 1: IT pcm */ @@ -127,9 +223,29 @@ static struct usbmix_name_map justlink_map[] = { */ static struct usbmix_ctl_map usbmix_ctl_maps[] = { - { 0x41e, 0x3000, extigy_map, 1 }, - { 0x8bb, 0x2702, linex_map, 1 }, - { 0xc45, 0x1158, justlink_map, 0 }, + { + .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(0x08bb, 0x2702), + .map = linex_map, + .ignore_ctl_error = 1, + }, + { + .id = USB_ID(0x0c45, 0x1158), + .map = justlink_map, + }, { 0 } /* terminator */ }; diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 88bbd94..f513564 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -203,11 +203,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-4", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x000f, - .in_cables = 0x000f + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + }, + { + .ifnum = -1 + } } } }, @@ -216,11 +233,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-8850", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x003f, - .in_cables = 0x003f + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x003f, + .in_cables = 0x003f + } + }, + { + .ifnum = -1 + } } } }, @@ -229,11 +263,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "U-8", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0005, - .in_cables = 0x0005 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0005, + .in_cables = 0x0005 + } + }, + { + .ifnum = -1 + } } } }, @@ -242,11 +293,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-2", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0003, - .in_cables = 0x0003 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + }, + { + .ifnum = -1 + } } } }, @@ -255,11 +323,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-8820", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0013, - .in_cables = 0x0013 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + }, + { + .ifnum = -1 + } } } }, @@ -268,11 +353,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "PC-300", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0001, - .in_cables = 0x0001 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } } } }, @@ -281,11 +383,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-1", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0001, - .in_cables = 0x0001 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } } } }, @@ -294,11 +413,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SK-500", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0013, - .in_cables = 0x0013 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + }, + { + .ifnum = -1 + } } } }, @@ -421,11 +557,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "SD-90", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x000f, - .in_cables = 0x000f + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + }, + { + .ifnum = -1 + } } } }, @@ -434,11 +587,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "MMP-2", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0001, - .in_cables = 0x0001 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } } } }, @@ -609,15 +779,33 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + /* + * 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 snd_usb_audio_quirk_t) { .vendor_name = "BOSS", .product_name = "GS-10", - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0003, - .in_cables = 0x0003 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } } } }, diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index b06a267..e6e6da1 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -1,6 +1,11 @@ /* * usbusy2y.c - ALSA USB US-428 Driver * +2005-04-14 Karsten Wiese + Version 0.8.7.2: + Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom. + Tested ok with kernel 2.6.12-rc2. + 2004-12-14 Karsten Wiese Version 0.8.7.1: snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open. @@ -143,7 +148,7 @@ MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>"); -MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.1"); +MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}"); @@ -430,8 +435,6 @@ static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr) if (ptr) { usX2Ydev_t* usX2Y = usX2Y((snd_card_t*)ptr); struct list_head* p; - if (usX2Y->chip_status == USX2Y_STAT_CHIP_HUP) // on 2.6.1 kernel snd_usbmidi_disconnect() - return; // calls us back. better leave :-) . usX2Y->chip.shutdown = 1; usX2Y->chip_status = USX2Y_STAT_CHIP_HUP; usX2Y_unlinkSeq(&usX2Y->AS04); @@ -439,11 +442,11 @@ static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr) snd_card_disconnect((snd_card_t*)ptr); /* release the midi resources */ list_for_each(p, &usX2Y->chip.midi_list) { - snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver); + snd_usbmidi_disconnect(p); } if (usX2Y->us428ctls_sharedmem) wake_up(&usX2Y->us428ctls_wait_queue_head); - snd_card_free_in_thread((snd_card_t*)ptr); + snd_card_free((snd_card_t*)ptr); } } diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 4c292e0..62dfd28 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -401,10 +401,8 @@ static void usX2Y_urbs_release(snd_usX2Y_substream_t *subs) for (i = 0; i < NRURBS; i++) usX2Y_urb_release(subs->urb + i, subs != subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]); - if (subs->tmpbuf) { - kfree(subs->tmpbuf); - subs->tmpbuf = NULL; - } + kfree(subs->tmpbuf); + subs->tmpbuf = NULL; } /* * initialize a substream's urbs |