patch-2.1.7 linux/drivers/net/soundmodem.c
Next file: linux/drivers/net/tulip.c
Previous file: linux/drivers/net/sm_tables.h
Back to the patch index
Back to the overall index
- Lines: 2121
- Date:
Wed Oct 30 14:15:12 1996
- Orig file:
v2.1.6/linux/drivers/net/soundmodem.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.6/linux/drivers/net/soundmodem.c linux/drivers/net/soundmodem.c
@@ -0,0 +1,2120 @@
+/*****************************************************************************/
+
+/*
+ * soundmodem.c -- soundcard radio modem driver.
+ *
+ * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please note that the GPL allows you to use the driver, NOT the radio.
+ * In order to use the radio, you need a license from the communications
+ * authority of your country.
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * hardware hardware type; 0=sbc, 1=wss, any other value invalid
+ * mode mode type; 0=1200 baud AFSK, 1=9600 baud FSK, any other
+ * value invalid
+ * iobase base address of the soundcard; common values are 0x220 for sbc,
+ * 0x530 for wss
+ * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ * dma dma number; common values are 0 or 1
+ *
+ *
+ * History:
+ * 0.1 21.09.96 Started
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <limits.h>
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+#define SM_DEBUG
+
+#define ENABLE_SBC
+#define ENABLE_WSS
+#undef ENABLE_WSSFDX
+
+#define ENABLE_AFSK1200
+#define ENABLE_FSK9600
+
+/* --------------------------------------------------------------------- */
+
+#include "sm_tables.h"
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+ int hardware, mode, iobase, irq, dma, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+{ SM_HARDWARE_INVALID, SM_MODE_INVALID, -1, 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)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase) (iobase+0)
+#define LPT_STATUS(iobase) (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE 0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase) (iobase)
+#define MIDI_STATUS(iobase) (iobase+1)
+#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY 1
+#define PARAM_PERSIST 2
+#define PARAM_SLOTTIME 3
+#define PARAM_TXTAIL 4
+#define PARAM_FULLDUP 5
+#define PARAM_HARDWARE 6
+#define PARAM_RETURN 255
+
+#define SP_SER 1
+#define SP_PAR 2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+ struct hdlcdrv_state hdrv;
+
+ struct config {
+ int hardware;
+ int mode;
+ } config;
+
+ 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;
+
+#define DIAGDATALEN 64
+ struct diag_data {
+ unsigned int mode;
+ unsigned int flags;
+ 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;
+ } debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+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;
+ void (*modulator)(struct sm_state *, unsigned char *, int);
+ void (*demodulator)(struct sm_state *, unsigned char *, int);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ sm->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+ sm->debug_vals.last_jiffies = cur_jiffies;
+ sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+ sm->debug_vals.cur_intcnt = 0;
+ }
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+static int inline check_lpt(unsigned int iobase)
+{
+ unsigned char b1,b2;
+ int i;
+
+ if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+ return 0;
+ if (check_region(iobase, LPT_EXTENT))
+ return 0;
+ b1 = inb(LPT_DATA(iobase));
+ b2 = inb(LPT_CONTROL(iobase));
+ outb(0xaa, LPT_DATA(iobase));
+ i = inb(LPT_DATA(iobase)) == 0xaa;
+ outb(0x55, LPT_DATA(iobase));
+ i &= inb(LPT_DATA(iobase)) == 0x55;
+ outb(0x0a, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+ outb(0x05, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+ outb(b1, LPT_DATA(iobase));
+ outb(b2, LPT_CONTROL(iobase));
+ return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+ c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+ { "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart inline check_uart(unsigned int iobase)
+{
+ unsigned char b1,b2,b3;
+ enum uart u;
+ enum uart uart_tab[] =
+ { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+ if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+ return c_uart_unknown;
+ if (check_region(iobase, SER_EXTENT))
+ return c_uart_unknown;
+ b1 = inb(UART_MCR(iobase));
+ outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */
+ b2 = inb(UART_MSR(iobase));
+ outb(0x1a, UART_MCR(iobase));
+ b3 = inb(UART_MSR(iobase)) & 0xf0;
+ outb(b1, UART_MCR(iobase)); /* restore old values */
+ outb(b2, UART_MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(UART_RBR(iobase));
+ inb(UART_RBR(iobase));
+ outb(0x01, UART_FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, UART_SCR(iobase));
+ b1 = inb(UART_SCR(iobase));
+ outb(0xa5, UART_SCR(iobase));
+ b2 = inb(UART_SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline check_midi(unsigned int iobase)
+{
+ unsigned long timeout;
+ unsigned long flags;
+ unsigned char b;
+
+ if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+ return 0;
+ if (check_region(iobase, MIDI_EXTENT))
+ return 0;
+ timeout = jiffies + (HZ / 100);
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ save_flags(flags);
+ cli();
+ outb(0xff, MIDI_DATA(iobase));
+ b = inb(MIDI_STATUS(iobase));
+ restore_flags(flags);
+ if (!(b & MIDI_WRITE_EMPTY))
+ return 0;
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline output_status(struct sm_state *sm)
+{
+ int invert_dcd = 0;
+ int invert_ptt = 0;
+
+ int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt;
+ int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+ if (sm->hdrv.ptt_out.flags & SP_SER) {
+ outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+ outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+ }
+ if (sm->hdrv.ptt_out.flags & SP_PAR) {
+ outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+ }
+ if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+ outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct sm_state *sm)
+{
+ enum uart u = c_uart_unknown;
+
+ sm->hdrv.ptt_out.flags = 0;
+ if (sm->hdrv.ptt_out.seriobase > 0 &&
+ sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+ ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+ sm->hdrv.ptt_out.flags |= SP_SER;
+ request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+ outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+ /* 5 bits, 1 stop, no parity, no break, Div latch access */
+ outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
+ outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+ outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+ /* LCR and MCR set by output_status */
+ }
+ if (sm->hdrv.ptt_out.pariobase > 0 &&
+ sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+ check_lpt(sm->hdrv.ptt_out.pariobase)) {
+ sm->hdrv.ptt_out.flags |= SP_PAR;
+ request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+ }
+ if (sm->hdrv.ptt_out.midiiobase > 0 &&
+ sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+ check_midi(sm->hdrv.ptt_out.midiiobase)) {
+ sm->hdrv.ptt_out.flags |= SP_MIDI;
+ request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
+ "sm midi ptt");
+ }
+ output_status(sm);
+
+ printk(KERN_INFO "sm: ptt output:");
+ if (sm->hdrv.ptt_out.flags & SP_SER)
+ printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+ uart_str[u]);
+ if (sm->hdrv.ptt_out.flags & SP_PAR)
+ printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+ if (sm->hdrv.ptt_out.flags & SP_MIDI)
+ printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+ if (!sm->hdrv.ptt_out.flags)
+ printk(" none");
+ printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct sm_state *sm)
+{
+ /* release regions used for PTT output */
+ sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+ output_status(sm);
+ if (sm->hdrv.ptt_out.flags & SP_SER)
+ release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+ if (sm->hdrv.ptt_out.flags & SP_PAR)
+ release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+ if (sm->hdrv.ptt_out.flags & SP_MIDI)
+ release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+ sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+static inline void diag_trigger(struct sm_state *sm)
+{
+ if (sm->diag.ptr < 0)
+ if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+ sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+ int val;
+
+ if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+ sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+ sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+ return;
+ val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+ /* clip */
+ if (val > SHRT_MAX)
+ val = SHRT_MAX;
+ if (val < SHRT_MIN)
+ val = SHRT_MIN;
+ sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_one(struct sm_state *sm, int val)
+{
+ if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+ sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+ sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+ return;
+ /* clip */
+ if (val > SHRT_MAX)
+ val = SHRT_MAX;
+ if (val < SHRT_MIN)
+ val = SHRT_MIN;
+ sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 1200 baud =========================
+ */
+
+static inline unsigned int hweight32(unsigned int w)
+ __attribute__ ((unused));
+static inline unsigned int hweight16(unsigned short w)
+ __attribute__ ((unused));
+static inline unsigned int hweight8(unsigned char w)
+ __attribute__ ((unused));
+
+static 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);
+}
+
+static inline unsigned int hweight16(unsigned short w)
+{
+ unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+ res = (res & 0x3333) + ((res >> 2) & 0x3333);
+ res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+ return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+static 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
+
+static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ 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;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ 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);
+ }
+}
+
+#endif /* ENABLE_AFSK1200 */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 9600 baud =========================
+ */
+
+#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 */
+
+/* --------------------------------------------------------------------- */
+
+#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 ===================
+ */
+
+#ifdef ENABLE_SBC
+
+static int inline reset_dsp(struct device *dev)
+{
+ int i;
+
+ 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;
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack(struct device *dev)
+{
+ inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+
+ 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 int sbc_close(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (!dev || !sm)
+ 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;
+
+ 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))
+ 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);
+ 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;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (!dev || !sm)
+ 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);
+ }
+
+ /*
+ * 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);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev)
+{
+ 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;
+ }
+ 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;
+}
+
+#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)
+{
+ int i;
+ struct sm_state *sm;
+ struct sm_ioctl bi;
+ unsigned long flags;
+ unsigned int newdiagmode;
+ unsigned int newdiagflags;
+
+ if (!dev || !dev->priv ||
+ ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ sm = (struct sm_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case SMCTL_GETMODEMTYPE:
+ bi.data.cfg.hardware = sm->config.hardware;
+ bi.data.cfg.mode = sm->config.mode;
+ break;
+
+ case SMCTL_SETMODEMTYPE:
+ 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;
+ }
+ 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_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;
+ }
+
+ case SMCTL_DIAGNOSE:
+ newdiagmode = bi.data.diag.mode;
+ newdiagflags = bi.data.diag.flags;
+ if (newdiagmode > SM_DIAGMODE_DEMOD)
+ 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;
+ if (sm->diag.mode != newdiagmode) {
+ save_flags(flags);
+ cli();
+ sm->diag.ptr = -1;
+ sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+ sm->diag.mode = newdiagmode;
+ restore_flags(flags);
+ break;
+ }
+ 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 (copy_to_user(bi.data.diag.data, sm->diag.data,
+ bi.data.diag.datalen * sizeof(short)))
+ return -EFAULT;
+ bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+ save_flags(flags);
+ cli();
+ sm->diag.ptr = -1;
+ 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;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int sm_init(void)
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct sm_state *sm;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+ printk(KERN_INFO "sm: compiled %s %s\n", __TIME__, __DATE__);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ 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)
+ 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
+ found++;
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+int hardware = SM_HARDWARE_INVALID;
+int mode = SM_MODE_INVALID;
+int iobase = -1;
+int irq = -1;
+int dma = -1;
+int seriobase = 0;
+int pariobase = 0;
+int midiiobase = 0;
+
+int init_module(void)
+{
+ printk(KERN_INFO "sm: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+
+ if (hardware != SM_HARDWARE_INVALID) {
+ if (iobase == -1)
+ iobase = (hardware == SM_HARDWARE_SBC) ? 0x220 : 0x530;
+ if (irq == -1)
+ irq = (hardware == SM_HARDWARE_SBC) ? 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;
+
+ return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ printk(KERN_INFO "sm: cleanup_module called\n");
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = sm_device+i;
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (sm) {
+ if (sm->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "sm: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#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
+ */
+
+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;
+
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov