From 7b1eda223debcba706ab989a09c4eecb327aebdf Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 11 Mar 2010 21:13:22 +0100 Subject: [PATCH] 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 diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 0758d8dc8cd..744024a0a9f 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 00000000000..d2f4dcdf59e --- /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 00000000000..bdbfab09381 --- /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 0c0b23b6379..a82cfeda21f 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 ab8f0f0b65b..ec2436e9532 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 e199e4bb02f..63101ae201c 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 */