
From: C.L. Tien -  <cltien@cmedia.com.tw>

cmpci audio driver update.  Changes, in reverse chronological order:

- Load gameport driver only joystick == 1.

- Change cm: -> cmpci: in debugging messages.

- Remove ALLOCATE_IO since 8738-6ch will be used on HP's workstation.

- Change IRQ print width.

- Add inline back for those already have in kernel source tree.


- Due to kernel team's oppinion, changes:

	1. Return to save_irq/restore_irq.
	2. Remove inline.
	3. Remove PROC_FS support.
	4. Remove some debugging messages.

- Change mic_boost default as 0.

- Change mic record gain setting for chip version 039 and later.

- We don't need wave table.

- Add mic_boost to control mic boost.

- Only touch MIXER2 if we get MIC control.

- Remove the DMA 16MB check. It is not useful.

- Change to new style init/release.

- Change wait/schedule and use list.

- Add gameport support, now it can be loaded automatically.

- Add SPDIF-IN in DSP_BIND and disable it after stop adc.

- Use channel 0 for play and channel 1 for record, since I need SPDIF-in.

- Fix typo.

- Add ioctl for setting SPDIF out and in.

- Change channel number in constants to DAC/ADC. This can ease switch channels.

- Add more ioctl for SPDIF interface.

- Implement write_proc, it only accept '0' for now.

- Change const to macro.

- Use different SPD24SEL for different chip versions.

- Change register fields into document name.

- Add interface of /proc, which can be used for configuration and info.

- Add functions for SPDIF in/out and replace statements with them.

- Add mic_as_boost back for ASUS.

- Fix recording hang when SMP enabled.

- Change the way handling mute.

- Fix the code, now AUX can work as other sources.

- Change OUTSRC, it is fully implemented now.

- Add pseudo register and bits to support AUX volume/mute control.

- Remove some mixer range check to allow value for pseudo bits work.

- Change to SPDCOPYRHT.

- Add SNDCTL_SPDIF_PROTECT ioctl, requested by ZapMedia and InterVideo.

- Disable spdif out on all chip versions in set_dac_channels().


---

 25-akpm/sound/oss/cmpci.c | 1329 ++++++++++++++++++++++++++++++----------------
 1 files changed, 885 insertions(+), 444 deletions(-)

diff -puN sound/oss/cmpci.c~cmpci.c-5.64 sound/oss/cmpci.c
--- 25/sound/oss/cmpci.c~cmpci.c-5.64	2004-04-06 21:03:07.390540216 -0700
+++ 25-akpm/sound/oss/cmpci.c	2004-04-06 21:03:07.408537480 -0700
@@ -1,4 +1,5 @@
 /*****************************************************************************/
+
 /*
  *      cmpci.c  --  C-Media PCI audio driver.
  *
@@ -69,136 +70,167 @@
  *                     (8738 only)
  *                     Fix bug cause x11amp cannot play.
  *
- *    Fixes:
- *    Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *    18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it
- *    		   was calling prog_dmabuf with s->lock held, call missing
- *    		   unlock_kernel in cm_midi_release
- *    08/10/2001 - use set_current_state in some more places
- *
- *	Carlos Eduardo Gorges <carlos@techlinux.com.br>
- *	Fri May 25 2001 
- *	- SMP support ( spin[un]lock* revision )
- *	- speaker mixer support 
- *	Mon Aug 13 2001
- *	- optimizations and cleanups
- *    03/01/2003 - open_mode fixes from Georg Acher <acher@in.tum.de>
- *
  */
+
 /*****************************************************************************/
       
+#define EXPORT_SYMTAB
+#include <linux/version.h>
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/sound.h>
-#include <linux/slab.h>
+#include <linux/malloc.h>
 #include <linux/soundcard.h>
 #include <linux/pci.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/wrapper.h>
+#endif
+#include <asm/io.h>
+#include <asm/dma.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 #include <linux/spinlock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 #include <linux/smp_lock.h>
-#include <linux/bitops.h>
-#include <linux/wait.h>
-
-#include <asm/io.h>
-#include <asm/page.h>
+#endif
+#else
+#include <asm/spinlock.h>
+#       define AFMT_AC3         0x00000400      /* Dolby Digital AC3 */
+#       define DSP_CAP_BIND             0x00008000      /* channel binding to fr
+ont/rear/cneter/lfe */
+#define SNDCTL_DSP_GETCHANNELMASK               _SIOWR('P', 64, int)
+#define SNDCTL_DSP_BIND_CHANNEL         _SIOWR('P', 65, int)
+#       define DSP_BIND_QUERY           0x00000000
+#       define DSP_BIND_FRONT           0x00000001
+#       define DSP_BIND_SURR            0x00000002
+#       define DSP_BIND_CENTER_LFE      0x00000004
+#       define DSP_BIND_HANDSET         0x00000008
+#       define DSP_BIND_MIC             0x00000010
+#       define DSP_BIND_MODEM1          0x00000020
+#       define DSP_BIND_MODEM2          0x00000040
+#       define DSP_BIND_I2S             0x00000080
+#       define DSP_BIND_SPDIF           0x00000100
+#endif
 #include <asm/uaccess.h>
+#include <asm/hardirq.h>
 
 #include "dm.h"
 
 /* --------------------------------------------------------------------- */
+
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
-#undef DMABYTEIO
+
 /* --------------------------------------------------------------------- */
 
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA         0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738  0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+#endif
+
 #define CM_MAGIC  ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A)
 
-/* CM8338 registers definition ****************/
+/*
+ * CM8338 registers definition
+ */
 
-#define CODEC_CMI_FUNCTRL0		(0x00)
-#define CODEC_CMI_FUNCTRL1		(0x04)
-#define CODEC_CMI_CHFORMAT		(0x08)
-#define CODEC_CMI_INT_HLDCLR		(0x0C)
-#define CODEC_CMI_INT_STATUS		(0x10)
-#define CODEC_CMI_LEGACY_CTRL		(0x14)
-#define CODEC_CMI_MISC_CTRL		(0x18)
-#define CODEC_CMI_TDMA_POS		(0x1C)
-#define CODEC_CMI_MIXER			(0x20)
-#define CODEC_SB16_DATA			(0x22)
-#define CODEC_SB16_ADDR			(0x23)
-#define CODEC_CMI_MIXER1		(0x24)
-#define CODEC_CMI_MIXER2		(0x25)
-#define CODEC_CMI_AUX_VOL		(0x26)
-#define CODEC_CMI_MISC			(0x27)
-#define CODEC_CMI_AC97			(0x28)
-
-#define CODEC_CMI_CH0_FRAME1		(0x80)
-#define CODEC_CMI_CH0_FRAME2		(0x84)
-#define CODEC_CMI_CH1_FRAME1		(0x88)
-#define CODEC_CMI_CH1_FRAME2		(0x8C)
-
-#define CODEC_CMI_EXT_REG		(0xF0)
-
-/*  Mixer registers for SB16 ******************/
-
-#define DSP_MIX_DATARESETIDX		((unsigned char)(0x00))
-
-#define DSP_MIX_MASTERVOLIDX_L		((unsigned char)(0x30))
-#define DSP_MIX_MASTERVOLIDX_R		((unsigned char)(0x31))
-#define DSP_MIX_VOICEVOLIDX_L		((unsigned char)(0x32))
-#define DSP_MIX_VOICEVOLIDX_R		((unsigned char)(0x33))
-#define DSP_MIX_FMVOLIDX_L		((unsigned char)(0x34))
-#define DSP_MIX_FMVOLIDX_R		((unsigned char)(0x35))
-#define DSP_MIX_CDVOLIDX_L		((unsigned char)(0x36))
-#define DSP_MIX_CDVOLIDX_R		((unsigned char)(0x37))
-#define DSP_MIX_LINEVOLIDX_L		((unsigned char)(0x38))
-#define DSP_MIX_LINEVOLIDX_R		((unsigned char)(0x39))
-
-#define DSP_MIX_MICVOLIDX		((unsigned char)(0x3A))
-#define DSP_MIX_SPKRVOLIDX		((unsigned char)(0x3B))
-
-#define DSP_MIX_OUTMIXIDX		((unsigned char)(0x3C))
-
-#define DSP_MIX_ADCMIXIDX_L		((unsigned char)(0x3D))
-#define DSP_MIX_ADCMIXIDX_R		((unsigned char)(0x3E))
-
-#define DSP_MIX_INGAINIDX_L		((unsigned char)(0x3F))
-#define DSP_MIX_INGAINIDX_R		((unsigned char)(0x40))
-#define DSP_MIX_OUTGAINIDX_L		((unsigned char)(0x41))
-#define DSP_MIX_OUTGAINIDX_R		((unsigned char)(0x42))
-
-#define DSP_MIX_AGCIDX			((unsigned char)(0x43))
-
-#define DSP_MIX_TREBLEIDX_L		((unsigned char)(0x44))
-#define DSP_MIX_TREBLEIDX_R		((unsigned char)(0x45))
-#define DSP_MIX_BASSIDX_L		((unsigned char)(0x46))
-#define DSP_MIX_BASSIDX_R		((unsigned char)(0x47))
-
-#define CM_CH0_RESET			0x04
-#define CM_CH1_RESET			0x08
-#define CM_EXTENT_CODEC			0x100
-#define CM_EXTENT_MIDI			0x2
-#define CM_EXTENT_SYNTH			0x4
-#define CM_INT_CH0			1
-#define CM_INT_CH1			2
-
-#define CM_CFMT_STEREO			0x01
-#define CM_CFMT_16BIT			0x02
-#define CM_CFMT_MASK			0x03
-#define CM_CFMT_DACSHIFT		2
-#define CM_CFMT_ADCSHIFT		0
+#define CODEC_CMI_FUNCTRL0      (0x00)
+#define CODEC_CMI_FUNCTRL1      (0x04)
+#define CODEC_CMI_CHFORMAT      (0x08)
+#define CODEC_CMI_INT_HLDCLR    (0x0C)
+#define CODEC_CMI_INT_STATUS    (0x10)
+#define CODEC_CMI_LEGACY_CTRL   (0x14)
+#define CODEC_CMI_MISC_CTRL     (0x18)
+#define CODEC_CMI_TDMA_POS      (0x1C)
+#define CODEC_CMI_MIXER         (0x20)
+#define CODEC_SB16_DATA         (0x22)
+#define CODEC_SB16_ADDR         (0x23)
+#define CODEC_CMI_MIXER1        (0x24)
+#define CODEC_CMI_MIXER2        (0x25)
+#define CODEC_CMI_AUX_VOL       (0x26)
+#define CODEC_CMI_MISC          (0x27)
+#define CODEC_CMI_AC97          (0x28)
+
+#define CODEC_CMI_CH0_FRAME1    (0x80)
+#define CODEC_CMI_CH0_FRAME2    (0x84)
+#define CODEC_CMI_CH1_FRAME1    (0x88)
+#define CODEC_CMI_CH1_FRAME2    (0x8C)
 
-static const unsigned sample_shift[]	= { 0, 1, 1, 2 };
+#define CODEC_CMI_EXT_REG       (0xF0)
+#define UCHAR	unsigned char
+/*
+**  Mixer registers for SB16
+*/
+
+#define DSP_MIX_DATARESETIDX    ((UCHAR)(0x00))
+
+#define DSP_MIX_MASTERVOLIDX_L  ((UCHAR)(0x30))
+#define DSP_MIX_MASTERVOLIDX_R  ((UCHAR)(0x31))
+#define DSP_MIX_VOICEVOLIDX_L   ((UCHAR)(0x32))
+#define DSP_MIX_VOICEVOLIDX_R   ((UCHAR)(0x33))
+#define DSP_MIX_FMVOLIDX_L      ((UCHAR)(0x34))
+#define DSP_MIX_FMVOLIDX_R      ((UCHAR)(0x35))
+#define DSP_MIX_CDVOLIDX_L      ((UCHAR)(0x36))
+#define DSP_MIX_CDVOLIDX_R      ((UCHAR)(0x37))
+#define DSP_MIX_LINEVOLIDX_L    ((UCHAR)(0x38))
+#define DSP_MIX_LINEVOLIDX_R    ((UCHAR)(0x39))
+
+#define DSP_MIX_MICVOLIDX       ((UCHAR)(0x3A))
+#define DSP_MIX_SPKRVOLIDX      ((UCHAR)(0x3B))
+
+#define DSP_MIX_OUTMIXIDX       ((UCHAR)(0x3C))
+
+#define DSP_MIX_ADCMIXIDX_L     ((UCHAR)(0x3D))
+#define DSP_MIX_ADCMIXIDX_R     ((UCHAR)(0x3E))
+
+#define DSP_MIX_INGAINIDX_L     ((UCHAR)(0x3F))
+#define DSP_MIX_INGAINIDX_R     ((UCHAR)(0x40))
+#define DSP_MIX_OUTGAINIDX_L    ((UCHAR)(0x41))
+#define DSP_MIX_OUTGAINIDX_R    ((UCHAR)(0x42))
+
+#define DSP_MIX_AGCIDX          ((UCHAR)(0x43))
+
+#define DSP_MIX_TREBLEIDX_L     ((UCHAR)(0x44))
+#define DSP_MIX_TREBLEIDX_R     ((UCHAR)(0x45))
+#define DSP_MIX_BASSIDX_L       ((UCHAR)(0x46))
+#define DSP_MIX_BASSIDX_R       ((UCHAR)(0x47))
+#define CM_CH0_RESET	  0x04
+#define CM_CH1_RESET	  0x08
+#define CM_EXTENT_CODEC	  0x100
+#define CM_EXTENT_MIDI	  0x2
+#define CM_EXTENT_SYNTH	  0x4
+#define CM_INT_CH0	  1
+#define CM_INT_CH1	  2
+
+#define CM_CFMT_STEREO     0x01
+#define CM_CFMT_16BIT      0x02
+#define CM_CFMT_MASK       0x03
+#define CM_CFMT_DACSHIFT   2
+#define CM_CFMT_ADCSHIFT   0
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
 
 #define CM_ENABLE_CH1      0x2
 #define CM_ENABLE_CH0      0x1
 
-/* MIDI buffer sizes **************************/
+
+/* MIDI buffer sizes */
 
 #define MIDIINBUF  256
 #define MIDIOUTBUF 256
@@ -211,35 +243,49 @@ static const unsigned sample_shift[]	= {
 
 #define SND_DEV_DSP16   5 
 
-#define NR_DEVICE 3		/* maximum number of devices */
+#define	set_dac1_rate	set_adc_rate
+#define	stop_dac1	stop_adc
+#define	get_dmadac1	get_dmaadc
 
-/*********************************************/
+/* --------------------------------------------------------------------- */
 
 struct cm_state {
-	unsigned int magic;		/* magic */
-	struct cm_state *next;		/* we keep cm cards in a linked list */
+	/* magic */
+	unsigned int magic;
 
-	int dev_audio;			/* soundcore stuff */
+	/* we keep cm cards in a linked list */
+	struct cm_state *next;
+
+	/* soundcore stuff */
+	int dev_audio;
 	int dev_mixer;
 	int dev_midi;
 	int dev_dmfm;
 
-	unsigned int iosb, iobase, iosynth,
-			 iomidi, iogame, irq;	/* hardware resources */
-	unsigned short deviceid;		/* pci_id */
+	/* hardware resources */
+	unsigned int iosb, iobase, iosynth, iomidi, iogame, irq;
+	unsigned short deviceid;
 
-        struct {				/* mixer stuff */
+        /* mixer stuff */
+        struct {
                 unsigned int modcnt;
+#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS
 		unsigned short vol[13];
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
         } mix;
 
-	unsigned int rateadc, ratedac;		/* wave stuff */
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
 	unsigned char fmt, enable;
 
 	spinlock_t lock;
 	struct semaphore open_sem;
 	mode_t open_mode;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	wait_queue_head_t open_wait;
+#else
+	struct wait_queue *open_wait;
+#endif
 
 	struct dmabuf {
 		void *rawbuf;
@@ -250,15 +296,19 @@ struct cm_state {
 		unsigned hwptr, swptr;
 		unsigned total_bytes;
 		int count;
-		unsigned error;		/* over/underrun */
+		unsigned error; /* over/underrun */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		wait_queue_head_t wait;
-		
-		unsigned fragsize;	/* redundant, but makes calculations easier */
+#else
+		struct wait_queue *wait;
+#endif
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
 		unsigned dmasize;
 		unsigned fragsamples;
 		unsigned dmasamples;
-		
-		unsigned mapped:1;	/* OSS stuff */
+		/* OSS stuff */
+		unsigned mapped:1;
 		unsigned ready:1;
 		unsigned endcleared:1;
 		unsigned ossfragshift;
@@ -266,78 +316,119 @@ struct cm_state {
 		unsigned subdivision;
 	} dma_dac, dma_adc;
 
-	struct {			/* midi stuff */
+	/* midi stuff */
+	struct {
 		unsigned ird, iwr, icnt;
 		unsigned ord, owr, ocnt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		wait_queue_head_t iwait;
 		wait_queue_head_t owait;
+#else
+		struct wait_queue *iwait;
+		struct wait_queue *owait;
+#endif
 		struct timer_list timer;
 		unsigned char ibuf[MIDIINBUF];
 		unsigned char obuf[MIDIOUTBUF];
 	} midi;
 	
-	int	chip_version;		
+	/* misc stuff */
+	int	modem;
+	int	chip_version;
 	int	max_channels;
-	int	curr_channels;		
-	int	speakers;		/* number of speakers */
-	int	capability;		/* HW capability, various for chip versions */
-
-	int	status;			/* HW or SW state */
+	int	curr_channels;
+	int	speakers;	// number of speakers
+	int	capability;	// HW capability, various for chip versions
+	int	status;		// HW or SW state
 	
-	int	spdif_counter;		/* spdif frame counter */
+	/* spdif frame counter */
+	int	spdif_counter;
 };
 
 /* flags used for capability */
-#define	CAN_AC3_HW		0x00000001		/* 037 or later */
-#define	CAN_AC3_SW		0x00000002		/* 033 or later */
-#define	CAN_AC3			(CAN_AC3_HW | CAN_AC3_SW)
-#define CAN_DUAL_DAC		0x00000004		/* 033 or later */
-#define	CAN_MULTI_CH_HW		0x00000008		/* 039 or later */
-#define	CAN_MULTI_CH		(CAN_MULTI_CH_HW | CAN_DUAL_DAC)
-#define	CAN_LINE_AS_REAR	0x00000010		/* 033 or later */
-#define	CAN_LINE_AS_BASS	0x00000020		/* 039 or later */
-#define	CAN_MIC_AS_BASS		0x00000040		/* 039 or later */
+#define	CAN_AC3_HW	0x00000001	// 037 or later
+#define	CAN_AC3_SW	0x00000002	// 033 or later
+#define	CAN_AC3		(CAN_AC3_HW | CAN_AC3_SW)
+#define CAN_DUAL_DAC	0x00000004	// 033 or later
+#define	CAN_MULTI_CH_HW	0x00000008	// 039 or later
+#define	CAN_MULTI_CH	(CAN_MULTI_CH_HW | CAN_DUAL_DAC)
+#define	CAN_LINE_AS_REAR	0x00000010	// 033 or later
+#define	CAN_LINE_AS_BASS	0x00000020	// 039 or later
+#define	CAN_MIC_AS_BASS		0x00000040	// 039 or later
 
 /* flags used for status */
-#define	DO_AC3_HW		0x00000001
-#define	DO_AC3_SW		0x00000002
-#define	DO_AC3			(DO_AC3_HW | DO_AC3_SW)
-#define	DO_DUAL_DAC		0x00000004
-#define	DO_MULTI_CH_HW		0x00000008
-#define	DO_MULTI_CH		(DO_MULTI_CH_HW | DO_DUAL_DAC)
-#define	DO_LINE_AS_REAR		0x00000010		/* 033 or later */
-#define	DO_LINE_AS_BASS		0x00000020		/* 039 or later */
-#define	DO_MIC_AS_BASS		0x00000040		/* 039 or later */
-#define	DO_SPDIF_OUT		0x00000100
-#define	DO_SPDIF_IN		0x00000200
-#define	DO_SPDIF_LOOP		0x00000400
+#define	DO_AC3_HW	0x00000001
+#define	DO_AC3_SW	0x00000002
+#define	DO_AC3		(DO_AC3_HW | DO_AC3_SW)
+#define	DO_DUAL_DAC	0x00000004
+#define	DO_MULTI_CH_HW	0x00000008
+#define	DO_MULTI_CH	(DO_MULTI_CH_HW | DO_DUAL_DAC)
+#define	DO_LINE_AS_REAR	0x00000010	// 033 or later
+#define	DO_LINE_AS_BASS	0x00000020	// 039 or later
+#define	DO_MIC_AS_BASS	0x00000040	// 039 or later
+#define	DO_SPDIF_OUT	0x00000100
+#define	DO_SPDIF_IN	0x00000200
+#define	DO_SPDIF_LOOP	0x00000400
 
-static struct cm_state *devs;
-static unsigned long wavetable_mem;
+/* --------------------------------------------------------------------- */
+
+static struct cm_state *devs = NULL;
+static struct cm_state *devaudio = NULL;
+static unsigned long wavetable_mem = 0;
 
 /* --------------------------------------------------------------------- */
 
-static inline unsigned ld2(unsigned int x)
+extern __inline__ unsigned ld2(unsigned int x)
 {
-	unsigned exp=16,l=5,r=0;
-	static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000};
-
-	/* num: 2, 4, 16, 256, 65536 */
-	/* exp: 1, 2,  4,   8,    16 */
+	unsigned r = 0;
 	
-	while(l--) {
-		if( x >= num[l] ) {
-			if(num[l]>2) x >>= exp;
-			r+=exp;
-		}
-		exp>>=1;
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
 	}
-
+	if (x >= 2)
+		r++;
 	return r;
 }
 
+/*
+ * 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);
+}
+
 /* --------------------------------------------------------------------- */
 
+/*
+ * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver.
+ */
+
+#undef DMABYTEIO
+
 static void maskb(unsigned int addr, unsigned int mask, unsigned int value)
 {
 	outb((inb(addr) & mask) | value, addr);
@@ -389,77 +480,69 @@ static void set_countdac(struct cm_state
 	    set_countadc(s, count);
 }
 
-static inline unsigned get_dmadac(struct cm_state *s)
+extern __inline__ unsigned get_dmadac(struct cm_state *s)
 {
 	unsigned int curr_addr;
 
+#if 1
 	curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1;
 	curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
 	curr_addr = s->dma_dac.dmasize - curr_addr;
-
+#else
+	curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1);
+	curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]-1);
+	curr_addr -= s->dma_dac.rawphys;
+#endif
 	return curr_addr;
 }
 
-static inline unsigned get_dmaadc(struct cm_state *s)
+extern __inline__ unsigned get_dmaadc(struct cm_state *s)
 {
 	unsigned int curr_addr;
 
+#if 1
 	curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1;
 	curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK];
 	curr_addr = s->dma_adc.dmasize - curr_addr;
-
+#else
+	curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1);
+	curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]-1);
+	curr_addr -= s->dma_adc.rawphys;
+#endif
 	return curr_addr;
 }
 
 static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data)
 {
 	outb(idx, s->iobase + CODEC_SB16_ADDR);
-	udelay(10);
 	outb(data, s->iobase + CODEC_SB16_DATA);
-	udelay(10);
 }
 
 static unsigned char rdmixer(struct cm_state *s, unsigned char idx)
 {
 	unsigned char v;
-	unsigned long flags;
-	
-	spin_lock_irqsave(&s->lock, flags);
+
 	outb(idx, s->iobase + CODEC_SB16_ADDR);
-	udelay(10);
 	v = inb(s->iobase + CODEC_SB16_DATA);
-	udelay(10);
-	spin_unlock_irqrestore(&s->lock, flags);
 	return v;
 }
 
-static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data)
-{
-	if (mask)
-	{
-		s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT);
-		udelay(10);
-	}
-	s->fmt = (s->fmt & mask) | data;
-	outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT);
-	udelay(10);
-}
-
 static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	set_fmt_unlocked(s,mask,data);
+	if (mask)
+		s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT);
+	s->fmt = (s->fmt & mask) | data;
+	outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT);
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
 static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data)
 {
 	outb(idx, s->iobase + CODEC_SB16_ADDR);
-	udelay(10);
 	outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA);
-	udelay(10);
 }
 
 static struct {
@@ -479,8 +562,11 @@ static struct {
 	{ 48000,	(44100 + 48000) / 2,	48000,			7 }
 };
 
-static void set_spdifout_unlocked(struct cm_state *s, unsigned rate)
+static void set_spdifout(struct cm_state *s, unsigned rate)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
 	if (rate == 48000 || rate == 44100) {
 		// SPDIFI48K SPDF_ACc97
 		maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0);
@@ -499,14 +585,6 @@ static void set_spdifout_unlocked(struct
 			maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0);
 		s->status &= ~DO_SPDIF_OUT;
 	}
-}
-
-static void set_spdifout(struct cm_state *s, unsigned rate)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&s->lock, flags);
-	set_spdifout_unlocked(s,rate);
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
@@ -526,8 +604,12 @@ static unsigned parity(unsigned data)
 	return parity & 1;
 }
 
-static void set_ac3_unlocked(struct cm_state *s, unsigned rate)
+static void set_ac3(struct cm_state *s, unsigned rate)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	set_spdifout(s, rate);
 	/* enable AC3 */
 	if (rate == 48000 || rate == 44100) {
 		// mute DAC
@@ -567,30 +649,18 @@ static void set_ac3_unlocked(struct cm_s
 		s->status &= ~DO_AC3;
 	}
 	s->spdif_counter = 0;
-
-}
-
-static void set_ac3(struct cm_state *s, unsigned rate)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&s->lock, flags);
-	set_spdifout_unlocked(s, rate);
-	set_ac3_unlocked(s,rate);
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
-static int trans_ac3(struct cm_state *s, void *dest, const char *source, int size)
+static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size)
 {
 	int   i = size / 2;
-	int err;
 	unsigned long data;
 	unsigned long *dst = (unsigned long *) dest;
 	unsigned short *src = (unsigned short *)source;
 
 	do {
-		if ((err = __get_user(data, src++)))
-			return err;
+		data = (unsigned long) *src++;
 		data <<= 12;			// ok for 16-bit data
 		if (s->spdif_counter == 2 || s->spdif_counter == 3)
 			data |= 0x40000000;	// indicate AC-3 raw data
@@ -607,30 +677,6 @@ static int trans_ac3(struct cm_state *s,
 		if (s->spdif_counter == 384)
 			s->spdif_counter = 0;
 	} while (--i);
-
-	return 0;
-}
-
-static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate)
-{
-	unsigned char freq = 4;
-	int	i;
-
-	if (rate > 48000)
-		rate = 48000;
-	if (rate < 8000)
-		rate = 8000;
-	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) {
-		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) {
-			rate = rate_lookup[i].rate;
-			freq = rate_lookup[i].freq;
-			break;
-	    	}
-	}
-	s->rateadc = rate;
-	freq <<= 2;
-
-	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq);
 }
 
 static void set_adc_rate(struct cm_state *s, unsigned rate)
@@ -652,7 +698,6 @@ static void set_adc_rate(struct cm_state
 	}
 	s->rateadc = rate;
 	freq <<= 2;
-
 	spin_lock_irqsave(&s->lock, flags);
 	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq);
 	spin_unlock_irqrestore(&s->lock, flags);
@@ -677,17 +722,13 @@ static void set_dac_rate(struct cm_state
 	}
 	s->ratedac = rate;
 	freq <<= 5;
-
 	spin_lock_irqsave(&s->lock, flags);
 	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq);
-
-
+	spin_unlock_irqrestore(&s->lock, flags);
 	if (s->curr_channels <=  2)
-		set_spdifout_unlocked(s, rate);
+		set_spdifout(s, rate);
 	if (s->status & DO_DUAL_DAC)
-		set_adc_rate_unlocked(s, rate);
-
-	spin_unlock_irqrestore(&s->lock, flags);
+		set_dac1_rate(s, rate);
 }
 
 /* --------------------------------------------------------------------- */
@@ -695,7 +736,6 @@ static inline void reset_adc(struct cm_s
 {
 	/* reset bus master */
 	outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
-	udelay(10);
 	outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 }
 
@@ -720,7 +760,7 @@ static inline void pause_dac(struct cm_s
 		pause_adc(s);
 }
 
-static inline void disable_adc(struct cm_state *s)
+extern inline void disable_adc(struct cm_state *s)
 {
 	/* disable channel */
 	s->enable &= ~CM_ENABLE_CH0;
@@ -728,7 +768,7 @@ static inline void disable_adc(struct cm
 	reset_adc(s);
 }
 
-static inline void disable_dac(struct cm_state *s)
+extern inline void disable_dac(struct cm_state *s)
 {
 	/* disable channel */
 	s->enable &= ~CM_ENABLE_CH1;
@@ -738,7 +778,7 @@ static inline void disable_dac(struct cm
 		disable_adc(s);
 }
 
-static inline void enable_adc(struct cm_state *s)
+extern inline void enable_adc(struct cm_state *s)
 {
 	if (!(s->enable & CM_ENABLE_CH0)) {
 		/* enable channel */
@@ -748,7 +788,7 @@ static inline void enable_adc(struct cm_
 	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0);
 }
 
-static inline void enable_dac_unlocked(struct cm_state *s)
+extern inline void enable_dac(struct cm_state *s)
 {
 	if (!(s->enable & CM_ENABLE_CH1)) {
 		/* enable channel */
@@ -756,105 +796,78 @@ static inline void enable_dac_unlocked(s
 		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	}
 	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0);
-
 	if (s->status & DO_DUAL_DAC)
 		enable_adc(s);
 }
 
-static inline void enable_dac(struct cm_state *s)
+extern inline void stop_adc(struct cm_state *s)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	enable_dac_unlocked(s);
-	spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static inline void stop_adc_unlocked(struct cm_state *s)
-{
 	if (s->enable & CM_ENABLE_CH0) {
 		/* disable interrupt */
 		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0);
 		disable_adc(s);
 	}
+	spin_unlock_irqrestore(&s->lock, flags);
 }
 
-static inline void stop_adc(struct cm_state *s)
+extern inline void stop_dac(struct cm_state *s)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	stop_adc_unlocked(s);
-	spin_unlock_irqrestore(&s->lock, flags);
-
-}
-
-static inline void stop_dac_unlocked(struct cm_state *s)
-{
 	if (s->enable & CM_ENABLE_CH1) {
 		/* disable interrupt */
 		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0);
 		disable_dac(s);
 	}
+	spin_unlock_irqrestore(&s->lock, flags);
 	if (s->status & DO_DUAL_DAC)
-		stop_adc_unlocked(s);
+		stop_dac1(s);
 }
 
-static inline void stop_dac(struct cm_state *s)
+static void start_adc(struct cm_state *s)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	stop_dac_unlocked(s);
-	spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void start_adc_unlocked(struct cm_state *s)
-{
 	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
 	    && s->dma_adc.ready) {
 		/* enable interrupt */
 		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1);
 		enable_adc(s);
 	}
-}
+	spin_unlock_irqrestore(&s->lock, flags);
+}
 
-static void start_adc(struct cm_state *s)
+static void start_dac1(struct cm_state *s)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	start_adc_unlocked(s);
-	spin_unlock_irqrestore(&s->lock, flags);
-}	
-
-static void start_dac1_unlocked(struct cm_state *s)
-{
 	if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) {
 		/* enable interrupt */
 //		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1);
- 		enable_dac_unlocked(s);
-	}
-}
-
-static void start_dac_unlocked(struct cm_state *s)
-{
-	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
-		/* enable interrupt */
-		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2);
-		enable_dac_unlocked(s);
+		enable_dac(s);
 	}
-		if (s->status & DO_DUAL_DAC)
-			start_dac1_unlocked(s);
-}
+	spin_unlock_irqrestore(&s->lock, flags);
+}
 
 static void start_dac(struct cm_state *s)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	start_dac_unlocked(s);
+	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		/* enable interrupt */
+		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2);
+		enable_dac(s);
+	}
 	spin_unlock_irqrestore(&s->lock, flags);
+	if (s->status & DO_DUAL_DAC)
+		start_dac1(s);
 }	
 
 static int prog_dmabuf(struct cm_state *s, unsigned rec);
@@ -862,11 +875,11 @@ static int prog_dmabuf(struct cm_state *
 static int set_dac_channels(struct cm_state *s, int channels)
 {
 	unsigned long flags;
-	spin_lock_irqsave(&s->lock, flags);
 
+	spin_lock_irqsave(&s->lock, flags);
 	if ((channels > 2) && (channels <= s->max_channels)
 	 && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) {
-	    set_spdifout_unlocked(s, 0);
+	    set_spdifout(s, 0);
 	    if (s->capability & CAN_MULTI_CH_HW) {
 		// NXCHG
 		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80);
@@ -886,12 +899,8 @@ static int set_dac_channels(struct cm_st
 		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0);
 		s->status |= DO_DUAL_DAC;
 		// prepare secondary buffer
-
-		spin_unlock_irqrestore(&s->lock, flags);
 		ret = prog_dmabuf(s, 1);
 		if (ret) return ret;
-		spin_lock_irqsave(&s->lock, flags);
-
 		// copy the hw state
 		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
 		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
@@ -900,12 +909,10 @@ static int set_dac_channels(struct cm_st
 		fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
 		fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
 		fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
-		
-		set_fmt_unlocked(s, fmtm, fmts);
-		set_adc_rate_unlocked(s, s->ratedac);
-
+		set_fmt(s, fmtm, fmts);
+		set_dac1_rate(s, s->ratedac);
 	    }
-
+	    // N4SPK3D, disable 4 speaker mode (analog duplicate)
 	    if (s->speakers > 2)
 		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0);
 	    s->curr_channels = channels;
@@ -923,7 +930,6 @@ static int set_dac_channels(struct cm_st
 	    s->status &= ~DO_MULTI_CH;
 	    s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1;
 	}
-
 	spin_unlock_irqrestore(&s->lock, flags);
 	return s->curr_channels;
 }
@@ -935,19 +941,30 @@ static int set_dac_channels(struct cm_st
 
 static void dealloc_dmabuf(struct dmabuf *db)
 {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	struct page *pstart, *pend;
+#else
+	unsigned long map, mapend;
+#endif
 	
 	if (db->rawbuf) {
 		/* undo marking the pages as reserved */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
 		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)
-			ClearPageReserved(pstart);
+			mem_map_unreserve(pstart);
+#else
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);
+#endif
 		free_pages((unsigned long)db->rawbuf, db->buforder);
 	}
 	db->rawbuf = NULL;
 	db->mapped = db->ready = 0;
 }
 
+
 /* Ch1 is used for playback, Ch0 is used for recording */
 
 static int prog_dmabuf(struct cm_state *s, unsigned rec)
@@ -957,10 +974,15 @@ static int prog_dmabuf(struct cm_state *
 	int order;
 	unsigned bytepersec;
 	unsigned bufs;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	struct page *pstart, *pend;
+#else
+	unsigned long map, mapend;
+#endif
 	unsigned char fmt;
 	unsigned long flags;
 
+	spin_lock_irqsave(&s->lock, flags);
 	fmt = s->fmt;
 	if (rec) {
 		stop_adc(s);
@@ -969,7 +991,7 @@ static int prog_dmabuf(struct cm_state *
 		stop_dac(s);
 		fmt >>= CM_CFMT_DACSHIFT;
 	}
-
+	spin_unlock_irqrestore(&s->lock, flags);
 	fmt &= CM_CFMT_MASK;
 	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
 	if (!db->rawbuf) {
@@ -982,15 +1004,21 @@ static int prog_dmabuf(struct cm_state *
 		db->buforder = order;
 		db->rawphys = virt_to_bus(db->rawbuf);
 		if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
-			printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n",
 			       (long) db->rawphys, PAGE_SIZE << db->buforder);
 		if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
-			printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n",
 			       (long) db->rawphys, PAGE_SIZE << db->buforder);
 		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
 		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)
-			SetPageReserved(pstart);
+			mem_map_reserve(pstart);
+#else
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			set_bit(PG_reserved, &mem_map[map].flags);
+#endif
 	}
 	bytepersec = rate << sample_shift[fmt];
 	bufs = PAGE_SIZE << db->buforder;
@@ -1013,6 +1041,13 @@ static int prog_dmabuf(struct cm_state *
 	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
 		db->numfrag = db->ossmaxfrags;
  	/* to make fragsize >= 4096 */
+	if (s->modem) {
+	 	while (db->fragsize < 4096 && db->numfrag >= 4) {
+ 			db->fragsize *= 2;
+ 			db->fragshift++;
+ 			db->numfrag /= 2;
+ 		}
+	}
 	db->fragsamples = db->fragsize >> sample_shift[fmt];
 	db->dmasize = db->numfrag << db->fragshift;
 	db->dmasamples = db->dmasize >> sample_shift[fmt];
@@ -1035,7 +1070,7 @@ static int prog_dmabuf(struct cm_state *
 	return 0;
 }
 
-static inline void clear_advance(struct cm_state *s)
+extern __inline__ void clear_advance(struct cm_state *s)
 {
 	unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80;
 	unsigned char *buf = s->dma_dac.rawbuf;
@@ -1159,7 +1194,7 @@ static void cm_handle_midi(struct cm_sta
 }
 #endif
 
-static irqreturn_t cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
         struct cm_state *s = (struct cm_state *)dev_id;
 	unsigned int intsrc, intstat;
@@ -1168,7 +1203,7 @@ static irqreturn_t cm_interrupt(int irq,
 	/* fastpath out, to ease interrupt sharing */
 	intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS);
 	if (!(intsrc & 0x80000000))
-		return IRQ_NONE;
+		return;
 	spin_lock(&s->lock);
 	intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 	/* acknowledge interrupt */
@@ -1183,7 +1218,6 @@ static irqreturn_t cm_interrupt(int irq,
 	cm_handle_midi(s);
 #endif
 	spin_unlock(&s->lock);
-	return IRQ_HANDLED;
 }
 
 #ifdef CONFIG_SOUND_CMPCI_MIDI
@@ -1202,7 +1236,7 @@ static void cm_midi_timer(unsigned long 
 
 /* --------------------------------------------------------------------- */
 
-static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";
+static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n";
 
 #ifdef CONFIG_SOUND_CMPCI	/* support multiple chips */
 #define VALIDATE_STATE(s)
@@ -1236,10 +1270,62 @@ static const struct {
 	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      DSP_MIX_MICVOLIDX,      MT_5MUTEMONO, 0x01, 0x01 },
 	[SOUND_MIXER_SYNTH]  = { DSP_MIX_FMVOLIDX_L,  	 DSP_MIX_FMVOLIDX_R,     MT_5MUTE,     0x40, 0x00 },
 	[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE,     0x00, 0x00 },
-	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 },
-	[SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX,	 DSP_MIX_SPKRVOLIDX,	 MT_5MUTEMONO, 0x01, 0x01 }
+	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 }
 };
 
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+static int return_mixval(struct cm_state *s, unsigned i, int *arg)
+{
+	unsigned long flags;
+	unsigned char l, r, rl, rr;
+
+	spin_lock_irqsave(&s->lock, flags);
+	l = rdmixer(s, mixtable[i].left);
+	r = rdmixer(s, mixtable[i].right);
+	spin_unlock_irqrestore(&s->lock, flags);
+	switch (mixtable[i].type) {
+	case MT_4:
+		r &= 0xf;
+		l &= 0xf;
+		rl = 10 + 6 * (l & 15);
+		rr = 10 + 6 * (r & 15);
+		break;
+
+	case MT_4MUTEMONO:
+		rl = 55 - 3 * (l & 15);
+		if (r & 0x10)
+			rl += 45;
+		rr = rl;
+		r = l;
+		break;
+
+	case MT_5MUTEMONO:
+		r = l;
+		rl = 100 - 3 * ((l >> 3) & 31);
+		rr = rl;
+		break;
+
+	case MT_5MUTE:
+	default:
+		rl = 100 - 3 * ((l >> 3) & 31);
+		rr = 100 - 3 * ((r >> 3) & 31);
+		break;
+
+	case MT_6MUTE:
+		rl = 100 - 3 * (l & 63) / 2;
+		rr = 100 - 3 * (r & 63) / 2;
+		break;
+	}
+	if (l & 0x80)
+		rl = 0;
+	if (r & 0x80)
+		rr = 0;
+	return put_user((rr << 8) | rl, arg);
+}
+
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
 static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = 
 {
 	[SOUND_MIXER_CD]     = 1,
@@ -1247,15 +1333,19 @@ static const unsigned char volidx[SOUND_
 	[SOUND_MIXER_MIC]    = 3,
 	[SOUND_MIXER_SYNTH]  = 4,
 	[SOUND_MIXER_VOLUME] = 5,
-	[SOUND_MIXER_PCM]    = 6,
-	[SOUND_MIXER_SPEAKER]= 7
+	[SOUND_MIXER_PCM]    = 6
 };
 
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
 static unsigned mixer_recmask(struct cm_state *s)
 {
+	unsigned long flags;
 	int i, j, k;
 
+	spin_lock_irqsave(&s->lock, flags);
 	j = rdmixer(s, DSP_MIX_ADCMIXIDX_L);
+	spin_unlock_irqrestore(&s->lock, flags);
 	j &= 0x7f;
 	for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 		if (j & mixtable[i].rec)
@@ -1272,9 +1362,8 @@ static int mixer_ioctl(struct cm_state *
 	VALIDATE_STATE(s);
         if (cmd == SOUND_MIXER_INFO) {
 		mixer_info info;
-		memset(&info, 0, sizeof(info));
-		strlcpy(info.id, "cmpci", sizeof(info.id));
-		strlcpy(info.name, "C-Media PCI", sizeof(info.name));
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media PCI", sizeof(info.name));
 		info.modify_counter = s->mix.modcnt;
 		if (copy_to_user((void *)arg, &info, sizeof(info)))
 			return -EFAULT;
@@ -1282,9 +1371,8 @@ static int mixer_ioctl(struct cm_state *
 	}
 	if (cmd == SOUND_OLD_MIXER_INFO) {
 		_old_mixer_info info;
-		memset(&info, 0, sizeof(info));
-		strlcpy(info.id, "cmpci", sizeof(info.id));
-		strlcpy(info.name, "C-Media cmpci", sizeof(info.name));
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media cmpci", sizeof(info.name));
 		if (copy_to_user((void *)arg, &info, sizeof(info)))
 			return -EFAULT;
 		return 0;
@@ -1332,9 +1420,13 @@ static int mixer_ioctl(struct cm_state *
 			i = _IOC_NR(cmd);
                         if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
                                 return -EINVAL;
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+			return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
 			if (!volidx[i])
 				return -EINVAL;
 			return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
 		}
 	}
         if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
@@ -1342,9 +1434,13 @@ static int mixer_ioctl(struct cm_state *
 	s->mix.modcnt++;
 	switch (_IOC_NR(cmd)) {
 	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-		i = generic_hweight32(val);
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
+		i = hweight32(val);
 		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
 			if (!(val & (1 << i)))
 				continue;
@@ -1361,8 +1457,12 @@ static int mixer_ioctl(struct cm_state *
 		return 0;
 
 	case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
 			if (!(val & (1 << i)))
 				continue;
@@ -1381,8 +1481,12 @@ static int mixer_ioctl(struct cm_state *
 		i = _IOC_NR(cmd);
 		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
 			return -EINVAL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		l = val & 0xff;
 		r = (val >> 8) & 0xff;
 		if (l > 100)
@@ -1436,19 +1540,29 @@ static int mixer_ioctl(struct cm_state *
 			break;
 		}
 		spin_unlock_irqrestore(&s->lock, flags);
-
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
 		if (!volidx[i])
 			return -EINVAL;
 		s->mix.vol[volidx[i]-1] = val;
 		return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
 	}
 }
 
 /* --------------------------------------------------------------------- */
 
+static loff_t cm_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
 static int cm_open_mixdev(struct inode *inode, struct file *file)
 {
-	int minor = iminor(inode);
+	int minor = MINOR(inode->i_rdev);
 	struct cm_state *s = devs;
 
 	while (s && s->dev_mixer != minor)
@@ -1457,6 +1571,9 @@ static int cm_open_mixdev(struct inode *
 		return -ENODEV;
        	VALIDATE_STATE(s);
 	file->private_data = s;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+	MOD_INC_USE_COUNT;
+#endif
 	return 0;
 }
 
@@ -1467,6 +1584,9 @@ static int cm_release_mixdev(struct inod
 	s = file->private_data;
 	
 	VALIDATE_STATE(s);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+	MOD_DEC_USE_COUNT;
+#endif
 	return 0;
 }
 
@@ -1476,25 +1596,140 @@ static int cm_ioctl_mixdev(struct inode 
 }
 
 static /*const*/ struct file_operations cm_mixer_fops = {
-	.owner	 = THIS_MODULE,
-	.llseek	 = no_llseek,
-	.ioctl	 = cm_ioctl_mixdev,
-	.open	 = cm_open_mixdev,
-	.release = cm_release_mixdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner:		THIS_MODULE,
+#endif
+	llseek:		cm_llseek,
+	ioctl:		cm_ioctl_mixdev,
+	open:		cm_open_mixdev,
+	release:	cm_release_mixdev,
 };
 
+int IntrOpen(void)
+{
+    struct cm_state *s = devs;
+    unsigned char fmtm = ~0, fmts = 0;
+
+    /* Locate the /dev/dsp file descriptor */
+    while (s && ((s->dev_audio ^ 3) & ~0xf))
+	s = s->next;
+
+    devaudio = s;
+    down(&s->open_sem);
+    if (s->open_mode & FMODE_WRITE) {
+  	up(&s->open_sem);
+	devaudio = NULL;
+	return -EBUSY;
+    }
+
+    if (!s->dma_dac.ready) {
+	set_dac_rate(s, 8000);
+	fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
+	set_fmt(s, fmtm, fmts);
+    	s->modem = 1;
+    }
+
+    s->open_mode |= FMODE_WRITE;
+    up(&s->open_sem);
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+EXPORT_SYMBOL(IntrOpen);
+
+int IntrClose(void)
+{
+    struct cm_state *s = devaudio;
+
+    if (!s)
+	  return -ENODEV;
+    down(&s->open_sem);
+    stop_dac(s);
+#ifndef FIXEDDMA
+    dealloc_dmabuf(&s->dma_dac);
+#endif
+
+    s->open_mode &= ~FMODE_WRITE;
+    s->modem = 0;
+    up(&s->open_sem);
+    wake_up(&s->open_wait);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+    MOD_DEC_USE_COUNT;
+#endif
+    devaudio = NULL;
+    return 0;
+}
+EXPORT_SYMBOL(IntrClose);
+
+int IntrWrite(const char *buffer, int count)
+{
+	struct cm_state *s = devaudio;
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	if (!s)
+		return -ENODEV;
+	VALIDATE_STATE(s);
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+
+	s->dma_dac.ossfragshift = 8;
+	s->dma_dac.ossmaxfrags = 16;
+	s->dma_dac.subdivision = 0;
+
+	while (count > 0) {
+	   	spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac(s);
+			return ret;
+		}
+		if (__copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(IntrWrite);
 
 /* --------------------------------------------------------------------- */
 
 static int drain_dac(struct cm_state *s, int nonblock)
 {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	DECLARE_WAITQUEUE(wait, current);
+#else
+        struct wait_queue wait = { current, NULL };
+#endif
 	unsigned long flags;
 	int count, tmo;
 
 	if (s->dma_dac.mapped || !s->dma_dac.ready)
 		return 0;
-        set_current_state(TASK_INTERRUPTIBLE);
+        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac.wait, &wait);
         for (;;) {
                 spin_lock_irqsave(&s->lock, flags);
@@ -1506,16 +1741,16 @@ static int drain_dac(struct cm_state *s,
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac.wait, &wait);
-                        set_current_state(TASK_RUNNING);
+                        current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
 		tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
 		if (!schedule_timeout(tmo + 1))
-			printk(KERN_DEBUG "cmpci: dma timed out??\n");
+			printk(KERN_DEBUG "cm: dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac.wait, &wait);
-        set_current_state(TASK_RUNNING);
+        current->state = TASK_RUNNING;
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1541,7 +1776,11 @@ static ssize_t cm_read(struct file *file
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
 	ret = 0;
-
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   cm_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		swptr = s->dma_adc.swptr;
@@ -1556,11 +1795,11 @@ static ssize_t cm_read(struct file *file
 			if (file->f_flags & O_NONBLOCK)
 				return ret ? ret : -EAGAIN;
 			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
-				printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				printk(KERN_DEBUG "cm: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
 				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
 				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				stop_adc(s);
 				spin_lock_irqsave(&s->lock, flags);
-				stop_adc_unlocked(s);
 				set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples);
 				/* program sample counts */
 				set_countadc(s, s->dma_adc.fragsamples);
@@ -1577,11 +1816,11 @@ static ssize_t cm_read(struct file *file
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_adc.swptr = swptr;
 		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
-		start_adc_unlocked(s);
-		spin_unlock_irqrestore(&s->lock, flags);
+		start_adc(s);
 	}
 	return ret;
 }
@@ -1612,7 +1851,11 @@ static ssize_t cm_write(struct file *fil
 			return -EFAULT;
 	}
 	ret = 0;
-
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   cm_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac.count < 0) {
@@ -1643,11 +1886,11 @@ static ssize_t cm_write(struct file *fil
 			if (file->f_flags & O_NONBLOCK)
 				return ret ? ret : -EAGAIN;
 			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
-				printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				printk(KERN_DEBUG "cm: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
 				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count,
 				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				stop_dac(s);
 				spin_lock_irqsave(&s->lock, flags);
-				stop_dac_unlocked(s);
 				set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples);
 				/* program sample counts */
 				set_countdac(s, s->dma_dac.fragsamples);
@@ -1663,16 +1906,13 @@ static ssize_t cm_write(struct file *fil
 			continue;
 		}
 		if (s->status & DO_AC3_SW) {
-			int err;
-
 			// clip exceeded data, caught by 033 and 037
 			if (swptr + 2 * cnt > s->dma_dac.dmasize)
 				cnt = (s->dma_dac.dmasize - swptr) / 2;
-			if ((err = trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt)))
-				return err;
+			trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt);
 			swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize;
 		} else if (s->status & DO_DUAL_DAC) {
-			int	i, err;
+			int	i;
 			unsigned long *src, *dst0, *dst1;
 
 			src = (unsigned long *) buffer;
@@ -1680,10 +1920,8 @@ static ssize_t cm_write(struct file *fil
 			dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr);
 			// copy left/right sample at one time
 			for (i = 0; i <= cnt / 4; i++) {
-				if ((err = __get_user(*dst0++, src++)))
-					return err;
-				if ((err = __get_user(*dst1++, src++)))
-					return err;
+				*dst0++ = *src++;
+				*dst1++ = *src++;
 			}
 			swptr = (swptr + cnt) % s->dma_dac.dmasize;
 		} else {
@@ -1749,7 +1987,9 @@ static int cm_mmap(struct file *file, st
 	unsigned long size;
 
 	VALIDATE_STATE(s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	lock_kernel();
+#endif
 	if (vma->vm_flags & VM_WRITE) {
 		if ((ret = prog_dmabuf(s, 0)) != 0)
 			goto out;
@@ -1761,18 +2001,24 @@ static int cm_mmap(struct file *file, st
 	} else
 		goto out;
 	ret = -EINVAL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	if (vma->vm_pgoff != 0)
+#else
+	if (vma->vm_offset != 0)
+#endif
 		goto out;
 	size = vma->vm_end - vma->vm_start;
 	if (size > (PAGE_SIZE << db->buforder))
 		goto out;
 	ret = -EINVAL;
-	if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
 		goto out;
 	db->mapped = 1;
 	ret = 0;
 out:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	unlock_kernel();
+#endif
 	return ret;
 }
 
@@ -1806,28 +2052,30 @@ static int cm_ioctl(struct inode *inode,
         case SNDCTL_DSP_RESET:
 		if (file->f_mode & FMODE_WRITE) {
 			stop_dac(s);
-			synchronize_irq(s->irq);
+			synchronize_irq();
 			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
 			if (s->status & DO_DUAL_DAC)
 				s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
 		}
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(s);
-			synchronize_irq(s->irq);
+			synchronize_irq();
 			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
 		}
 		return 0;
 
         case SNDCTL_DSP_SPEED:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+                get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (val >= 0) {
 			if (file->f_mode & FMODE_READ) {
-			 	spin_lock_irqsave(&s->lock, flags);
-				stop_adc_unlocked(s);
+				stop_adc(s);
 				s->dma_adc.ready = 0;
-				set_adc_rate_unlocked(s, val);
-				spin_unlock_irqrestore(&s->lock, flags);
+				set_adc_rate(s, val);
 			}
 			if (file->f_mode & FMODE_WRITE) {
 				stop_dac(s);
@@ -1840,8 +2088,12 @@ static int cm_ioctl(struct inode *inode,
 		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
 
         case SNDCTL_DSP_STEREO:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+                get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		fmtd = 0;
 		fmtm = ~0;
 		if (file->f_mode & FMODE_READ) {
@@ -1871,8 +2123,12 @@ static int cm_ioctl(struct inode *inode,
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (val != 0) {
 			fmtd = 0;
 			fmtm = ~0;
@@ -1913,8 +2169,12 @@ static int cm_ioctl(struct inode *inode,
                 return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg);
 		
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (val != AFMT_QUERY) {
 			fmtd = 0;
 			fmtm = ~0;
@@ -1971,8 +2231,12 @@ static int cm_ioctl(struct inode *inode,
 		return put_user(val, (int *)arg);
 
 	case SNDCTL_DSP_SETTRIGGER:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+		get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (file->f_mode & FMODE_READ) {
 			if (val & PCM_ENABLE_INPUT) {
 				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
@@ -2047,9 +2311,7 @@ static int cm_ioctl(struct inode *inode,
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
 		spin_unlock_irqrestore(&s->lock, flags);
-		if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo)))
-			return -EFAULT;
-		return 0;
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
 
         case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
@@ -2066,9 +2328,7 @@ static int cm_ioctl(struct inode *inode,
 				s->dma_adc.count &= s->dma_adc.fragsize-1;
 		}
 		spin_unlock_irqrestore(&s->lock, flags);
-		if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo)))
-			return -EFAULT;
-		return 0;
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
 
         case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
@@ -2086,8 +2346,12 @@ static int cm_ioctl(struct inode *inode,
 		return put_user(s->dma_adc.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+                get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (file->f_mode & FMODE_READ) {
 			s->dma_adc.ossfragshift = val & 0xffff;
 			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -2118,8 +2382,12 @@ static int cm_ioctl(struct inode *inode,
 		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
 		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
 			return -EINVAL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
+#else
+                get_user_ret(val, (int *)arg, -EFAULT);
+#endif
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		if (file->f_mode & FMODE_READ)
@@ -2209,7 +2477,7 @@ static int cm_ioctl(struct inode *inode,
 
 static int cm_open(struct inode *inode, struct file *file)
 {
-	int minor = iminor(inode);
+	int minor = MINOR(inode->i_rdev);
 	struct cm_state *s = devs;
 	unsigned char fmtm = ~0, fmts = 0;
 
@@ -2255,6 +2523,9 @@ static int cm_open(struct inode *inode, 
 	set_fmt(s, fmtm, fmts);
 	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 	up(&s->open_sem);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+	MOD_INC_USE_COUNT;
+#endif
 	return 0;
 }
 
@@ -2263,17 +2534,19 @@ static int cm_release(struct inode *inod
 	struct cm_state *s = (struct cm_state *)file->private_data;
 
 	VALIDATE_STATE(s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	lock_kernel();
+#endif
 	if (file->f_mode & FMODE_WRITE)
 		drain_dac(s, file->f_flags & O_NONBLOCK);
 	down(&s->open_sem);
 	if (file->f_mode & FMODE_WRITE) {
 		stop_dac(s);
-
+#ifndef FIXEDDMA
 		dealloc_dmabuf(&s->dma_dac);
 		if (s->status & DO_DUAL_DAC)
 			dealloc_dmabuf(&s->dma_adc);
-
+#endif
 		if (s->status & DO_MULTI_CH)
 			set_dac_channels(s, 0);
 		if (s->status & DO_AC3)
@@ -2283,25 +2556,33 @@ static int cm_release(struct inode *inod
 	}
 	if (file->f_mode & FMODE_READ) {
 		stop_adc(s);
+#ifndef FIXEDDMA
 		dealloc_dmabuf(&s->dma_adc);
+#endif
 	}
-	s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	unlock_kernel();
+#else
+	MOD_DEC_USE_COUNT;
+#endif
 	return 0;
 }
 
 static /*const*/ struct file_operations cm_audio_fops = {
-	.owner	 = THIS_MODULE,
-	.llseek	 = no_llseek,
-	.read	 = cm_read,
-	.write	 = cm_write,
-	.poll	 = cm_poll,
-	.ioctl	 = cm_ioctl,
-	.mmap	 = cm_mmap,
-	.open	 = cm_open,
-	.release = cm_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner:		THIS_MODULE,
+#endif
+	llseek:		cm_llseek,
+	read:		cm_read,
+	write:		cm_write,
+	poll:		cm_poll,
+	ioctl:		cm_ioctl,
+	mmap:		cm_mmap,
+	open:		cm_open,
+	release:	cm_release,
 };
 
 #ifdef CONFIG_SOUND_CMPCI_MIDI
@@ -2310,7 +2591,9 @@ static /*const*/ struct file_operations 
 static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct cm_state *s = (struct cm_state *)file->private_data;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	DECLARE_WAITQUEUE(wait, current);
+#endif
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2322,7 +2605,9 @@ static ssize_t cm_midi_read(struct file 
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
 	ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	add_wait_queue(&s->midi.iwait, &wait);
+#endif
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.ird;
@@ -2334,6 +2619,7 @@ static ssize_t cm_midi_read(struct file 
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 			{
 				if (!ret)
 					ret = -EAGAIN;
@@ -2347,14 +2633,24 @@ static ssize_t cm_midi_read(struct file 
 					ret = -ERESTARTSYS;
 				break;
 			}
+#else
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+#endif
 			continue;
 		}
 		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		{
 			if (!ret)
 				ret = -EFAULT;
 			break;
 		}
+#else
+			return ret ? ret : -EFAULT;
+#endif
 		ptr = (ptr + cnt) % MIDIINBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.ird = ptr;
@@ -2363,17 +2659,23 @@ static ssize_t cm_midi_read(struct file 
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		break;
+#endif
 	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.iwait, &wait);
+#endif
 	return ret;
 }
 
 static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct cm_state *s = (struct cm_state *)file->private_data;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	DECLARE_WAITQUEUE(wait, current);
+#endif
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2384,10 +2686,14 @@ static ssize_t cm_midi_write(struct file
 		return -ESPIPE;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	if (count == 0)
 		return 0;
+#endif
 	ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	add_wait_queue(&s->midi.owait, &wait);
+#endif
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.owr;
@@ -2401,6 +2707,7 @@ static ssize_t cm_midi_write(struct file
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 			{
 				if (!ret)
 					ret = -EAGAIN;
@@ -2413,14 +2720,24 @@ static ssize_t cm_midi_write(struct file
 					ret = -ERESTARTSYS;
 				break;
 			}
+#else
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+#endif
 			continue;
 		}
 		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		{
 			if (!ret)
 				ret = -EFAULT;
 			break;
 		}
+#else
+			return ret ? ret : -EFAULT;
+#endif
 		ptr = (ptr + cnt) % MIDIOUTBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.owr = ptr;
@@ -2433,8 +2750,10 @@ static ssize_t cm_midi_write(struct file
 		cm_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.owait, &wait);
+#endif
 	return ret;
 }
 
@@ -2464,7 +2783,7 @@ static unsigned int cm_midi_poll(struct 
 
 static int cm_midi_open(struct inode *inode, struct file *file)
 {
-	int minor = iminor(inode);
+	int minor = MINOR(inode->i_rdev);
 	struct cm_state *s = devs;
 	unsigned long flags;
 
@@ -2515,21 +2834,32 @@ static int cm_midi_open(struct inode *in
 	spin_unlock_irqrestore(&s->lock, flags);
 	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
 	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
 static int cm_midi_release(struct inode *inode, struct file *file)
 {
 	struct cm_state *s = (struct cm_state *)file->private_data;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 	DECLARE_WAITQUEUE(wait, current);
+#else
+        struct wait_queue wait = { current, NULL };
+#endif
 	unsigned long flags;
 	unsigned count, tmo;
 
 	VALIDATE_STATE(s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	lock_kernel();
+#endif
 
 	if (file->f_mode & FMODE_WRITE) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		__set_current_state(TASK_INTERRUPTIBLE);
+#else
+		current->state = TASK_INTERRUPTIBLE;
+#endif
 		add_wait_queue(&s->midi.owait, &wait);
 		for (;;) {
 			spin_lock_irqsave(&s->lock, flags);
@@ -2541,19 +2871,26 @@ static int cm_midi_release(struct inode 
 				break;
 			if (file->f_flags & O_NONBLOCK) {
 				remove_wait_queue(&s->midi.owait, &wait);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 				set_current_state(TASK_RUNNING);
-				unlock_kernel();
+#else
+				current->state = TASK_RUNNING;
+#endif
 				return -EBUSY;
 			}
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
-				printk(KERN_DEBUG "cmpci: midi timed out??\n");
+				printk(KERN_DEBUG "cm: midi timed out??\n");
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		set_current_state(TASK_RUNNING);
+#else
+		current->state = TASK_RUNNING;
+#endif
 	}
 	down(&s->open_sem);
-	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		del_timer(&s->midi.timer);		
@@ -2566,18 +2903,24 @@ static int cm_midi_release(struct inode 
 	spin_unlock_irqrestore(&s->lock, flags);
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	unlock_kernel();
+#else
+	MOD_DEC_USE_COUNT;
+#endif
 	return 0;
 }
 
 static /*const*/ struct file_operations cm_midi_fops = {
-	.owner	 = THIS_MODULE,
-	.llseek	 = no_llseek,
-	.read	 = cm_midi_read,
-	.write	 = cm_midi_write,
-	.poll	 = cm_midi_poll,
-	.open	 = cm_midi_open,
-	.release = cm_midi_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner:		THIS_MODULE,
+#endif
+	llseek:		cm_llseek,
+	read:		cm_midi_read,
+	write:		cm_midi_write,
+	poll:		cm_midi_poll,
+	open:		cm_midi_open,
+	release:	cm_midi_release,
 };
 #endif
 
@@ -2675,13 +3018,15 @@ static int cm_dmfm_ioctl(struct inode *i
 		outb(5, s->iosynth+2);
 		outb(arg & 1, s->iosynth+3);
 		return 0;
+
+	default:
+		return -EINVAL;
 	}
-	return -EINVAL;
 }
 
 static int cm_dmfm_open(struct inode *inode, struct file *file)
 {
-	int minor = iminor(inode);
+	int minor = MINOR(inode->i_rdev);
 	struct cm_state *s = devs;
 
 	while (s && s->dev_dmfm != minor)
@@ -2712,6 +3057,7 @@ static int cm_dmfm_open(struct inode *in
 	outb(1, s->iosynth+3);  /* enable OPL3 */
 	s->open_mode |= FMODE_DMFM;
 	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -2721,7 +3067,9 @@ static int cm_dmfm_release(struct inode 
 	unsigned int regb;
 
 	VALIDATE_STATE(s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	lock_kernel();
+#endif
 	down(&s->open_sem);
 	s->open_mode &= ~FMODE_DMFM;
 	for (regb = 0xb0; regb < 0xb9; regb++) {
@@ -2732,20 +3080,37 @@ static int cm_dmfm_release(struct inode 
 	}
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 	unlock_kernel();
+#else
+	MOD_DEC_USE_COUNT;
+#endif
 	return 0;
 }
 
 static /*const*/ struct file_operations cm_dmfm_fops = {
-	.owner	 = THIS_MODULE,
-	.llseek	 = no_llseek,
-	.ioctl	 = cm_dmfm_ioctl,
-	.open	 = cm_dmfm_open,
-	.release = cm_dmfm_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner:		THIS_MODULE,
+#endif
+	llseek:		cm_llseek,
+	ioctl:		cm_dmfm_ioctl,
+	open:		cm_dmfm_open,
+	release:	cm_dmfm_release,
 };
 #endif /* CONFIG_SOUND_CMPCI_FM */
 
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
 
+#if 0
+static int reverb[NR_DEVICE] = { 0, };
+
+static int wavetable[NR_DEVICE] = { 0, };
+#endif
+
+/* --------------------------------------------------------------------- */
 
 static struct initvol {
 	int mixch;
@@ -2808,24 +3173,24 @@ static int query_chip(struct cm_state *s
 }
 
 #ifdef CONFIG_SOUND_CMPCI_MIDI
-static	int	mpuio = CONFIG_SOUND_CMPCI_MPUIO;
+static	int	mpu_io = CONFIG_SOUND_CMPCI_MPUIO;
 #else
-static	int	mpuio;
+static	int	mpu_io = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_FM
-static	int	fmio = CONFIG_SOUND_CMPCI_FMIO;
+static	int	fm_io = CONFIG_SOUND_CMPCI_FMIO;
 #else
-static	int	fmio;
+static	int	fm_io = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_SPDIFINVERSE
 static	int	spdif_inverse = 1;
 #else
-static	int	spdif_inverse;
+static	int	spdif_inverse = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP
 static	int	spdif_loop = 1;
 #else
-static	int	spdif_loop;
+static	int	spdif_loop = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_SPEAKERS
 static	int	speakers = CONFIG_SOUND_CMPCI_SPEAKERS;
@@ -2835,51 +3200,48 @@ static	int	speakers = 2;
 #ifdef CONFIG_SOUND_CMPCI_LINE_REAR
 static	int	use_line_as_rear = 1;
 #else
-static	int	use_line_as_rear;
+static	int	use_line_as_rear = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_LINE_BASS
 static	int	use_line_as_bass = 1;
 #else
-static	int	use_line_as_bass;
+static	int	use_line_as_bass = 0;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_PCTEL
+static	int	modem = 1;
+#else
+static	int	modem = 0;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_JOYSTICK
 static	int	joystick = 1;
 #else
-static	int	joystick;
+static	int	joystick = 0;
 #endif
-MODULE_PARM(mpuio, "i");
-MODULE_PARM(fmio, "i");
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(fm_io, "i");
 MODULE_PARM(spdif_inverse, "i");
 MODULE_PARM(spdif_loop, "i");
 MODULE_PARM(speakers, "i");
 MODULE_PARM(use_line_as_rear, "i");
 MODULE_PARM(use_line_as_bass, "i");
+MODULE_PARM(modem, "i");
 MODULE_PARM(joystick, "i");
-MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable");
-MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable");
+MODULE_PARM_DESC(mpu_io, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable");
+MODULE_PARM_DESC(fm_io, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable");
 MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal");
 MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly");
 MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect");
 MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out");
 MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center");
+MODULE_PARM_DESC(modem, "(1/0) Use HSP modem, still need PCTel modem driver");
 MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver");
 
-static struct pci_device_id cmpci_pci_tbl[] = {
-	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, 
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- 	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, 
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, 
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-	{ 0 }
-};
-MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl);
-
 void initialize_chip(struct pci_dev *pcidev)
 {
 	struct cm_state *s;
+	mm_segment_t fs;
 	int i, val;
-#if defined(CONFIG_SOUND_CMPCI_MIDI) || defined(CONFIG_SOUND_CMPCI_FM)
+#ifdef CONFIG_SOUND_CMPCI_MIDI
 	unsigned char reg_mask = 0;
 #endif
 	struct {
@@ -2894,13 +3256,14 @@ void initialize_chip(struct pci_dev *pci
 	};
 	char	*devicename = "unknown";
 	{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (pci_enable_device(pcidev))
 			return;
+#endif
 		if (pcidev->irq == 0)
 			return;
-		s = kmalloc(sizeof(*s), GFP_KERNEL);
-		if (!s) {
-			printk(KERN_WARNING "cmpci: out of memory\n");
+		if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) {
+			printk(KERN_WARNING "cm: out of memory\n");
 			return;
 		}
 		/* search device name */
@@ -2912,17 +3275,32 @@ void initialize_chip(struct pci_dev *pci
 			}
 		}
 		memset(s, 0, sizeof(struct cm_state));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
 		init_waitqueue_head(&s->dma_adc.wait);
 		init_waitqueue_head(&s->dma_dac.wait);
 		init_waitqueue_head(&s->open_wait);
 		init_waitqueue_head(&s->midi.iwait);
 		init_waitqueue_head(&s->midi.owait);
 		init_MUTEX(&s->open_sem);
+#else
+		init_waitqueue(&s->dma_adc.wait);
+		init_waitqueue(&s->dma_dac.wait);
+		init_waitqueue(&s->open_wait);
+		init_waitqueue(&s->midi.iwait);
+		init_waitqueue(&s->midi.owait);
+		s->open_sem = MUTEX;
+#endif
 		spin_lock_init(&s->lock);
 		s->magic = CM_MAGIC;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		s->iobase = pci_resource_start(pcidev, 0);
-		s->iosynth = fmio;
-		s->iomidi = mpuio;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+		s->iobase = pcidev->resource[0].start;
+#else
+		s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+#endif
+		s->iosynth = fm_io;
+		s->iomidi = mpu_io;
 		s->status = 0;
 		/* range check */
 		if (speakers < 2)
@@ -2934,16 +3312,27 @@ void initialize_chip(struct pci_dev *pci
 			return;
 		s->irq = pcidev->irq;
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) {
-			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+#else
+		if (check_region(s->iobase, CM_EXTENT_CODEC)) {
+#endif
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
 			goto err_region5;
 		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+		request_region(s->iobase, CM_EXTENT_CODEC, "cmpci");
+#endif
 #ifdef CONFIG_SOUND_CMPCI_MIDI
 		/* disable MPU-401 */
 		maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0);
 		if (s->iomidi) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		    if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) {
-			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+#else
+		    if (check_region(s->iomidi, CM_EXTENT_MIDI)) {
+#endif
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
 			s->iomidi = 0;
 		    } else {
 		        /* set IO based at 0x330 */
@@ -2968,6 +3357,9 @@ void initialize_chip(struct pci_dev *pci
 			/* enable MPU-401 */
 			if (s->iomidi) {
 			    maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+			    request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci MPU");
+#endif
 			}
 		    }
 		}
@@ -2976,8 +3368,12 @@ void initialize_chip(struct pci_dev *pci
 		/* disable FM */
 		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0);
 		if (s->iosynth) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		    if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) {
-			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+#else
+		    if (check_region(s->iosynth, CM_EXTENT_SYNTH)) {
+#endif
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
 			s->iosynth = 0;
 		    } else {
 		        /* set IO based at 0x388 */
@@ -3002,6 +3398,9 @@ void initialize_chip(struct pci_dev *pci
 		        /* enable FM */
 			if (s->iosynth) {
 		            maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+			    request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
+#endif
 			}
 		    }
 		}
@@ -3019,10 +3418,10 @@ void initialize_chip(struct pci_dev *pci
 
 		/* request irq */
 		if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
-			printk(KERN_ERR "cmpci: irq %u in use\n", s->irq);
+			printk(KERN_ERR "cm: irq %u in use\n", s->irq);
 			goto err_irq;
 		}
-		printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
+		printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n",
 		       devicename, s->iobase, s->irq);
 		/* register devices */
 		if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
@@ -3039,6 +3438,8 @@ void initialize_chip(struct pci_dev *pci
 #endif
 		pci_set_master(pcidev);	/* enable bus mastering */
 		/* initialize the chips */
+		fs = get_fs();
+		set_fs(KERNEL_DS);
 		/* set mixer output */
 		frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f);
 		/* set mixer input */
@@ -3050,19 +3451,25 @@ void initialize_chip(struct pci_dev *pci
 		}
 		/* use channel 0 for record, channel 1 for play */
 		maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+		set_fs(fs);
+#endif
 		s->deviceid = pcidev->device;
-
 		if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) {
-
 			/* chip version and hw capability check */
 			s->chip_version = query_chip(s);
-			printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version);
-
+			printk(KERN_INFO "chip version = 0%d\n", s->chip_version);
+			s->modem = modem;
+			if (modem) {
+				/* enable FLINKON and disable FLINKOFF */
+				maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x40, 0x80);
+				printk(KERN_INFO "cm: modem function supported\n");
+			}
 			/* seet SPDIF-in inverse before enable SPDIF loop */
 			if (spdif_inverse) {
 				/* turn on spdif-in inverse */
 				maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1);
-				printk(KERN_INFO "cmpci: Inverse SPDIF-in\n");
+				printk(KERN_INFO "cm: Inverse SPDIF-in\n");
 			} else {
 				/* turn off spdif-ininverse */
 				maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0);
@@ -3073,7 +3480,7 @@ void initialize_chip(struct pci_dev *pci
 				s->status |= DO_SPDIF_LOOP;
 				/* turn on spdif-in to spdif-out */
 				maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80);
-				printk(KERN_INFO "cmpci: Enable SPDIF loop\n");
+				printk(KERN_INFO "cm: Enable SPDIF loop\n");
 			} else {
 				s->status &= ~DO_SPDIF_LOOP;
 				/* turn off spdif-in to spdif-out */
@@ -3097,6 +3504,9 @@ void initialize_chip(struct pci_dev *pci
 			/* 8338 will fall here */
 			s->max_channels = 2;
 		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+		set_fs(fs);
+#endif
 		/* queue it for later freeing */
 		s->next = devs;
 		devs = s;
@@ -3114,7 +3524,7 @@ void initialize_chip(struct pci_dev *pci
 	err_dev2:
 		unregister_sound_dsp(s->dev_audio);
 	err_dev1:
-		printk(KERN_ERR "cmpci: cannot register misc device\n");
+		printk(KERN_ERR "cm: cannot register misc device\n");
 		free_irq(s->irq, s);
 	err_irq:
 #ifdef CONFIG_SOUND_CMPCI_FM
@@ -3125,7 +3535,11 @@ void initialize_chip(struct pci_dev *pci
 #endif
 		release_region(s->iobase, CM_EXTENT_CODEC);
 	err_region5:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		kfree(s);
+#else
+		kfree_s(s, sizeof(struct cm_state));
+#endif
 	}
 	if (!devs) {
 		if (wavetable_mem)
@@ -3135,13 +3549,28 @@ void initialize_chip(struct pci_dev *pci
 	return;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 static int __init init_cmpci(void)
+#else
+#ifdef MODULE
+int __init init_module(void)
+#else
+int __init init_cmpci(void)
+#endif
+#endif
 {
 	struct pci_dev *pcidev = NULL;
 	int index = 0;
 
-	printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n");
-
+#ifdef CONFIG_PCI
+	if (!pci_present())   /* No PCI bus in this machine! */
+#endif
+		return -ENODEV;
+	printk(KERN_INFO "cm: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n");
+#if 0
+	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
+		printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+#endif
 	while (index < NR_DEVICE && (
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { 
 		initialize_chip(pcidev);
@@ -3164,19 +3593,25 @@ static int __init init_cmpci(void)
 
 MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw");
 MODULE_DESCRIPTION("CM8x38 Audio Driver");
-MODULE_LICENSE("GPL");
-
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 static void __exit cleanup_cmpci(void)
+#else
+void cleanup_module(void)
+#endif
 {
 	struct cm_state *s;
 
 	while ((s = devs)) {
 		devs = devs->next;
 		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
-		synchronize_irq(s->irq);
+		synchronize_irq();
 		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
 		free_irq(s->irq, s);
+#ifdef FIXEDDMA
+    		dealloc_dmabuf(&s->dma_dac);
+    		dealloc_dmabuf(&s->dma_adc);
+#endif
 
 		/* reset mixer */
 		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
@@ -3196,12 +3631,18 @@ static void __exit cleanup_cmpci(void)
 #ifdef CONFIG_SOUND_CMPCI_FM
 		unregister_sound_special(s->dev_dmfm);
 #endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		kfree(s);
+#else
+		kfree_s(s, sizeof(struct cm_state));
+#endif
 	}
 	if (wavetable_mem)
 		free_pages(wavetable_mem, 20-PAGE_SHIFT);
-	printk(KERN_INFO "cmpci: unloading\n");
+	printk(KERN_INFO "cm: unloading\n");
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 module_init(init_cmpci);
 module_exit(cleanup_cmpci);
+#endif

_
