[ALSA] caiaq - add control API and more input features

 - added support for all input controllers on Native Instrument's 'Kore
   controller'.
 - added ALSA controls to switch LEDs on 'RigKontrol 2', 'RigKontrol3',
   'Audio Kontrol 1' and 'Kore controller'.
 - added ALSA controls to switch input mode, software lock and ground
   lift features on 'Audio 8 DJ'.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index 58af814..dc2e7f7 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -31,17 +31,19 @@
 #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.0");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
 			 "{Native Instruments, RigKontrol3},"
@@ -140,14 +142,21 @@
 	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 +165,10 @@
 		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 +216,8 @@
 		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,7 +236,8 @@
 				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)
@@ -241,7 +252,7 @@
 		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,12 +260,34 @@
 		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;
 	}
 	
@@ -278,6 +311,10 @@
 		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)
@@ -340,7 +377,7 @@
 	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;