]> Pileus Git - ~andy/linux/blobdiff - sound/usb/usbmixer.c
Pull remove-sn-bist-lock into release branch
[~andy/linux] / sound / usb / usbmixer.c
index d90d6fa7431afa5929cc64830ab5ad09ae407940..c3c08c9cb46edfceca5b018f9f5efe7897201aa0 100644 (file)
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/hwdep.h>
+#include <sound/info.h>
 
 #include "usbaudio.h"
 
-#if 0
-#include <linux/lirc.h>
-#else
-/* only those symbols from lirc.h we actually need: */
-#include <linux/ioctl.h>
-#define LIRC_MODE2REC(x)       ((x) << 16)
-#define LIRC_MODE_CODE         0x00000008
-#define LIRC_CAN_REC_CODE      LIRC_MODE2REC(LIRC_MODE_CODE)
-#define LIRC_GET_FEATURES      _IOR('i', 0x00000000, __u32)
-#define LIRC_GET_REC_MODE      _IOR('i', 0x00000002, __u32)
-#define LIRC_SET_REC_MODE      _IOW('i', 0x00000012, __u32)
-#endif
-
 /*
  */
 
@@ -83,6 +71,8 @@ struct usb_mixer_interface {
        struct urb *rc_urb;
        struct usb_ctrlrequest *rc_setup_packet;
        u8 rc_buffer[6];
+
+       u8 audigy2nx_leds[3];
 };
 
 
@@ -373,7 +363,7 @@ static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value
 }
 
 /* channel = 0: master, 1 = first channel */
-inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value)
+static inline int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value)
 {
        return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value);
 }
@@ -409,7 +399,7 @@ static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value)
        return set_ctl_value(cval, SET_CUR, validx, value);
 }
 
-inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value)
+static inline int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value)
 {
        return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
 }
@@ -633,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;
 }
 
 
@@ -836,7 +824,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
        if (check_ignored_ctl(state, unitid, control))
                return;
 
-       cval = kcalloc(1, sizeof(*cval), GFP_KERNEL);
+       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        if (! cval) {
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                return;
@@ -1009,7 +997,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
        if (check_ignored_ctl(state, unitid, 0))
                return;
 
-       cval = kcalloc(1, sizeof(*cval), GFP_KERNEL);
+       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        if (! cval)
                return;
 
@@ -1256,7 +1244,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
                        continue;
                if (check_ignored_ctl(state, unitid, valinfo->control))
                        continue;
-               cval = kcalloc(1, sizeof(*cval), GFP_KERNEL);
+               cval = kzalloc(sizeof(*cval), GFP_KERNEL);
                if (! cval) {
                        snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                        return -ENOMEM;
@@ -1442,7 +1430,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
        if (check_ignored_ctl(state, unitid, 0))
                return 0;
 
-       cval = kcalloc(1, sizeof(*cval), GFP_KERNEL);
+       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        if (! cval) {
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                return -ENOMEM;
@@ -1633,11 +1621,22 @@ static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
 static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
                                        int unitid)
 {
-       /* SB remote control */
-       if (mixer->rc_type != RC_NONE && unitid == 0) {
-               /* read control code from device memory */
+       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;
        }
 }
 
@@ -1773,25 +1772,6 @@ static unsigned int snd_usb_sbrc_hwdep_poll(snd_hwdep_t *hw, struct file *file,
        return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
 }
 
-static int snd_usb_sbrc_hwdep_ioctl(snd_hwdep_t *hw, struct file *file,
-                                   unsigned int cmd, unsigned long arg)
-{
-       u32 __user *argp = (u32 __user *)arg;
-       u32 mode;
-
-       switch (cmd) {
-       case LIRC_GET_FEATURES:
-               return put_user(LIRC_CAN_REC_CODE, argp);
-       case LIRC_GET_REC_MODE:
-               return put_user(LIRC_MODE_CODE, argp);
-       case LIRC_SET_REC_MODE:
-               if (get_user(mode, argp))
-                       return -EFAULT;
-               return mode == LIRC_MODE_CODE ? 0 : -ENOSYS;
-       }
-       return -ENOTTY;
-}
-
 static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
 {
        snd_hwdep_t *hwdep;
@@ -1822,7 +1802,6 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
        hwdep->ops.open = snd_usb_sbrc_hwdep_open;
        hwdep->ops.release = snd_usb_sbrc_hwdep_release;
        hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
-       hwdep->ops.ioctl = snd_usb_sbrc_hwdep_ioctl; 
 
        mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!mixer->rc_urb)
@@ -1846,6 +1825,116 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *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 = {
@@ -1856,7 +1945,7 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
 
        strcpy(chip->card->mixername, "USB Mixer");
 
-       mixer = kcalloc(1, sizeof(*mixer), GFP_KERNEL);
+       mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
        if (!mixer)
                return -ENOMEM;
        mixer->chip = chip;
@@ -1871,23 +1960,31 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
        }
 
        if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
-           (err = snd_usb_mixer_status_create(mixer)) < 0) {
-               snd_usb_mixer_free(mixer);
-               return err;
-       }
+           (err = snd_usb_mixer_status_create(mixer)) < 0)
+               goto _error;
 
-       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) {
-               snd_usb_mixer_free(mixer);
-               return err;
+       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) {
-               snd_usb_mixer_free(mixer);
-               return err;
-       }
+       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)