patch-2.2.18 linux/drivers/sound/emu10k1/mixer.c

Next file: linux/drivers/sound/emu10k1/recmgr.c
Previous file: linux/drivers/sound/emu10k1/midi.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/sound/emu10k1/mixer.c linux/drivers/sound/emu10k1/mixer.c
@@ -35,10 +35,13 @@
 
 #define __NO_VERSION__		/* Kernel version only defined once */
 #include <linux/module.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
 
 #include "hwaccess.h"
 #include "8010.h"
+#include "recmgr.h"
 
 #define AC97_PESSIMISTIC
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
@@ -49,32 +52,13 @@
 #define vol_to_sw_5(hwvol) (((31 - (hwvol)) * 100) / 31)
 #define vol_to_sw_4(hwvol) (((15 - (hwvol)) * 100) / 15)
 
+#define DM_MUTE 0x80000000
+
 #ifdef PRIVATE_PCM_VOLUME
 struct sblive_pcm_volume_rec sblive_pcm_volume[MAX_PCM_CHANNELS];
 u16 pcm_last_mixer = 0x6464;
 #endif
 
-/* --------------------------------------------------------------------- */
-
-/*
- * hweightN: returns the hamming weight (i.e. the number
- * of bits set) of a N-bit word
- */
-
-#ifdef hweight32
-#undef hweight32
-#endif
-
-extern __inline__ unsigned int hweight32(unsigned int w)
-{
-	unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
-
-	res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
-	res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
-	res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
-	return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
 /* Mapping arrays */
 static const unsigned int recsrc[] = {
 	SOUND_MASK_MIC,
@@ -85,10 +69,10 @@
 	SOUND_MASK_VOLUME,
 	SOUND_MASK_OGAIN,	/* Used to be PHONEOUT */
 	SOUND_MASK_PHONEIN,
+#ifdef TONE_CONTROL
 	SOUND_MASK_TREBLE,
 	SOUND_MASK_BASS,
-	SOUND_MASK_MONITOR,
-	SOUND_MASK_PCM,
+#endif
 };
 
 static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = {
@@ -135,12 +119,25 @@
 	int nL, nR;
 
 	switch (ch) {
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_VOLUME:
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+        case SOUND_MIXER_BASS:
+#endif
+                return put_user(0x0000, (int *) arg);
+	default:
+		break;
+	}
+
+	if(card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
 	case SOUND_MIXER_LINE:
 	case SOUND_MIXER_CD:
 	case SOUND_MIXER_VIDEO:
 	case SOUND_MIXER_LINE1:
-	case SOUND_MIXER_PCM:
-	case SOUND_MIXER_VOLUME:
 		sblive_readac97(card, volreg[ch], &reg);
 		nL = ((~(reg >> 8) & 0x1f) * 100) / 32;
 		nR = (~(reg & 0x1f) * 100) / 32;
@@ -166,9 +163,6 @@
 		nR = (~(reg & 0x1f) * 100) / 16;
 		return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg);
 
-	case SOUND_MIXER_TREBLE:
-	case SOUND_MIXER_BASS:
-		return put_user(0x0000, (int *) arg);
 	default:
 		return -EINVAL;
 	}
@@ -205,6 +199,8 @@
 	[SOUND_MIXER_DIGITAL2] = 19
 };
 
+#ifdef TONE_CONTROL
+
 static const u32 bass_table[41][5] = {
 	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
 	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
@@ -317,6 +313,8 @@
 	}
 }
 
+#endif
+
 static const u32 db_table[101] = {
 	0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
 	0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
@@ -341,6 +339,29 @@
 	0x7fffffff,
 };
 
+static void aps_update_digital(struct emu10k1_card *card)
+{
+	int i, l1, r1, l2, r2;
+	
+	i = card->arrwVol[volidx[SOUND_MIXER_VOLUME]];
+	l1 = (i & 0xff);
+	r1 = ((i >> 8) & 0xff);
+
+	i = card->arrwVol[volidx[SOUND_MIXER_PCM]];
+	l2 = (i & 0xff);
+	r2 = ((i >> 8) & 0xff);
+	
+	for (i = 0; i < 108; i++) {
+		if (card->digmix[i] != DM_MUTE) {
+			if ((i % 18 >= 0) && (i % 18 < 4))
+				card->digmix[i] = ((i & 1) ? ((u64) db_table[r1] * (u64) db_table[r2]) : ((u64) db_table[l1] * (u64) db_table[l2])) >> 31;
+			else
+				card->digmix[i] = (i & 1) ? db_table[r1] : db_table[l1];
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, card->digmix[i]);
+		}
+	}
+}
+
 static void update_digital(struct emu10k1_card *card)
 {
 	int i, k, l1, r1, l2, r2, l3, r3, l4, r4;
@@ -378,7 +399,7 @@
 	}
 
 	for (i = 0; i < 36; i++) {
-		if (card->digmix[i] != 0x80000000) {
+		if (card->digmix[i] != DM_MUTE) {
 			if (((i >= 0) && (i < 4)) || ((i >= 18) && (i < 22)))
 				j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r3]) : ((u64) db_table[l1] * (u64) db_table[l3]);
 			else if ((i == 6) || (i == 7) || (i == 24) || (i == 25))
@@ -391,7 +412,7 @@
 	}
 
 	for (i = 72; i < 90; i++) {
-		if (card->digmix[i] != 0x80000000) {
+		if (card->digmix[i] != DM_MUTE) {
 			if ((i >= 72) && (i < 76))
 				j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r3]) : ((u64) db_table[l2] * (u64) db_table[l3]);
 			else if ((i == 78) || (i == 79))
@@ -406,15 +427,15 @@
 	for (i = 36; i <= 90; i += 18) {
 		if (i != 72) {
 			for (k = 0; k < 4; k++)
-				if (card->digmix[i + k] != 0x80000000) {
+				if (card->digmix[i + k] != DM_MUTE) {
 					card->digmix[i + k] = db_table[l3];
 					sblive_writeptr(card, FXGPREGBASE + 0x10 + i + k, 0, card->digmix[i + k]);
 				}
-			if (card->digmix[i + 6] != 0x80000000) {
+			if (card->digmix[i + 6] != DM_MUTE) {
 				card->digmix[i + 6] = db_table[l4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 6, 0, card->digmix[i + 6]);
 			}
-			if (card->digmix[i + 7] != 0x80000000) {
+			if (card->digmix[i + 7] != DM_MUTE) {
 				card->digmix[i + 7] = db_table[r4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 7, 0, card->digmix[i + 7]);
 			}
@@ -506,10 +527,11 @@
 
 	switch (ch) {
 	case SOUND_MIXER_VOLUME:
-	case SOUND_MIXER_DIGITAL1:
-	case SOUND_MIXER_LINE3:
-		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_VOLUME) ? "VOLUME" : (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
-		update_digital(card);
+		DPF(4, "SOUND_MIXER_VOLUME:\n");
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
 		return 0;
 	case SOUND_MIXER_PCM:
 		DPF(4, "SOUND_MIXER_PCM\n");
@@ -517,6 +539,34 @@
 		if (update_pcm_attn(card, l1, r1))
 			return 0;
 #endif
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
+		return 0;
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+                DPF(4, "SOUND_MIXER_TREBLE:\n");
+                set_treble(card, l1, r1);
+                return 0;
+
+        case SOUND_MIXER_BASS:
+                DPF(4, "SOUND_MIXER_BASS:\n");
+                set_bass(card, l1, r1);
+		return 0;
+#endif
+	default:
+		break;
+	}
+
+
+	if (card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
+	case SOUND_MIXER_DIGITAL1:
+	case SOUND_MIXER_LINE3:
+		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
 		update_digital(card);
 		return 0;
 	case SOUND_MIXER_DIGITAL2:
@@ -573,16 +623,6 @@
 		sblive_writeac97(card, volreg[ch], wval);
 		return 0;
 
-	case SOUND_MIXER_TREBLE:
-		DPF(4, "SOUND_MIXER_TREBLE:\n");
-		set_treble(card, l1, r1);
-		return 0;
-
-	case SOUND_MIXER_BASS:
-		DPF(4, "SOUND_MIXER_BASS:\n");
-		set_bass(card, l1, r1);
-		return 0;
-
 	default:
 		DPF(2, "Got unknown SOUND_MIXER ioctl\n");
 		return -EINVAL;
@@ -598,6 +638,10 @@
 /* Mixer file operations */
 
 /* FIXME: Do we need spinlocks in here? */
+/* WARNING! not all the ioctl's are supported by the emu-APS
+   (anything AC97 related). As a general rule keep the AC97 related ioctls
+   separate from the rest. This will make it easier to rewrite the mixer
+   using the kernel AC97 interface. */ 
 static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	static const char id[] = "SBLive";
@@ -659,11 +703,10 @@
 			return -EFAULT;
 
 		for (i = 0; i < sizeof(card->digmix) / sizeof(card->digmix[0]); i++)
-			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & 0x80000000) ? 0 : card->digmix[i]);
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & DM_MUTE) ? 0 : card->digmix[i]);
 		return 0;
 
 		break;
-#ifdef EMU10K1_DEBUG
 	case SOUND_MIXER_PRIVATE3: {
 			struct mixer_private_ioctl ctl;
 
@@ -671,18 +714,28 @@
 				return -EFAULT;
 
 			switch (ctl.cmd) {
+#ifdef EMU10K1_DEBUG
 			case CMD_WRITEFN0:
-				if(ctl.val[0] >= 0x7f)
-					return -EINVAL;
-
 				emu10k1_writefn0(card, ctl.val[0], ctl.val[1]);
 				return 0;
 				break;
 
-			case CMD_READFN0:
-				if(ctl.val[0] >= 0x7f)
+			case CMD_WRITEPTR:
+				if(ctl.val[1] >= 0x40)
+					return -EINVAL;
+
+				if(ctl.val[0] > 0xff)
 					return -EINVAL;
 
+				if((ctl.val[0] & 0x7ff) > 0x3f)
+					ctl.val[1] = 0x00;
+
+				sblive_writeptr(card, ctl.val[0], ctl.val[1], ctl.val[2]);
+
+				return 0;
+				break;
+#endif
+			case CMD_READFN0:
 				ctl.val[2] = emu10k1_readfn0(card, ctl.val[0]);
 
 				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
@@ -691,32 +744,68 @@
 				return 0;
 				break;
 
-			case CMD_WRITEPTR:
-				if(ctl.val[1] < 0x00 || ctl.val[1] >= 0x40)
+			case CMD_READPTR:
+				if(ctl.val[1] >= 0x40)
 					return -EINVAL;
 
-				if(ctl.val[0] >= 0x100)
+				if((ctl.val[0] & 0x7ff) > 0xff)
 					return -EINVAL;
 
-				if(ctl.val[0] >= 0x40)
+				if((ctl.val[0] & 0x7ff) > 0x3f)
 					ctl.val[1] = 0x00;
 
-				sblive_writeptr(card, ctl.val[0], ctl.val[1], ctl.val[2]);
+				ctl.val[2] = sblive_readptr(card, ctl.val[0], ctl.val[1]);
+
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
 
 				return 0;
 				break;
 
-			case CMD_READPTR:
-				if(ctl.val[1] < 0x00 || ctl.val[1] >= 0x40)
+			case CMD_SETRECSRC:
+				switch(ctl.val[0]){
+				case WAVERECORD_AC97:
+					if(card->isaps)
+						return -EINVAL;
+					card->wavein.recsrc = WAVERECORD_AC97;
+					break;
+				case WAVERECORD_MIC:	
+					card->wavein.recsrc = WAVERECORD_MIC;
+					break;
+				case WAVERECORD_FX:
+					card->wavein.recsrc = WAVERECORD_FX;
+					card->wavein.fxwc = ctl.val[1] & 0xffff;
+					if(!card->wavein.fxwc)
+						return -EINVAL;
+					break;
+				default:
 					return -EINVAL;
+				}
+				return 0;
+				break;
 
-				if(ctl.val[0] >= 0x100)
-					return -EINVAL;
+			case CMD_GETRECSRC:
+				ctl.val[0] = card->wavein.recsrc;
+				ctl.val[1] = card->wavein.fxwc;
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
 
-				if(ctl.val[0] > 0x40)
-					ctl.val[1] = 0x00;
+				return 0;
+				break;
 
-				ctl.val[2] = sblive_readptr(card, ctl.val[0], ctl.val[1]);
+			case CMD_GETVOICEPARAM:
+
+				ctl.val[0] = card->waveout.send_routing[0];
+				ctl.val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 |
+				             card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24;
+
+				ctl.val[2] = card->waveout.send_routing[1]; 
+				ctl.val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 |
+					     card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24;
+
+				ctl.val[4] = card->waveout.send_routing[2]; 
+				ctl.val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 |
+					     card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24;
 
 				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
 					return -EFAULT;
@@ -724,13 +813,35 @@
 				return 0;
 				break;
 
+			case CMD_SETVOICEPARAM:
+				card->waveout.send_routing[0] = ctl.val[0] & 0xffff;
+				card->waveout.send_a[0] = ctl.val[1] & 0xff;
+				card->waveout.send_b[0] = (ctl.val[1] >> 8) & 0xff;
+				card->waveout.send_c[0] = (ctl.val[1] >> 16) & 0xff;
+				card->waveout.send_d[0] = (ctl.val[1] >> 24) & 0xff;
+
+				card->waveout.send_routing[1] = ctl.val[2] & 0xffff;
+				card->waveout.send_a[1] = ctl.val[3] & 0xff;
+				card->waveout.send_b[1] = (ctl.val[3] >> 8) & 0xff;
+				card->waveout.send_c[1] = (ctl.val[3] >> 16) & 0xff;
+				card->waveout.send_d[1] = (ctl.val[3] >> 24) & 0xff;
+
+				card->waveout.send_routing[2] = ctl.val[4] & 0xffff;
+				card->waveout.send_a[2] = ctl.val[5] & 0xff;
+				card->waveout.send_b[2] = (ctl.val[5] >> 8) & 0xff;
+				card->waveout.send_c[2] = (ctl.val[5] >> 16) & 0xff;
+				card->waveout.send_d[2] = (ctl.val[5] >> 24) & 0xff;
+
+				return 0;
+				break;
+
 			default:
 				return -EINVAL;
 				break;
 			}
 		}
 		break;
-#endif
+
 	case SOUND_MIXER_PRIVATE4:{
 			u32 size;
 			int size_reg = 0;
@@ -797,57 +908,107 @@
 
 	if (_IOC_DIR(cmd) == _IOC_READ) {
 		switch (_IOC_NR(cmd)) {
-		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
-			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
-			sblive_readac97(card, AC97_RECORDSELECT, &reg);
-			return put_user(recsrc[reg & 7], (int *) arg);
-
-		case SOUND_MIXER_DEVMASK:	/* Arg contains a bit for each supported device */
-			DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			case SOUND_MIXER_DEVMASK:       /* Arg contains a bit for each supported device */
+                        DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			if (card->isaps)
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+						SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+						(int *) arg); 
+#else
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+						(int *) arg); 
+#endif
+		
+#ifdef TONE_CONTROL
 			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#else
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#endif
+
+			case SOUND_MIXER_RECMASK:       /* Arg contains a bit for each supported recording source */
+				DPF(2, "SOUND_MIXER_READ_RECMASK\n");
+				if (card->isaps)
+					return put_user(0, (int *) arg);
+
+				return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
+					SOUND_MASK_LINE1 | SOUND_MASK_LINE |
+					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN |
+					SOUND_MASK_PHONEIN, (int *) arg);
+
+			case SOUND_MIXER_STEREODEVS:    /* Mixer channels supporting stereo */
+				DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
+
+				if (card->isaps)
+#ifdef TONE_CONTROL
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        		SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+                                        		(int *) arg);
+#else
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+                                         		(int *) arg);
+#endif
+
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
 					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
-					SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
-					SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
-					SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
-		case SOUND_MIXER_RECMASK:	/* Arg contains a bit for each supported recording source */
-			DPF(2, "SOUND_MIXER_READ_RECMASK\n");
-			return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
-					SOUND_MASK_LINE1 | SOUND_MASK_LINE | 
-					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN | 
-					SOUND_MASK_PHONEIN | SOUND_MASK_MONITOR | 
-					SOUND_MASK_PCM, (int *) arg);
-
-		case SOUND_MIXER_STEREODEVS:	/* Mixer channels supporting stereo */
-			DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
-			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
+					SOUND_MASK_LINE2, (int *) arg);
+#else
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
 					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
-					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | 
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
 					SOUND_MASK_LINE2, (int *) arg);
+#endif
 
-		case SOUND_MIXER_CAPS:
-			DPF(2, "SOUND_MIXER_READ_CAPS\n");
-			return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
-
+			case SOUND_MIXER_CAPS:
+				DPF(2, "SOUND_MIXER_READ_CAPS\n");
+				return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
 #ifdef PRIVATE_PCM_VOLUME
-		case SOUND_MIXER_PCM:
-			/* needs to be before default: !!*/
-			{
-				int i;
-
-				for (i = 0; i < MAX_PCM_CHANNELS; i++) {
-					if (sblive_pcm_volume[i].files == current->files) {
-						return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
-					}
-				}
-			}
+                case SOUND_MIXER_PCM:
+                        /* needs to be before default: !!*/
+                        {
+                                int i;
+
+                                for (i = 0; i < MAX_PCM_CHANNELS; i++) {
+                                        if (sblive_pcm_volume[i].files == current->files) {
+                                                return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
+                                        }
+                                }
+                        }
 #endif
 		default:
+			break;
+		}
+
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
+			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
+			if (card->isaps)
+				return put_user(0, (int *) arg);
+
+			sblive_readac97(card, AC97_RECORDSELECT, &reg);
+			return put_user(recsrc[reg & 7], (int *) arg);
+
+		default:
 			i = _IOC_NR(cmd);
 			DPD(4, "SOUND_MIXER_READ(%d)\n", i);
 			if (i >= SOUND_MIXER_NRDEVICES)
@@ -862,6 +1023,7 @@
 #endif				/* OSS_DOCUMENTED_MIXER_SEMANTICS */
 		}
 	}
+
 	/* End of _IOC_READ */
 	if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE))
 		return -EINVAL;
@@ -873,7 +1035,12 @@
 	case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
 		DPF(2, "SOUND_MIXER_WRITE_RECSRC\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (card->isaps)
+			return -EINVAL;
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		i = hweight32(val);
 		if (i == 0)
 			return 0;	/* val = mixer_recmask(s); */
@@ -897,7 +1064,9 @@
 
 		if (i >= SOUND_MIXER_NRDEVICES)
 			return -EINVAL;
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		if (emu10k1_mixer_wrch(card, i, val))
 			return -EINVAL;
 
@@ -936,14 +1105,14 @@
 
 static int emu10k1_mixer_release(struct inode *inode, struct file *file)
 {
-	DPF(3, "emu10k1_mixer_release()\n");
+	DPF(4, "emu10k1_mixer_release()\n");
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 struct file_operations emu10k1_mixer_fops = {
-	llseek:emu10k1_mixer_llseek,
-	ioctl:emu10k1_mixer_ioctl,
-	open:emu10k1_mixer_open,
-	release:emu10k1_mixer_release,
+	llseek:		emu10k1_mixer_llseek,
+	ioctl:		emu10k1_mixer_ioctl,
+	open:		emu10k1_mixer_open,
+	release:	emu10k1_mixer_release,
 };

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)