[ALSA] snd_usb_caiaq: add support for 'Session I/O' interface
[linux-2.6.git] / sound / usb / caiaq / caiaq-device.c
index 58af814..8317508 100644 (file)
 #include <linux/usb.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
+#include <sound/control.h>
 
 #include "caiaq-device.h"
 #include "caiaq-audio.h"
 #include "caiaq-midi.h"
+#include "caiaq-control.h"
 
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
 #include "caiaq-input.h"
 #endif
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.2.0");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, RigKontrol3},"
                         "{Native Instruments, Kore Controller},"
-                        "{Native Instruments, Audio Kontrol 1}"
-                        "{Native Instruments, Audio 8 DJ}}");
+                        "{Native Instruments, Kore Controller 2},"
+                        "{Native Instruments, Audio Kontrol 1},"
+                        "{Native Instruments, Audio 8 DJ},"
+                        "{Native Instruments, Session I/O}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -94,6 +97,11 @@ static struct usb_device_id snd_usb_id_table[] = {
                .idProduct =    USB_PID_KORECONTROLLER
        },
        {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     USB_VID_NATIVEINSTRUMENTS,
+               .idProduct =    USB_PID_KORECONTROLLER2
+       },
+       {
                .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
                .idProduct =    USB_PID_AK1
@@ -103,6 +111,11 @@ static struct usb_device_id snd_usb_id_table[] = {
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
                .idProduct =    USB_PID_AUDIO8DJ
        },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     USB_VID_NATIVEINSTRUMENTS,
+               .idProduct =    USB_PID_SESSIONIO
+       },
        { /* terminator */ }
 };
 
@@ -140,14 +153,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
        case EP1_CMD_MIDI_READ:
                snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]);
                break;
-
+       case EP1_CMD_READ_IO:
+               if (dev->chip.usb_id ==
+                       USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
+                       if (urb->actual_length > sizeof(dev->control_state))
+                               urb->actual_length = sizeof(dev->control_state);
+                       memcpy(dev->control_state, buf + 1, urb->actual_length);
+                       wake_up(&dev->ep1_wait_queue);
+                       break;
+               }
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
        case EP1_CMD_READ_ERP:
        case EP1_CMD_READ_ANALOG:
-       case EP1_CMD_READ_IO:
                snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length);
-               break;
 #endif
+               break;
        }
 
        dev->ep1_in_urb.actual_length = 0;
@@ -156,10 +176,10 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
                log("unable to submit urb. OOM!?\n");
 }
 
-static int send_command (struct snd_usb_caiaqdev *dev,
-                        unsigned char command, 
-                        const unsigned char *buffer,
-                        int len)
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+                              unsigned char command,
+                              const unsigned char *buffer,
+                              int len)
 {
        int actual_len;
        struct usb_device *usb_dev = dev->chip.dev;
@@ -207,7 +227,8 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
                rate, depth, bpp);
 
        dev->audio_parm_answer = -1;
-       ret = send_command(dev, EP1_CMD_AUDIO_PARAMS, tmp, sizeof(tmp));
+       ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS,
+                                        tmp, sizeof(tmp));
 
        if (ret)
                return ret;
@@ -226,10 +247,11 @@ int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev,
                                int digital, int analog, int erp)
 {
        char tmp[3] = { digital, analog, erp };
-       return send_command(dev, EP1_CMD_AUTO_MSG, tmp, sizeof(tmp));
+       return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
+                                         tmp, sizeof(tmp));
 }
 
-static void setup_card(struct snd_usb_caiaqdev *dev)
+static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
 {
        int ret;
        char val[4];
@@ -241,7 +263,7 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                val[0] = 0x00;
                val[1] = 0x00;
                val[2] = 0x01;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 3);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3);
                break;
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
                /* RigKontrol2 - display two centered dashes ('--') */
@@ -249,22 +271,52 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                val[1] = 0x40;
                val[2] = 0x40;
                val[3] = 0x00;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 4);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4);
                break;
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
                /* Audio Kontrol 1 - make USB-LED stop blinking */
                val[0] = 0x00;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+               break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
+               /* Audio 8 DJ - trigger read of current settings */
+               dev->control_state[0] = 0xff;
+               snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0);
+
+               if (!wait_event_timeout(dev->ep1_wait_queue,
+                                       dev->control_state[0] != 0xff, HZ))
+                       return;
+
+               /* fix up some defaults */
+               if ((dev->control_state[1] != 2) ||
+                   (dev->control_state[2] != 3) ||
+                   (dev->control_state[4] != 2)) {
+                       dev->control_state[1] = 2;
+                       dev->control_state[2] = 3;
+                       dev->control_state[4] = 2;
+                       snd_usb_caiaq_send_command(dev,
+                               EP1_CMD_WRITE_IO, dev->control_state, 6);
+               }
+
                break;
        }
        
-       ret = snd_usb_caiaq_audio_init(dev);
-       if (ret < 0)
-               log("Unable to set up audio system (ret=%d)\n", ret);
+       if (dev->spec.num_analog_audio_out +
+           dev->spec.num_analog_audio_in +
+           dev->spec.num_digital_audio_out +
+           dev->spec.num_digital_audio_in > 0) {
+               ret = snd_usb_caiaq_audio_init(dev);
+               if (ret < 0)
+                       log("Unable to set up audio system (ret=%d)\n", ret);
+       }
        
-       ret = snd_usb_caiaq_midi_init(dev);
-       if (ret < 0)
-               log("Unable to set up MIDI system (ret=%d)\n", ret);
+       if (dev->spec.num_midi_in +
+           dev->spec.num_midi_out > 0) {
+               ret = snd_usb_caiaq_midi_init(dev);
+               if (ret < 0)
+                       log("Unable to set up MIDI system (ret=%d)\n", ret);
+       }
 
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
        ret = snd_usb_caiaq_input_init(dev);
@@ -278,6 +330,10 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                log("snd_card_register() returned %d\n", ret);
                snd_card_free(dev->chip.card);
        }
+
+       ret = snd_usb_caiaq_control_init(dev);
+       if (ret < 0)
+               log("Unable to set up control system (ret=%d)\n", ret);
 }
 
 static struct snd_card* create_card(struct usb_device* usb_dev)
@@ -301,15 +357,15 @@ static struct snd_card* create_card(struct usb_device* usb_dev)
        dev = caiaqdev(card);
        dev->chip.dev = usb_dev;
        dev->chip.card = card;
-       dev->chip.usb_id = USB_ID(usb_dev->descriptor.idVendor,
-                                       usb_dev->descriptor.idProduct);
+       dev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
+                                 le16_to_cpu(usb_dev->descriptor.idProduct));
        spin_lock_init(&dev->spinlock);
        snd_card_set_dev(card, &usb_dev->dev);
 
        return card;
 }
 
-static int init_card(struct snd_usb_caiaqdev *dev)
+static int __devinit init_card(struct snd_usb_caiaqdev *dev)
 {
        char *c;
        struct usb_device *usb_dev = dev->chip.dev;
@@ -340,7 +396,7 @@ static int init_card(struct snd_usb_caiaqdev *dev)
        if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
                return -EIO;
 
-       err = send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
+       err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
        if (err)
                return err;
 
@@ -378,7 +434,7 @@ static int init_card(struct snd_usb_caiaqdev *dev)
        return 0;
 }
 
-static int snd_probe(struct usb_interface *intf, 
+static int __devinit snd_probe(struct usb_interface *intf, 
                     const struct usb_device_id *id)
 {
        int ret;
@@ -406,7 +462,7 @@ static void snd_disconnect(struct usb_interface *intf)
        struct snd_usb_caiaqdev *dev;
        struct snd_card *card = dev_get_drvdata(&intf->dev);
 
-       debug("snd_disconnect(%p)\n", intf);
+       debug("%s(%p)\n", __func__, intf);
 
        if (!card)
                return;