patch-2.1.17 linux/drivers/net/soundmodem.c

Next file: linux/drivers/net/tulip.c
Previous file: linux/drivers/net/sm_wss.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.16/linux/drivers/net/soundmodem.c linux/drivers/net/soundmodem.c
@@ -44,6 +44,7 @@
 
 #include <linux/module.h>
 
+#include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -65,85 +66,33 @@
 
 /* --------------------------------------------------------------------- */
 
-#define NR_PORTS 4
-
-#define SM_DEBUG
-
-#define ENABLE_SBC
-#define ENABLE_WSS
-#undef  ENABLE_WSSFDX
-
-#define ENABLE_AFSK1200
-#define ENABLE_FSK9600
+static const char sm_drvname[] = "soundmodem";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.3 compiled " __TIME__ " " __DATE__ "\n";           ;
 
 /* --------------------------------------------------------------------- */
 
-#include "sm_tables.h"
+#define NR_PORTS 4
+
+#define SM_DEBUG
 
 /* --------------------------------------------------------------------- */
 
 static struct device sm_device[NR_PORTS];
 
 static struct {
-	int hardware, mode, iobase, irq, dma, seriobase, pariobase, midiiobase;
+	char *mode;
+	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
 } sm_ports[NR_PORTS] = { 
-{ SM_HARDWARE_INVALID, SM_MODE_INVALID, -1, 0, 0, -1, -1, -1 }, 
+	{ NULL, -1, 0, 0, 0, -1, -1, -1 }, 
 };
 
 /* --------------------------------------------------------------------- */
-/* 
- * the sbc converter's registers 
- */
-#define DSP_RESET(iobase)        (iobase+0x6)
-#define DSP_READ_DATA(iobase)    (iobase+0xa)
-#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
-#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
-#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
-#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
-#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
-#define SBC_EXTENT               16
-
-/* --------------------------------------------------------------------- */
-/*
- * SBC commands
- */
-#define SBC_OUTPUT             0x14
-#define SBC_INPUT              0x24
-#define SBC_HISPEED            0x48
-#define SBC_HI_OUTPUT          0x91 
-#define SBC_HI_INPUT           0x99 
-#define SBC_LO_OUTPUT_AUTOINIT 0x1c
-#define SBC_LO_INPUT_AUTOINIT  0x2c
-#define SBC_HI_OUTPUT_AUTOINIT 0x90 
-#define SBC_HI_INPUT_AUTOINIT  0x98
-#define SBC_IMMED_INT          0xf2
-#define SBC_GET_REVISION       0xe1
-#define ESS_GET_REVISION       0xe7
-#define SBC_SPEAKER_ON         0xd1
-#define SBC_SPEAKER_OFF        0xd3
-#define SBC_DMA_ON             0xd0
-#define SBC_DMA_OFF            0xd4
-#define SBC_SAMPLE_RATE        0x40
-#define SBC_MONO_8BIT          0xa0
-#define SBC_MONO_16BIT         0xa4
-#define SBC_STEREO_8BIT        0xa8
-#define SBC_STEREO_16BIT       0xac
 
 #define DMA_MODE_AUTOINIT      0x10
 
 /* --------------------------------------------------------------------- */
 
-#define WSS_CONFIG(iobase)       (iobase+0)
-#define WSS_STATUS(iobase)       (iobase+3)
-#define WSS_CODEC_IA(iobase)     (iobase+4)
-#define WSS_CODEC_ID(iobase)     (iobase+5)
-#define WSS_CODEC_STATUS(iobase) (iobase+6)
-#define WSS_CODEC_DATA(iobase)   (iobase+7)
-
-#define WSS_EXTENT   8
-
-/* --------------------------------------------------------------------- */
-
 #define UART_RBR(iobase) (iobase+0)
 #define UART_THR(iobase) (iobase+0)
 #define UART_IER(iobase) (iobase+1)
@@ -195,34 +144,27 @@
 struct sm_state {
 	struct hdlcdrv_state hdrv;
 
-	struct config {
-		int hardware;
-		int mode;
-	} config;
+	const struct modem_tx_info *mode_tx;
+	const struct modem_rx_info *mode_rx;
 
-	struct modem_state {
-		unsigned char revhi, revlo;
-		
-		unsigned int shreg;
-		
-		unsigned char last_sample;
-		unsigned int bit_pll;
-		unsigned int dcd_shreg;
-		int dcd_sum0, dcd_sum1, dcd_sum2;
-		unsigned int dcd_time;
-		unsigned char last_rxbit;
-		unsigned char tx_bit;
-		
-		signed char filt[9];
-		
-		unsigned long descram;
-		unsigned long scram;
-		
-		unsigned char *dmabufr;
-		unsigned char *dmabufw;
-		unsigned char dmabufidx;
-		unsigned char oldptt;
-	} modem;
+	const struct hardware_info *hwdrv;
+
+	/*
+	 * Hardware (soundcard) access routines state
+	 */
+	union {
+		long hw[32/sizeof(long)];
+	} hw;
+	
+	/*
+	 * state of the modem code
+	 */
+	union {
+		long m[32/sizeof(long)];
+	} m;
+	union {
+		long d[256/sizeof(long)];
+	} d;
 	
 #define DIAGDATALEN 64
 	struct diag_data {
@@ -231,34 +173,75 @@
 		volatile int ptr;
 		short data[DIAGDATALEN];
 	} diag;
+
 	
 #ifdef SM_DEBUG
 	struct debug_vals {
 		unsigned long last_jiffies;
 		unsigned cur_intcnt;
 		unsigned last_intcnt;
-		int cur_pllcorr;
-		int last_pllcorr;
+		unsigned mod_cyc;
+		unsigned demod_cyc;
+		unsigned dma_residue;
 	} debug_vals;
 #endif /* SM_DEBUG */
 };
 
-/* --------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------- */
+/*
+ * Mode definition structure
+ */
 
-struct modem_info {
-	struct hdlcdrv_ops hops;
-	unsigned int samplerate;
-	unsigned char sbcmix;
-	unsigned char sperbit;
-	char *mode_name; /* used for request_{region,irq,dma} */
-	/*
-	 * low level chip informations
-	 */
-	unsigned char data_fmt;
-	unsigned int dmabuflen;
+struct modem_tx_info {
+	const struct modem_tx_info *next;
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int dmabuflenmodulo;
 	void (*modulator)(struct sm_state *, unsigned char *, int);
+	void (*init)(struct sm_state *);
+};
+#define NEXT_TX_INFO NULL
+
+extern const struct modem_tx_info *modem_tx_base;
+
+struct modem_rx_info {
+	const struct modem_rx_info *next;
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int dmabuflenmodulo;
+	unsigned int sperbit;
 	void (*demodulator)(struct sm_state *, unsigned char *, int);
+	void (*init)(struct sm_state *);
+};
+#define NEXT_RX_INFO NULL
+
+extern const struct modem_rx_info *modem_rx_base;
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Soundcard driver definition structure
+ */
+
+struct hardware_info {
+	const struct hardware_info *next;
+	char *hw_name; /* used for request_{region,irq,dma} */
+	unsigned int loc_storage;
+	/*
+	 * mode specific open/close
+	 */
+	int (*open)(struct device *, struct sm_state *);
+	int (*close)(struct device *, struct sm_state *);
+	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *, 
+		     struct hdlcdrv_ioctl *, int);
+	int (*sethw)(struct device *, struct sm_state *, char *);
 };
+#define NEXT_HW_INFO NULL
+
+extern const struct hardware_info *hardware_base;
 
 /* --------------------------------------------------------------------- */
 
@@ -288,7 +271,7 @@
  * ===================== port checking routines ========================
  */
 
-static int inline check_lpt(unsigned int iobase)
+static int check_lpt(unsigned int iobase)
 {
 	unsigned char b1,b2;
 	int i;
@@ -319,7 +302,7 @@
 static const char *uart_str[] =
 	{ "unknown", "8250", "16450", "16550", "16550A" };
 
-static enum uart inline check_uart(unsigned int iobase)
+static enum uart check_uart(unsigned int iobase)
 {
 	unsigned char b1,b2,b3;
 	enum uart u;
@@ -356,7 +339,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static int inline check_midi(unsigned int iobase)
+static int check_midi(unsigned int iobase)
 {
 	unsigned long timeout;
 	unsigned long flags;
@@ -385,7 +368,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static void inline output_status(struct sm_state *sm)
+extern void inline output_status(struct sm_state *sm)
 {
 	int invert_dcd = 0;
 	int invert_ptt = 0;
@@ -439,7 +422,7 @@
 	}
 	output_status(sm);
 
-	printk(KERN_INFO "sm: ptt output:");
+	printk(KERN_INFO "%s: ptt output:", sm_drvname);
 	if (sm->hdrv.ptt_out.flags & SP_SER)
 		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
 		       uart_str[u]);
@@ -473,7 +456,7 @@
  * ===================== diagnostics stuff ===============================
  */
 
-static inline void diag_trigger(struct sm_state *sm)
+extern inline void diag_trigger(struct sm_state *sm)
 {
 	if (sm->diag.ptr < 0) 
 		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
@@ -482,7 +465,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
 {
 	int val;
 
@@ -501,7 +484,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static inline void diag_add_one(struct sm_state *sm, int val)
+extern inline void diag_add_one(struct sm_state *sm, int val)
 {
 	if ((sm->diag.mode != SM_DIAGMODE_INPUT && 
 	     sm->diag.mode != SM_DIAGMODE_DEMOD) || 
@@ -516,18 +499,38 @@
 }
 
 /* --------------------------------------------------------------------- */
+
+static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) || 
+	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (vali > SHRT_MAX)
+		vali = SHRT_MAX;
+	if (vali < SHRT_MIN)
+		vali = SHRT_MIN;
+	if (valq > SHRT_MAX)
+		valq = SHRT_MAX;
+	if (valq < SHRT_MIN)
+		valq = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = vali;
+	sm->diag.data[sm->diag.ptr++] = valq;
+}
+
+/* --------------------------------------------------------------------- */
 /*
- * ===================== modem routines 1200 baud =========================
+ * ===================== utility functions ===============================
  */
 
-static inline unsigned int hweight32(unsigned int w)
+extern inline unsigned int hweight32(unsigned int w)
 	__attribute__ ((unused));
-static inline unsigned int hweight16(unsigned short w)
+extern inline unsigned int hweight16(unsigned short w)
 	__attribute__ ((unused));
-static inline unsigned int hweight8(unsigned char w)
+extern inline unsigned int hweight8(unsigned char w)
         __attribute__ ((unused));
 
-static inline unsigned int hweight32(unsigned int w) 
+extern inline unsigned int hweight32(unsigned int w) 
 {
         unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
         res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
@@ -536,7 +539,7 @@
         return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
 }
 
-static inline unsigned int hweight16(unsigned short w)
+extern inline unsigned int hweight16(unsigned short w)
 {
         unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
         res = (res & 0x3333) + ((res >> 2) & 0x3333);
@@ -544,1265 +547,199 @@
         return (res & 0x00FF) + ((res >> 8) & 0x00FF);
 }
 
-static inline unsigned int hweight8(unsigned char w)
+extern inline unsigned int hweight8(unsigned char w)
 {
         unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
         res = (res & 0x33) + ((res >> 2) & 0x33);
         return (res & 0x0F) + ((res >> 4) & 0x0F);
 }
 
-/* --------------------------------------------------------------------- */
-
-#ifdef ENABLE_AFSK1200
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
 
-static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
 {
-	static const int dds_inc[2] = { 8192, 15019 };
-	int j, k;
-
-	for (; buflen >= 8; buflen -= 8) {
-		if (sm->modem.shreg <= 1)
-			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		sm->modem.tx_bit = (sm->modem.tx_bit ^ 
-				    (!(sm->modem.shreg & 1))) & 1;
-		sm->modem.shreg >>= 1;
-		k = dds_inc[sm->modem.tx_bit & 1];
-		for (j = 0; j < 8; j++) {
-			*buf++ = sinetab[(sm->modem.bit_pll >> 10) & 0x3f];
-			sm->modem.bit_pll += k;
-		}
+	for (;;) {
+		if (!x)
+			return y;
+		if (!y) 
+			return x;
+		if (x > y)
+			x %= y;
+		else
+			y %= x;
 	}
 }
 
-/* --------------------------------------------------------------------- */
-
-static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
 {
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	int j;
-	signed char *fp;
-	const signed char *coeffp;
-	int sum1, sum2;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sm->modem.filt[8] = (*buf - 128);
-		for (sum1 = j = 0, fp = sm->modem.filt+1, coeffp = tx_lo_i; 
-		     j < 8; j++, fp++, coeffp++) {
-			sum1 += (*coeffp) * (*fp);
-			fp[-1] = fp[0];
-		}
-		sum1 >>= 7;
-		sum2 = sum1 * sum1;
-		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_lo_q; 
-		     j < 8; j++, fp++, coeffp++)
-			sum1 += (*coeffp) * (*fp);
-		sum1 >>= 7;
-		sum2 += sum1 * sum1;
-		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_i; 
-		     j < 8; j++, fp++, coeffp++)
-			sum1 += (*coeffp) * (*fp);
-		sum1 >>= 7;
-		sum2 -= sum1 * sum1;
-		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_q; 
-		     j < 8; j++, fp++, coeffp++)
-			sum1 += (*coeffp) * (*fp);
-		sum1 >>= 7;
-		sum2 -= sum1 * sum1;
-		sm->modem.dcd_shreg <<= 1;
-		sm->modem.bit_pll += 0x2000;
-		newsample = (sum2 > 0);
-		if (sm->modem.last_sample ^ newsample) {
-			sm->modem.last_sample = newsample;
-			sm->modem.dcd_shreg |= 1;
-			sm->modem.bit_pll += pll_corr
-				[sm->modem.bit_pll < 0x9000];
-			j = 4 * hweight8(sm->modem.dcd_shreg & 0x38)
-				- hweight16(sm->modem.dcd_shreg & 0x7c0);
-			sm->modem.dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
-		if ((--sm->modem.dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
-						   sm->modem.dcd_sum1 + 
-						   sm->modem.dcd_sum2) < 0);
-			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
-			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
-			sm->modem.dcd_sum0 = 2; /* slight bias */
-			sm->modem.dcd_time = 120;
-		}
-		if (sm->modem.bit_pll >= 0x10000) {
-			sm->modem.bit_pll &= 0xffff;
-			sm->modem.shreg >>= 1;
-			sm->modem.shreg |= (!(sm->modem.last_rxbit ^
-					      sm->modem.last_sample)) << 16;
-			sm->modem.last_rxbit = sm->modem.last_sample;
-			diag_trigger(sm);
-			if (sm->modem.shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
-				sm->modem.shreg = 0x10000;
-			}
-		}
-		diag_add(sm, sm->modem.filt[7] << 8, sum2);
-	}
+	return x * y / gcd(x, y);
 }
 
-#endif /* ENABLE_AFSK1200 */
-
 /* --------------------------------------------------------------------- */
 /*
- * ===================== modem routines 9600 baud =========================
+ * ===================== profiling =======================================
  */
 
-#ifdef ENABLE_FSK9600
 
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
+#if defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686))
 
-#ifdef ENABLE_SBC
-
-static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	int j;
-	const unsigned char *cp;
-
-	for (; buflen >= 4; buflen -= 4) {
-		if (sm->modem.shreg <= 1)
-			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		sm->modem.scram = ((sm->modem.scram << 1) |
-				   (sm->modem.scram & 1));
-		sm->modem.scram ^= (!(sm->modem.shreg & 1));
-		sm->modem.shreg >>= 1;
-		if (sm->modem.scram & (SCRAM_TAP1 << 1))
-			sm->modem.scram ^= (SCRAM_TAPN << 1);
-		sm->modem.tx_bit = (sm->modem.tx_bit << 1) | 
-			(!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
-		cp = tx_filter_9k6_4 + (sm->modem.tx_bit & 0xff);
-		for (j = 0; j < 4; j++) {
-			*buf++ = *cp;
-			cp += 0x100;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sm->modem.dcd_shreg <<= 1;
-		sm->modem.bit_pll += 0x4000;
-		curbit = (*buf >= 0x80);
-		if (sm->modem.last_sample ^ curbit) {
-			sm->modem.dcd_shreg |= 1;
-			sm->modem.bit_pll += pll_corr
-				[sm->modem.bit_pll < 0xa000];
-			sm->modem.dcd_sum0 += 8 * 
-				hweight8(sm->modem.dcd_shreg & 0x0c) - 
-					!!(sm->modem.dcd_shreg & 0x10);
-		}
-		sm->modem.last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
-		if ((--sm->modem.dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
-						   sm->modem.dcd_sum1 + 
-						   sm->modem.dcd_sum2) < 0);
-			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
-			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
-			sm->modem.dcd_sum0 = 2; /* slight bias */
-			sm->modem.dcd_time = 240;
-		}
-		if (sm->modem.bit_pll >= 0x10000) {
-			sm->modem.bit_pll &= 0xffff;
-			sm->modem.descram = (sm->modem.descram << 1) | curbit;
-			descx = sm->modem.descram ^ (sm->modem.descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			sm->modem.shreg >>= 1;
-			sm->modem.shreg |= (!(descx & 1)) << 16;
-			if (sm->modem.shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
-				sm->modem.shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-#endif /* ENABLE_SBC */
-
-/* --------------------------------------------------------------------- */
-
-#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
-
-static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	int j;
-	const unsigned char *cp;
-
-	for (; buflen >= 5; buflen -= 5) {
-		if (sm->modem.shreg <= 1)
-			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		sm->modem.scram = ((sm->modem.scram << 1) |
-				   (sm->modem.scram & 1));
-		sm->modem.scram ^= (!(sm->modem.shreg & 1));
-		sm->modem.shreg >>= 1;
-		if (sm->modem.scram & (SCRAM_TAP1 << 1))
-			sm->modem.scram ^= (SCRAM_TAPN << 1);
-		sm->modem.tx_bit = (sm->modem.tx_bit << 1) | 
-			(!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
-		cp = tx_filter_9k6_5 + (sm->modem.tx_bit & 0xff);
-		for (j = 0; j < 5; j++) {
-			*buf++ = *cp;
-			cp += 0x100;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sm->modem.dcd_shreg <<= 1;
-		sm->modem.bit_pll += 0x3333;
-		curbit = (*buf >= 0x80);
-		if (sm->modem.last_sample ^ curbit) {
-			sm->modem.dcd_shreg |= 1;
-			sm->modem.bit_pll += pll_corr
-				[sm->modem.bit_pll < 0x9999];
-			sm->modem.dcd_sum0 += 16 * 
-				hweight8(sm->modem.dcd_shreg & 0x0c) - 
-					hweight8(sm->modem.dcd_shreg & 0x70);
-		}
-		sm->modem.last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
-		if ((--sm->modem.dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
-						   sm->modem.dcd_sum1 + 
-						   sm->modem.dcd_sum2) < 0);
-			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
-			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
-			sm->modem.dcd_sum0 = 2; /* slight bias */
-			sm->modem.dcd_time = 240;
-		}
-		if (sm->modem.bit_pll >= 0x10000) {
-			sm->modem.bit_pll &= 0xffff;
-			sm->modem.descram = (sm->modem.descram << 1) | curbit;
-			descx = sm->modem.descram ^ (sm->modem.descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			sm->modem.shreg >>= 1;
-			sm->modem.shreg |= (!(descx & 1)) << 16;
-			if (sm->modem.shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
-				sm->modem.shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
-#endif /* ENABLE_FSK9600 */
-
-/* --------------------------------------------------------------------- */
 /*
- * ===================== soundblaster specific routines ===================
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
+ * in fact, overflowing modems would require over 2THz clock speeds :-)
  */
 
-#ifdef ENABLE_SBC
+#define time_exec(var,cmd)                                      \
+({                                                              \
+	unsigned int cnt1, cnt2, cnt3;                          \
+	__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
+	cmd;                                                    \
+	__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
+	var = cnt2-cnt1;                                        \
+})
+#else /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
 
-static int inline reset_dsp(struct device *dev)
-{
-	int i;
+#define time_exec(var,cmd) cmd
 
-	outb(1, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0x100; i++)
-		SLOW_DOWN_IO;
-	outb(0, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0xffff; i++)
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
-			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
-				return 1;
-	return 0;
-}
+#endif /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
 
 /* --------------------------------------------------------------------- */
 
-static void inline write_dsp(struct device *dev, unsigned char data)
-{
-	int i;
-	
-	for (i = 0; i < 0xffff; i++)
-		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
-			outb(data, DSP_WRITE_DATA(dev->base_addr));
-			return;
-		}
-}
-
-/* --------------------------------------------------------------------- */
+#ifdef CONFIG_SOUNDMODEM_WSS
+#include "sm_wss.h"
+#endif /* CONFIG_SOUNDMODEM_WSS */
+#ifdef CONFIG_SOUNDMODEM_SBC
+#include "sm_sbc.h"
+#endif /* CONFIG_SOUNDMODEM_SBC */
 
-static int inline read_dsp(struct device *dev, unsigned char *data)
-{
-	int i;
-
-	if (!data)
-		return 0;
-	for (i = 0; i < 0xffff; i++) 
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
-			*data = inb(DSP_READ_DATA(dev->base_addr));
-			return 1;
-		}
-	return 0;
-}
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+#include "sm_afsk1200.h"
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+#include "sm_fsk9600.h"
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+#include "sm_afsk2666.h"
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+#include "sm_psk4800.h"
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
 
 /* --------------------------------------------------------------------- */
 
-static void inline sbc_int_ack(struct device *dev)
-{
-	inb(DSP_DATA_AVAIL(dev->base_addr));
-}
+static int sm_open(struct device *dev);
+static int sm_close(struct device *dev);
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, 
+		    struct hdlcdrv_ioctl *hi, int cmd);
 
 /* --------------------------------------------------------------------- */
 
-static void setup_dma_dsp(struct device *dev, int send)
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-        unsigned long flags;
-        static const unsigned char sbcmode[2][2] = {
-        { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
-	{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }};
-        static const unsigned char dmamode[2] = 
-        { DMA_MODE_READ | DMA_MODE_AUTOINIT, 
-		  DMA_MODE_WRITE  | DMA_MODE_AUTOINIT};
-	static const unsigned char sbcskr[2] = 
-	{ SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
-	unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
-
-	send = !!send;
-        if (!reset_dsp(dev)) {
-                printk(KERN_ERR "sm: cannot reset sb dsp\n");
-                return;
-        }
-        if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
-                panic("sm: DMA buffer violates DMA boundary!");
-        save_flags(flags);
-        cli();
-        sbc_int_ack(dev);
-        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
-        write_dsp(dev, mi->data_fmt);
-        write_dsp(dev, sbcskr[send]); 
-        disable_dma(dev->dma);
-        clear_dma_ff(dev->dma);
-        set_dma_mode(dev->dma, dmamode[send]);
-        set_dma_addr(dev->dma, dmabufaddr);
-        set_dma_count(dev->dma, mi->dmabuflen);
-        enable_dma(dev->dma);
-        sbc_int_ack(dev);
-        write_dsp(dev, SBC_HISPEED);
-        write_dsp(dev, ((mi->dmabuflen >> 1) - 1) & 0xff);
-        write_dsp(dev, ((mi->dmabuflen >> 1) - 1) >> 8);
-        write_dsp(dev, sbcmode[mi->samplerate >= 13000][send]);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-#if 0
-static int probe_int(struct device *dev, struct sm_state *sm)
-{
-	unsigned long irqs;
-	int irq;
-
-	irqs = probe_irq_on();
-	setup_dma_dsp(dev, virt_to_bus(sm->modem.dmabufr), 4, 256-77, 0);
-	udelay(2000);
-        irq = probe_irq_off(irqs);
-	disable_dma(dev->dma);
-	return irq;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-	unsigned char new_ptt;
-	unsigned char *buf;
+const struct modem_tx_info *modem_tx_base = NEXT_TX_INFO;
+const struct modem_rx_info *modem_rx_base = NEXT_RX_INFO;
+const struct hardware_info *hardware_base = NEXT_HW_INFO;
 
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	new_ptt = hdlcdrv_ptt(&sm->hdrv);
- 	sbc_int_ack(dev);
- 	buf = sm->modem.dmabufr;
-	if (sm->modem.dmabufidx)
-		buf += mi->dmabuflen/2;
-	sm->modem.dmabufidx = !sm->modem.dmabufidx;
-	if (sm->modem.oldptt != new_ptt) {
-		disable_dma(dev->dma);
-		sti();
- 		sm->modem.dmabufidx = 0;
-		if (!new_ptt) {
-			setup_dma_dsp(dev, 0);
-			goto endint;
-		}
-		mi->demodulator(sm, buf, mi->dmabuflen/2);
-		mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
- 		setup_dma_dsp(dev, 1);
-		mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2, 
-			      mi->dmabuflen/2);
-		goto endint;
- 	}
-	sm_int_freq(sm);
-	sti();
-	/*
-	 * check if transmitter active
-	 */
-	if (new_ptt)
-		mi->modulator(sm, buf, mi->dmabuflen/2);
-	else {
-		mi->demodulator(sm, buf, mi->dmabuflen/2);
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-	}
- endint:
-	sm->modem.oldptt = new_ptt;
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_open(struct device *dev) 
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-	unsigned char revreq = (mi->samplerate >= 13000) ? 3 : 2;
-	
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, SBC_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (!reset_dsp(dev))
-		return -ENODEV;
-	write_dsp(dev, SBC_GET_REVISION);
-	if (!read_dsp(dev, &sm->modem.revhi) || 
-	    !read_dsp(dev, &sm->modem.revlo))
-		return -ENODEV;
-	if (sm->modem.revhi < revreq) {
-		printk(KERN_ERR "sm: sbc io 0x%lx: DSP rev %d.%02d too "
-		       "old, at least %d.00 required\n", dev->base_addr,
-		       sm->modem.revhi, sm->modem.revlo, revreq);
-		return -ENODEV;
-	}
-	/*
-	 * initialize some variables
-	 */
-	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	sm->modem.shreg = sm->modem.last_sample = 0;
-	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
-	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
-	sm->modem.dmabufidx = sm->modem.oldptt = 0;
-	sm->modem.dmabufw = NULL;
-	sm->modem.dcd_time = 120;
-	sm->modem.dcd_sum0 = 2;
-#if 0
-	if (!dev->irq) {
-		int irq = probe_int(dev, sm);
-		if (irq < 0) {
-			printk(KERN_ERR "sm: irq autoprobe failed\n");
-			kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-			return -EBUSY;
-		}
-		dev->irq = irq;
-	}
-#endif
-	if (request_dma(dev->dma, mi->mode_name)) {
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
-			mi->mode_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, SBC_EXTENT, mi->mode_name);
-	setup_dma_dsp(dev, 0);
-	output_open(sm);
-	printk(KERN_INFO "sm: sbc at iobase 0x%lx irq %u dma "
-	       "%u DSP revision %u.%02u\n", dev->base_addr, dev->irq, 
-	       dev->dma, (unsigned int)sm->modem.revhi, 
-	       (unsigned int)sm->modem.revlo);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
+static const struct hdlcdrv_ops sm_ops = {
+	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
+};
 
 /* --------------------------------------------------------------------- */
 
-static int sbc_close(struct device *dev) 
+static int sm_open(struct device *dev)
 {
-	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct sm_state *sm;
+	int err;
 
-	if (!dev || !sm)
+	if (!dev || !dev->priv || 
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_open: invalid device struct\n");
 		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	reset_dsp(dev);	
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, SBC_EXTENT);
-	kfree(sm->modem.dmabufr);
-	output_close(sm);
-	printk(KERN_INFO "sm: close sbc at iobase 0x%lx irq %u dma %u\n", 
-	       dev->base_addr, dev->irq, dev->dma);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#endif /* ENABLE_SBC */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== Windows Sound System specific routines ==========
- */
-
-#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
-
-static void write_codec(struct device *dev, unsigned char idx,
-			unsigned char data)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx, WSS_CODEC_IA(dev->base_addr));
-	outb(data, WSS_CODEC_ID(dev->base_addr));
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static unsigned char read_codec(struct device *dev, unsigned char idx)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx & 0xf, WSS_CODEC_IA(dev->base_addr));
-	return inb(WSS_CODEC_ID(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline wss_ack_int(struct device *dev)
-{
-	outb(0, WSS_CODEC_STATUS(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_init_codec(struct device *dev, unsigned char sdc, 
-			  unsigned char src_l, unsigned char src_r, 
-			  int igain_l, int igain_r,
-			  int ogain_l, int ogain_r)
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-
-	unsigned char tmp, reg0, reg1, reg6, reg7;
-	static const signed char irqtab[16] = 
-	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
-		  -1, -1 };
-	static const signed char dmatab[4] = { 1, 2, -1, 3 };
-	unsigned long time;
-				
-	tmp = inb(WSS_STATUS(dev->base_addr));
-	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
-	    (tmp & 0x3f) != 0x0f) {
-		printk(KERN_ERR "sm: WSS card not found, address 0x%lx, ID "
-		       "register 0x%02x\n", dev->base_addr, (int)tmp);
-		return -1;
-	}
-	if ((tmp & 0x80) && ((dev->dma == 0) || ((dev->irq >= 8) && 
-						 (dev->irq != 9)))) {
-		printk(KERN_ERR "sm: WSS: DMA0 and/or IRQ8..IRQ15 (except "
-		       "IRQ9) cannot be used on an 8bit card\n");
-		return -1;
-	}		
-	if (dev->irq > 15 || irqtab[dev->irq] == -1) {
-		printk(KERN_ERR "sm: WSS: invalid interrupt %d\n", 
-		       (int)dev->irq);
-		return -1;
-	}
-	if (dev->dma > 3 || dmatab[dev->dma] == -1) {
-		printk(KERN_ERR "sm: WSS: invalid dma channel %d\n", 
-		       (int)dev->dma);
-		return -1;
-	}
-	tmp = irqtab[dev->irq] | dmatab[dev->dma];
-	outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); /* irq probe */
-	if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
-		outb(0, WSS_CONFIG(dev->base_addr));
-		printk(KERN_ERR "sm: WSS: IRQ%d is not free!\n", dev->irq);
-	}
-	outb(tmp, WSS_CONFIG(dev->base_addr));
-	/*
-	 * initialize the codec
-	 */
-	if (igain_l < 0)
-		igain_l = 0;
-	if (igain_r < 0)
-		igain_r = 0;
-	if (ogain_l > 0)
-		ogain_l = 0;
-	if (ogain_r > 0)
-		ogain_r = 0;
-	reg0 = (src_l << 6) & 0xc0;
-	reg1 = (src_r << 6) & 0xc0;
-	if (reg0 == 0x80 && igain_l >= 20) {
-		reg0 |= 0x20;
-		igain_l -= 20;
-	}
-	if (reg1 == 0x80 && igain_r >= 20) {
-		reg1 |= 0x20;
-		igain_r -= 20;
 	}
-	if (igain_l > 23)
-		igain_l = 23;
-	if (igain_r > 23)
-		igain_r = 23;
-	reg0 |= igain_l * 2 / 3;
-	reg1 |= igain_r * 2 / 3;
-	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
-	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
-#if 1
-	write_codec(dev, 9, 0);
-	write_codec(dev, 15, 0xaa);
-	write_codec(dev, 14, 0x55);
-	if ((read_codec(dev, 15) != 0xaa) || (read_codec(dev, 14) != 0x55)) 
-		goto codec_err;
-#endif
-	write_codec(dev, 0x48, mi->data_fmt);  /* Clock and data format register */
-	write_codec(dev, 0x49, sdc ? 0xc : 0x8); /* MCE and interface config reg */
-	/* single DMA channel, disable both DMA */
-	/* clear MCE and wait until ACI set */
-	time = jiffies + HZ/4;
-	while (!(read_codec(dev, 0x0b) & 0x20) &&
-	       ((signed)(jiffies - time) < 0));
-	/* wait until ACI cleared */
-	while ((read_codec(dev, 0x0b) & 0x20) &&
-	       ((signed)(jiffies - time) < 0));
-	if ((signed)(jiffies - time) >= 0) {
-		printk(KERN_WARNING "sm: ad1848 auto calibration timed out\n");
-		goto codec_err;
-	}
-        write_codec(dev, 0, reg0); /* left input control */
-        write_codec(dev, 1, reg1); /* right input control */
-        write_codec(dev, 2, 0x80); /* left aux#1 input control */
-        write_codec(dev, 3, 0x80); /* right aux#1 input control */
-        write_codec(dev, 4, 0x80); /* left aux#2 input control */
-        write_codec(dev, 5, 0x80); /* right aux#2 input control */
-        write_codec(dev, 6, reg6); /* left dac control */
-        write_codec(dev, 7, reg7); /* right dac control */
-        write_codec(dev, 0xa, 0x2); /* pin control register */
-        write_codec(dev, 0xd, 0x0); /* digital mix control */
-	sm->modem.revhi = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
-	sm->modem.revlo = read_codec(dev, 0xc) & 0xf;
-	/*
-	 * print revisions
-	 */
-	printk(KERN_INFO "sm: WSS revision %d, CODEC revision %d\n", 
-	       (int)sm->modem.revhi, (int)sm->modem.revlo);
-	return 0;
- codec_err:
-	outb(0, WSS_CONFIG(dev->base_addr));
-	printk(KERN_ERR "sm: no WSS soundcard found at address 0x%lx\n",
-	       dev->base_addr);
-	return -1;
-}
-
-#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
-
-/* --------------------------------------------------------------------- */
-
-#ifdef ENABLE_WSS
-
-static void setup_dma_wss(struct device *dev, int flg)
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-        unsigned long flags;
-        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
-        static const unsigned char dmamode[2] = 
-        { DMA_MODE_READ | DMA_MODE_AUTOINIT, 
-		  DMA_MODE_WRITE  | DMA_MODE_AUTOINIT};
-	unsigned char oldcodecmode, codecdma;
-	long abrt;
-	unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
-
-        if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
-                panic("sm: DMA buffer violates DMA boundary!");
-	flg = !!flg;
-        save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-	oldcodecmode = read_codec(dev, 9);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	wss_ack_int(dev);
-	if ((codecdma = read_codec(dev, 11)) & 0x10) {
-		disable_dma(dev->dma);
-		clear_dma_ff(dev->dma);
-		set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]);
-		set_dma_addr(dev->dma, dmabufaddr);
-		set_dma_count(dev->dma, mi->dmabuflen);
-		enable_dma(dev->dma);
-		abrt = 0;
-		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
-		       ((++abrt) >= 0x10000));
-	}
-        disable_dma(dev->dma);
-        clear_dma_ff(dev->dma);
-        set_dma_mode(dev->dma, dmamode[flg]);
-        set_dma_addr(dev->dma, dmabufaddr);
-        set_dma_count(dev->dma, mi->dmabuflen);
-        enable_dma(dev->dma);
-	write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
-	write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
-	write_codec(dev, 9, codecmode[flg]);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-	unsigned char new_ptt;
-	unsigned char *buf;
-	unsigned long flags;
-	int dmares;
-
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	new_ptt = hdlcdrv_ptt(&sm->hdrv);
-	save_flags(flags);
-	cli();
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	dmares = get_dma_residue(dev->dma);
-	enable_dma(dev->dma);
-	if (dmares <= 0)
-		dmares = mi->dmabuflen;
-	buf = sm->modem.dmabufr;
-	if (dmares > mi->dmabuflen/2)
-		buf += mi->dmabuflen/2;
-	if (dmares > mi->dmabuflen/2)
-		dmares -= mi->dmabuflen/2;
-#ifdef SM_DEBUG
-	if (!sm->debug_vals.last_pllcorr || 
-	    dmares < sm->debug_vals.last_pllcorr)
-		sm->debug_vals.last_pllcorr = dmares;
-#endif /* SM_DEBUG */
-	dmares--;
-	write_codec(dev, 15, dmares & 0xff);
-	write_codec(dev, 14, dmares >> 8);
-	restore_flags(flags);
-	if (sm->modem.oldptt != new_ptt) {
-		disable_dma(dev->dma);
-		sti();
- 		sm->modem.dmabufidx = 0;
-		if (!new_ptt) {
-			setup_dma_wss(dev, 0);
-			goto endint;
-		}
-		mi->demodulator(sm, buf, mi->dmabuflen/2);
-		mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
- 		setup_dma_wss(dev, 1);
-		mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2,
-			      mi->dmabuflen/2);
-		goto endint;
- 	}
-	sm_int_freq(sm);
-	sti();
-	/*
-	 * check if transmitter active
-	 */
-	if (new_ptt)
-		mi->modulator(sm, buf, mi->dmabuflen/2);
-	else {
-		mi->demodulator(sm, buf, mi->dmabuflen/2);
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-	}
- endint:
-	sm->modem.oldptt = new_ptt;
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_open(struct device *dev) 
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+	sm = (struct sm_state *)dev->priv;
 
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, 1, 1, 1, 0, 0, -45, -45))
+	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
 		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	sm->modem.shreg = sm->modem.last_sample = 0;
-	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
-	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
-	sm->modem.dmabufidx = sm->modem.oldptt = 0;
-	sm->modem.dmabufw = NULL;
-	sm->modem.dcd_time = 120;
-	sm->modem.dcd_sum0 = 2;
-	if (request_dma(dev->dma, mi->mode_name)) {
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
-			mi->mode_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
-	setup_dma_wss(dev, 0);
+	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
+	err = sm->hwdrv->open(dev, sm);
+	if (err)
+		return err;
 	output_open(sm);
-	printk(KERN_INFO "sm: wss at iobase 0x%lx irq %u dma "
-	       "%u\n", dev->base_addr, dev->irq, dev->dma);
 	MOD_INC_USE_COUNT;
+	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u\n", 
+	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name, 
+	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma);
 	return 0;
 }
 
 /* --------------------------------------------------------------------- */
 
-static int wss_close(struct device *dev) 
+static int sm_close(struct device *dev)
 {
-	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct sm_state *sm;
+	int err = -ENODEV;
 
-	if (!dev || !sm)
+	if (!dev || !dev->priv || 
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_close: invalid device struct\n");
 		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-	output_close(sm);
-	printk(KERN_INFO "sm: close wss at iobase 0x%lx irq %u"
-	       " dma %u\n", dev->base_addr, dev->irq, dev->dma);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#endif /* ENABLE_WSS */
-
-/* --------------------------------------------------------------------- */
-/*
- * =========== Windows Sound System Fullduplex specific routines ==========
- */
-
-/*
- * This does _not_ work on my hardware
- */
-
-#ifdef ENABLE_WSSFDX
-
-static void setup_dma_wssfdx(struct device *dev)
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-        unsigned long flags;
-	unsigned char codecdma;
-	long abrt;
-	unsigned long dmabufraddr = virt_to_bus(sm->modem.dmabufr);
-	unsigned long dmabufwaddr = virt_to_bus(sm->modem.dmabufw);
-
-        if (((dmabufraddr & 0xffff) + mi->dmabuflen > 0x10000) ||
-	    ((dmabufwaddr & 0xffff) + mi->dmabuflen > 0x10000))
-                panic("sm: DMA buffer violates DMA boundary!");
-        save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-        write_codec(dev, 9, 0x8); /* disable codec */
-	wss_ack_int(dev);
-	if ((codecdma = read_codec(dev, 11)) & 0x10) {
-		disable_dma(dev->dma);
-		disable_dma(!dev->dma);
-		clear_dma_ff(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
-		set_dma_addr(dev->dma, dmabufwaddr);
-		set_dma_count(dev->dma, mi->dmabuflen);
-		set_dma_mode(!dev->dma,  DMA_MODE_READ | DMA_MODE_AUTOINIT);
-		set_dma_addr(!dev->dma, dmabufraddr);
-		set_dma_count(!dev->dma, mi->dmabuflen);
-		enable_dma(dev->dma);
-		enable_dma(!dev->dma);
-		abrt = 0;
-		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
-		       ((++abrt) >= 0x10000));
-	}
-	disable_dma(dev->dma);
-	disable_dma(!dev->dma);
-	clear_dma_ff(dev->dma);
-	set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
-	set_dma_addr(dev->dma, dmabufwaddr);
-	set_dma_count(dev->dma, mi->dmabuflen);
-	set_dma_mode(!dev->dma,  DMA_MODE_READ | DMA_MODE_AUTOINIT);
-	set_dma_addr(!dev->dma, dmabufraddr);
-	set_dma_count(!dev->dma, mi->dmabuflen);
-	enable_dma(dev->dma);
-	enable_dma(!dev->dma);
-	write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
-	write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
-	write_codec(dev, 9, 0x0b);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int msgcnt = 10;
-
-static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-	unsigned char new_ptt;
-	unsigned char *bufr;
-	unsigned char *bufw;
-	unsigned long flags;
-	int dmares, dmares2, i;
-
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	new_ptt = hdlcdrv_ptt(&sm->hdrv);
-	save_flags(flags);
-	cli();
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	disable_dma(!dev->dma);
-	clear_dma_ff(dev->dma);
-	dmares = get_dma_residue(dev->dma);
-	dmares2 = get_dma_residue(!dev->dma);
-	enable_dma(dev->dma);
-	enable_dma(!dev->dma);
-	if (dmares <= 0)
-		dmares = mi->dmabuflen;
-	if (dmares2 <= 0)
-		dmares2 = mi->dmabuflen;
-	bufw = sm->modem.dmabufw;
-	if (dmares > mi->dmabuflen/2)
-		bufw += mi->dmabuflen/2;
-	bufr = sm->modem.dmabufr;
-	if (dmares2 > mi->dmabuflen/2)
-		bufr += mi->dmabuflen/2;
-	if ((i = dmares) > mi->dmabuflen/2)
-		i -= mi->dmabuflen/2;
-#ifdef SM_DEBUG
-	if (!sm->debug_vals.last_pllcorr || 
-	    i < sm->debug_vals.last_pllcorr)
-		sm->debug_vals.last_pllcorr = i;
-#endif /* SM_DEBUG */
-	i--;
-	write_codec(dev, 15, i & 0xff);
-	write_codec(dev, 14, i >> 8);
-	restore_flags(flags);
-	sm_int_freq(sm);
-	sti();
-
-	if (msgcnt > 0) {
-		msgcnt--;
-		printk(KERN_DEBUG "sm: DMA residue: playback: %d "
-		       "capture: %d\n", dmares, dmares2);
 	}
+	sm = (struct sm_state *)dev->priv;
 
-	/*
-	 * check if transmitter active
-	 */
-	if (new_ptt)
-		mi->modulator(sm, bufw, mi->dmabuflen/2);
-	else 
-		memset(bufw, 0x80, mi->dmabuflen/2);
-	mi->demodulator(sm, bufr, mi->dmabuflen/2);
-	hdlcdrv_arbitrate(dev, &sm->hdrv);
-	sm->modem.oldptt = new_ptt;
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
+	
+	if (sm->hwdrv && sm->hwdrv->close)
+		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
+	output_close(sm);
+	MOD_DEC_USE_COUNT;
+	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n", 
+	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
+	return err;
 }
 
 /* --------------------------------------------------------------------- */
 
-static int wssfdx_open(struct device *dev) 
+static int sethw(struct device *dev, struct sm_state *sm, char *mode)
 {
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
-
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, 0, 1, 1, 0, 0, -45, -45))
-		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	if (!(sm->modem.dmabufw = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) {
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		return -ENOMEM;
-	}
-	sm->modem.shreg = sm->modem.last_sample = 0;
-	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
-	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
-	sm->modem.dmabufidx = sm->modem.oldptt = 0;
-	sm->modem.dcd_time = 120;
-	sm->modem.dcd_sum0 = 2;
-	if (request_dma(dev->dma, mi->mode_name)) {
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_dma(!dev->dma, mi->mode_name)) {
-		free_dma(dev->dma);
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
-			mi->mode_name, dev)) {
-		free_dma(dev->dma);
-		free_dma(!dev->dma);
-		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
-		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
-		return -EBUSY;
+	char *cp = strchr(mode, ':');
+	const struct hardware_info *hwp = hardware_base;
+	
+	if (!cp)
+		cp = mode;
+	else {
+		*cp++ = '\0';
+		while (hwp && hwp->hw_name && strcmp(hwp->hw_name, mode))
+			hwp = hwp->next;
+		if (!hwp || !hwp->hw_name)
+			return -EINVAL;
+		if (hwp->loc_storage > sizeof(sm->hw)) {
+			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
+			       sm_drvname, hwp->hw_name, hwp->loc_storage);
+			return -EINVAL;
+		}
+		sm->hwdrv = hwp;
 	}
-	request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
-	setup_dma_wssfdx(dev);
-	output_open(sm);
-	printk(KERN_INFO "sm: wss fdx at iobase 0x%lx irq %u dma1 "
-	       "%u dma2 %u\n", dev->base_addr, dev->irq, dev->dma, !dev->dma);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_close(struct device *dev) 
-{
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	disable_dma(!dev->dma);
-        write_codec(dev, 9, 0x8); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	free_dma(!dev->dma);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree(sm->modem.dmabufr);
-	kfree(sm->modem.dmabufw);
-	output_close(sm);
-	printk(KERN_INFO "sm: close wss fdx at iobase 0x%lx irq %u"
-	       " dma %u\n", dev->base_addr, dev->irq, dev->dma);
-	MOD_DEC_USE_COUNT;
-	return 0;
+	if (!*cp) 
+		return 0;
+	if (sm->hwdrv && sm->hwdrv->sethw)
+		return sm->hwdrv->sethw(dev, sm, cp);
+	return -EINVAL;
 }
 
-#endif /* ENABLE_WSSFDX */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== hdlcdrv driver interface =========================
- */
-
-static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-
-#define SBC1200_SRATE (256-104) /* the SBC sampling rate, 256-(1E6/srate) */
-#define SBC1200_DMABUFLEN 192   /* DMA buffer duration exactly 20ms */
-
-#define SBC9600_SRATE (256-26) /* the SBC sampling rate, 256-(1E6/srate) */
-#define SBC9600_DMABUFLEN 768    /* DMA buffer duration exactly 20ms */
-
-#define WSS1200_DATAFMT 0x0e   /* 8bit unsigned PCM, Mono, XTAL1, 9.6kHz */
-#define WSS1200_DMABUFLEN 192        /* DMA buffer duration exactly 20ms */
-
-#define WSS9600_DATAFMT 0x0c    /* 8bit unsigned PCM, Mono, XTAL1, 48kHz */
-#define WSS9600_DMABUFLEN 960        /* DMA buffer duration exactly 20ms */
-
-/* --------------------------------------------------------------------- */
-
-#ifdef ENABLE_SBC
-
-static const struct modem_info sbc1200_ops = { 
-{ 1200, sbc_open, sbc_close, sm_ioctl }, 9600, 1, 8, "sbc_1200",
-SBC1200_SRATE, SBC1200_DMABUFLEN,
-modulator_1200, demodulator_1200
-};
-/* --------------------------------------------------------------------- */
-
-static const struct modem_info sbc9600_ops = {
-{ 9600, sbc_open, sbc_close, sm_ioctl }, 38400, 1, 4, "sbc_9600",
-SBC9600_SRATE, SBC9600_DMABUFLEN,
-modulator_9600_4, demodulator_9600_4
-};
-
-#endif /* ENABLE_SBC */
-
-/* --------------------------------------------------------------------- */
-
-#ifdef ENABLE_WSS
-
-static const struct modem_info wss1200_ops = {
-{ 1200, wss_open, wss_close, sm_ioctl }, 9600, 0, 8, "wss_1200",
-WSS1200_DATAFMT, WSS1200_DMABUFLEN,
-modulator_1200, demodulator_1200
-};
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_info wss9600_ops = {
-{ 9600, wss_open, wss_close, sm_ioctl }, 48000, 0, 5, "wss_9600",
-WSS9600_DATAFMT, WSS9600_DMABUFLEN,
-modulator_9600_5, demodulator_9600_5
-};
-
-#endif /* ENABLE_WSS */
-
-/* --------------------------------------------------------------------- */
-
-#ifdef ENABLE_WSSFDX
-
-static const struct modem_info wss1200fdx_ops = {
-{ 1200, wssfdx_open, wssfdx_close, sm_ioctl }, 9600, 0, 8, "wss_fdx_1200",
-WSS1200_DATAFMT, WSS1200_DMABUFLEN,
-modulator_1200, demodulator_1200
-};
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_info wss9600fdx_ops = {
-{ 9600, wssfdx_open, wssfdx_close, sm_ioctl }, 48000, 0, 5, "wss_fdx_9600",
-WSS9600_DATAFMT, WSS9600_DMABUFLEN,
-modulator_9600_5, demodulator_9600_5
-};
-
-#endif /* ENABLE_WSSFDX */
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_info dummy_ops = {
-{ 0, NULL, NULL, sm_ioctl }, 0, 0, 0, "none",
-0, 0,
-NULL, NULL
-};
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_info *ops_tab[3][2] = {
-#ifdef ENABLE_SBC
-{ &sbc1200_ops, &sbc9600_ops }, 
-#else /* ENABLE_SBC */
-{ &dummy_ops, &dummy_ops }, 
-#endif /* ENABLE_SBC */
-#ifdef ENABLE_WSS
-{ &wss1200_ops, &wss9600_ops },
-#else /* ENABLE_WSS */
-{ &dummy_ops, &dummy_ops }, 
-#endif /* ENABLE_WSS */
-#ifdef ENABLE_WSSFDX
-{ &wss1200fdx_ops, &wss9600fdx_ops }
-#else /* ENABLE_WSSFDX */
-{ &dummy_ops, &dummy_ops }
-#endif /* ENABLE_WSSFDX */
-};
-
 /* --------------------------------------------------------------------- */
 
-static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, 
+		    struct hdlcdrv_ioctl *hi, int cmd)
 {
-	int i;
 	struct sm_state *sm;
 	struct sm_ioctl bi;
 	unsigned long flags;
 	unsigned int newdiagmode;
 	unsigned int newdiagflags;
-	
+	char *cp;
+	const struct modem_tx_info *mtp = modem_tx_base;
+	const struct modem_rx_info *mrp = modem_rx_base;
+	const struct hardware_info *hwp = hardware_base;
+
 	if (!dev || !dev->priv || 
 	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
 		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
@@ -1810,132 +747,91 @@
 	}
 	sm = (struct sm_state *)dev->priv;
 
-	if (cmd != SIOCDEVPRIVATE)
+	if (cmd != SIOCDEVPRIVATE) {
+		if (!sm->hwdrv || !sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
 		return -ENOIOCTLCMD;
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
+	}
+	switch (hi->cmd) {
 	default:
+		if (sm->hwdrv && sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
 		return -ENOIOCTLCMD;
-
-	case SMCTL_GETMODEMTYPE:
-		bi.data.cfg.hardware = sm->config.hardware;
-		bi.data.cfg.mode = sm->config.mode;
-		break;
-
-	case SMCTL_SETMODEMTYPE:
+		
+	case HDLCDRVCTL_GETMODE:
+		cp = hi->data.modename;
+		if (sm->hwdrv && sm->hwdrv->hw_name)
+			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
+		else 
+			cp += sprintf(cp, "<unspec>:");
+		if (sm->mode_tx && sm->mode_tx->name)
+			cp += sprintf(cp, "%s", sm->mode_tx->name);
+		else 
+			cp += sprintf(cp, "<unspec>");
+		if (!sm->mode_rx || !sm->mode_rx || 
+		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
+			if (sm->mode_rx && sm->mode_rx->name)
+				cp += sprintf(cp, ",%s", sm->mode_rx->name);
+			else 
+				cp += sprintf(cp, ",<unspec>");
+		}
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+		
+	case HDLCDRVCTL_SETMODE:
 		if (!suser() || dev->start)
 			return -EACCES;
-		if (bi.data.cfg.hardware < SM_HARDWARE_INVALID ||
-		    bi.data.cfg.hardware > SM_HARDWARE_WSSFDX ||
-		    bi.data.cfg.mode < SM_MODE_INVALID ||
-		    bi.data.cfg.mode > SM_MODE_FSK9600)
-			return -EINVAL;
-		sm->config.hardware = bi.data.cfg.hardware;
-		sm->config.mode = bi.data.cfg.mode;
-		if (bi.data.cfg.hardware == SM_HARDWARE_INVALID ||
-		    bi.data.cfg.mode == SM_MODE_INVALID)
-			sm->hdrv.ops = &dummy_ops.hops;
-		else {
-			sm->hdrv.ops = &ops_tab[sm->config.hardware][sm->config.mode]->hops;
-			if (!((struct modem_info *)sm->hdrv.ops)->samplerate)
-				return -ENODEV;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return sethw(dev, sm, hi->data.modename);
+		
+	case HDLCDRVCTL_MODELIST:
+		cp = hi->data.modename;
+		while (hwp) {
+			if (hwp->hw_name)
+				cp += sprintf("%s:,", hwp->hw_name);
+			hwp = hwp->next;
+		}
+		while (mtp) {
+			if (mtp->name)
+				cp += sprintf(">%s,", mtp->name);
+			mtp = mtp->next;
+		}
+		while (mrp) {
+			if (mrp->name)
+				cp += sprintf("<%s,", mrp->name);
+			mrp = mrp->next;
 		}
+		cp[-1] = '\0';
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
 		return 0;
-
+		
 #ifdef SM_DEBUG
 	case SMCTL_GETDEBUG:
-		bi.data.dbg.debug1 = sm->hdrv.ptt_keyed;
-		bi.data.dbg.debug2 = sm->debug_vals.last_intcnt;
-		bi.data.dbg.debug3 = sm->debug_vals.last_pllcorr;
-		break;
-#endif /* SM_DEBUG */
-
-	case SMCTL_GETMIXER:
-		i = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(bi));
-		if (i)
-			return i;
-		i = 0;
-		bi.data.mix.sample_rate = ((struct modem_info *)sm->hdrv.ops)->samplerate;
-		bi.data.mix.bit_rate = sm->hdrv.ops->bitrate;
-		if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
-			switch (sm->modem.revhi) {
-			case 2:
-				bi.data.mix.mixer_type = SM_MIXER_CT1335;
-				break;
-			case 3:
-				bi.data.mix.mixer_type = SM_MIXER_CT1345;
-				break;
-			case 4:
-				bi.data.mix.mixer_type = SM_MIXER_CT1745;
-				break;
-			}
-			if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
-			    bi.data.mix.reg < 0x80) {
-				save_flags(flags);
-				cli();
-				outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-				bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
-				restore_flags(flags);
-				i = 1;
-			}
-		} else {
-			bi.data.mix.mixer_type = SM_MIXER_AD1848;
-			if ((0x20ff >> bi.data.mix.reg) & 1) {
-				bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
-				i = 1;
-			}
-		}
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
+		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
+		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
+		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
+		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc = 
+			sm->debug_vals.dma_residue = 0;
 		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
 			return -EFAULT;
-		return i;
-
-	case SMCTL_SETMIXER:
-		if (!suser())
-			return -EACCES;
-		if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
-			switch (sm->modem.revhi) {
-			case 2:
-				if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
-					return -EINVAL;
-				break;
-			case 3:
-				if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
-					return -EINVAL;
-				break;
-			case 4:
-				if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
-					return -EINVAL;
-				break;
-			default:
-				return -ENODEV;
-			}
-			if (bi.data.mix.reg >= 0x80)
-				return -EACCES;
-			save_flags(flags);
-			cli();
-			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-			outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
-			restore_flags(flags);
-			return 0;
-		} else {
-			if (bi.data.mix.mixer_type != SM_MIXER_AD1848)
-				return -EINVAL;
-			if (!((0x20ff >> bi.data.mix.reg) & 1))
-				return -EACCES;
-			write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
-			return 0;
-		}
+		return 0;
+#endif /* SM_DEBUG */
 
 	case SMCTL_DIAGNOSE:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
 		newdiagmode = bi.data.diag.mode;
 		newdiagflags = bi.data.diag.flags;
-		if (newdiagmode > SM_DIAGMODE_DEMOD)
+		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
 			return -EINVAL;
 		bi.data.diag.mode = sm->diag.mode;
 		bi.data.diag.flags = sm->diag.flags;
-		bi.data.diag.samplesperbit = ((struct modem_info *)sm->hdrv.ops)->sperbit;
+		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
 		if (sm->diag.mode != newdiagmode) {
 			save_flags(flags);
 			cli();
@@ -1943,19 +839,22 @@
 			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
 			sm->diag.mode = newdiagmode;
 			restore_flags(flags);
-			break;
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
 		}
-		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF)
-			break;
 		if (bi.data.diag.datalen > DIAGDATALEN)
 			bi.data.diag.datalen = DIAGDATALEN;
-		if (sm->diag.ptr < bi.data.diag.datalen)
-			break;
-		i = verify_area(VERIFY_WRITE, bi.data.diag.data,
-				bi.data.diag.datalen * 
-				sizeof(short));
-		if (i)
-			return i;
+		if (sm->diag.ptr < bi.data.diag.datalen) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
 		if (copy_to_user(bi.data.diag.data, sm->diag.data, 
 				 bi.data.diag.datalen * sizeof(short)))
 			return -EFAULT;
@@ -1966,13 +865,10 @@
 		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
 		sm->diag.mode = newdiagmode;
 		restore_flags(flags);
-		break;
-
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
 	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
 }
 
 /* --------------------------------------------------------------------- */
@@ -1987,7 +883,7 @@
 	struct sm_state *sm;
 	char ifname[HDLCDRV_IFNAMELEN];
 	
-	printk(KERN_INFO "sm: compiled %s %s\n", __TIME__, __DATE__);
+	printk(sm_drvinfo);
 	/*
 	 * register net devices
 	 */
@@ -1995,33 +891,26 @@
 		struct device *dev = sm_device+i;
 		sprintf(ifname, "sm%d", i);
 
-		if (sm_ports[i].hardware < SM_HARDWARE_INVALID ||
-		    sm_ports[i].hardware > SM_HARDWARE_WSSFDX ||
-		    sm_ports[i].mode < SM_MODE_INVALID ||
-		    sm_ports[i].mode > SM_MODE_FSK9600)
+		if (!sm_ports[i].mode)
 			set_hw = 0;
-		if (set_hw) {
-			j = hdlcdrv_register_hdlcdrv(dev, &ops_tab[sm_ports[i].hardware]
-						     [sm_ports[i].mode]->hops, 
-						     sizeof(struct sm_state), ifname, 
-						     sm_ports[i].iobase,
-						     sm_ports[i].irq,
-						     sm_ports[i].dma);
-			if (!j) {
-				sm = (struct sm_state *)dev->priv;
-				sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
-				sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
-				sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
-			}
-		} else 
-			j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops.hops, 
-						     sizeof(struct sm_state), 
-						     ifname, 0, 0, 0);
-		if (j) {
-			printk(KERN_WARNING "sm: cannot register net "
-			       "device\n");
-		} else
+		if (!set_hw)
+			sm_ports[i].iobase = sm_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), 
+					     ifname, sm_ports[i].iobase,
+					     sm_ports[i].irq, sm_ports[i].dma);
+		if (!j) {
+			sm = (struct sm_state *)dev->priv;
+			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
+			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
+				set_hw = 0;
 			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       sm_drvname);
+		}
 	}
 	if (!found)
 		return -ENXIO;
@@ -2035,36 +924,34 @@
 /*
  * command line settable parameters
  */
-int hardware = SM_HARDWARE_INVALID;
-int mode = SM_MODE_INVALID;
+char *mode = NULL;
 int iobase = -1;
 int irq = -1;
 int dma = -1;
-int seriobase = 0;
-int pariobase = 0;
-int midiiobase = 0;
+int dma2 = -1;
+int serio = 0;
+int pario = 0;
+int midiio = 0;
 
 int init_module(void)
 {
-	printk(KERN_INFO "sm: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
-
-	if (hardware != SM_HARDWARE_INVALID) {
+	if (mode) {
 		if (iobase == -1)
-			iobase = (hardware == SM_HARDWARE_SBC) ? 0x220 : 0x530;
+			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
 		if (irq == -1)
-			irq = (hardware == SM_HARDWARE_SBC) ? 5 : 11;
+			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
 		if (dma == -1)
 			dma = 1;
 	}
-	sm_ports[0].hardware = hardware;
 	sm_ports[0].mode = mode;
 	sm_ports[0].iobase = iobase;
 	sm_ports[0].irq = irq;
 	sm_ports[0].dma = dma;
-	sm_ports[0].seriobase = seriobase;
-	sm_ports[0].pariobase = pariobase;
-	sm_ports[0].midiiobase = midiiobase;
-	sm_ports[1].hardware = SM_HARDWARE_INVALID;
+	sm_ports[0].dma2 = dma2;
+	sm_ports[0].seriobase = serio;
+	sm_ports[0].pariobase = pario;
+	sm_ports[0].midiiobase = midiio;
+	sm_ports[1].mode = NULL;
 
 	return sm_init();
 }
@@ -2094,27 +981,32 @@
 #else /* MODULE */
 /* --------------------------------------------------------------------- */
 /*
- * format: sm=hw,mode,io,irq,dma,serio,pario[,hw,mode,io,irq,dma,serio,pario]
- * hw=0: SBC, hw=1: WSS; mode=0: AFSK1200, mode=1: FSK9600
+ * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
+ * mode: hw:modem
+ * hw: sbc, wss, wssfdx
+ * modem: afsk1200, fsk9600
  */
 
 void sm_setup(char *str, int *ints)
 {
 	int i;
 
-	for (i = 0; i < NR_PORTS; i++) 
-		if (ints[0] >= 8*(i+1)) {
-			sm_ports[i].hardware = ints[8*i+1];
-			sm_ports[i].mode = ints[8*i+2];
-			sm_ports[i].iobase = ints[8*i+3];
-			sm_ports[i].irq = ints[8*i+4];
-			sm_ports[i].dma = ints[8*i+5];
-			sm_ports[i].seriobase = ints[8*i+6];
-			sm_ports[i].pariobase = ints[8*i+7];
-			sm_ports[i].midiiobase = ints[8*i+8];
-		} else
-			sm_ports[i].hardware = SM_HARDWARE_INVALID;
-
+	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 4)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", sm_drvname);
+		return;
+	}
+	sm_ports[i].mode = str;
+	sm_ports[i].iobase = ints[1];
+	sm_ports[i].irq = ints[2];
+	sm_ports[i].dma = ints[3];
+	sm_ports[i].dma2 = (ints[0] >= 5) ? ints[4] : 0;
+	sm_ports[i].seriobase = (ints[0] >= 6) ? ints[5] : 0;
+	sm_ports[i].pariobase = (ints[0] >= 7) ? ints[6] : 0;
+	sm_ports[i].midiiobase = (ints[0] >= 8) ? ints[7] : 0;
+	if (i < NR_PORTS-1)
+		sm_ports[i+1].mode = NULL;
 }
 
 #endif /* MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov