summaryrefslogtreecommitdiffstats
path: root/drivers/char/hfmodem
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /drivers/char/hfmodem
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'drivers/char/hfmodem')
-rw-r--r--drivers/char/hfmodem/.cvsignore3
-rw-r--r--drivers/char/hfmodem/Makefile37
-rw-r--r--drivers/char/hfmodem/gentbl.c69
-rw-r--r--drivers/char/hfmodem/main.c736
-rw-r--r--drivers/char/hfmodem/modem.c792
-rw-r--r--drivers/char/hfmodem/refclock.c189
-rw-r--r--drivers/char/hfmodem/sbc.c741
-rw-r--r--drivers/char/hfmodem/wss.c437
8 files changed, 3004 insertions, 0 deletions
diff --git a/drivers/char/hfmodem/.cvsignore b/drivers/char/hfmodem/.cvsignore
new file mode 100644
index 000000000..add4bedde
--- /dev/null
+++ b/drivers/char/hfmodem/.cvsignore
@@ -0,0 +1,3 @@
+.depend
+tables.h
+gentbl
diff --git a/drivers/char/hfmodem/Makefile b/drivers/char/hfmodem/Makefile
new file mode 100644
index 000000000..42629693b
--- /dev/null
+++ b/drivers/char/hfmodem/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the hfmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET := hfmodem.o
+
+O_OBJS := refclock.o modem.o main.o
+ifeq ($(CONFIG_HFMODEM_SBC),y)
+O_OBJS += sbc.o
+endif
+ifeq ($(CONFIG_HFMODEM_WSS),y)
+O_OBJS += wss.o
+endif
+
+M_OBJS := $(O_TARGET)
+
+all: all_targets
+.PHONY: all
+
+gentbl: gentbl.c
+ $(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR := tables.h
+
+tables.h: gentbl
+ ./gentbl > $@
+
+fastdep: $(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/char/hfmodem/gentbl.c b/drivers/char/hfmodem/gentbl.c
new file mode 100644
index 000000000..558b9a405
--- /dev/null
+++ b/drivers/char/hfmodem/gentbl.c
@@ -0,0 +1,69 @@
+/*****************************************************************************/
+
+/*
+ * gentbl.c -- Linux soundcard HF FSK driver,
+ * Table generator.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/hfmodem.h>
+#include <math.h>
+#include <stdio.h>
+
+/* --------------------------------------------------------------------- */
+
+#define SINTABBITS 9
+#define SINTABSIZE (1<<SINTABBITS)
+
+/* --------------------------------------------------------------------- */
+
+static void gensintbl(void)
+{
+ int i;
+
+ printf("#define SINTABBITS %d\n#define SINTABSIZE (1<<SINTABBITS)\n"
+ "\nstatic short isintab[SINTABSIZE+SINTABSIZE/4] = {\n\t", SINTABBITS);
+ for (i = 0; i < (SINTABSIZE+SINTABSIZE/4); i++) {
+ printf("%6d", (int)(32767.0 * sin(2.0 * M_PI / SINTABSIZE * i)));
+ if (i < (SINTABSIZE+SINTABSIZE/4)-1) {
+ if ((i & 7) == 7)
+ printf(",\n\t");
+ else
+ printf(",");
+ }
+ }
+ printf("\n};\n\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n",
+ argv[0]);
+ gensintbl();
+ exit(0);
+}
+
+/* --------------------------------------------------------------------- */
+
diff --git a/drivers/char/hfmodem/main.c b/drivers/char/hfmodem/main.c
new file mode 100644
index 000000000..eb30cec3e
--- /dev/null
+++ b/drivers/char/hfmodem/main.c
@@ -0,0 +1,736 @@
+/*****************************************************************************/
+
+/*
+ * main.c -- Linux soundcard HF FSK driver.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ * Command line options (insmod command line)
+ *
+ * History:
+ * 0.1 15.04.97 Adapted from baycom.c and made network driver interface
+ * 0.2 05.07.97 All floating point stuff thrown out due to Linus' rantings :)
+ *
+ */
+
+/*****************************************************************************/
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_READ, from, n);
+ if (i)
+ return i;
+ memcpy_fromfs(to, from, n);
+ return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_WRITE, to, n);
+ if (i)
+ return i;
+ memcpy_tofs(to, from, n);
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const char hfmodem_drvname[] = "hfmodem";
+static const char hfmodem_drvinfo[] = KERN_INFO "hfmodem: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "hfmodem: version 0.2 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+/*
+ * currently we support only one device
+ */
+
+struct hfmodem_state hfmodem_state[NR_DEVICE];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+
+#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 SP_SER 1
+#define SP_PAR 2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+__initfunc(static int 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[] __initdata = { "unknown", "8250", "16450", "16550", "16550A" };
+
+__initfunc(static enum uart 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;
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static int 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 output_status(struct hfmodem_state *dev, int ptt)
+{
+ int dcd = 0;
+
+ ptt = !!ptt;
+ if (dev->ptt_out.flags & SP_SER) {
+ outb(dcd | (ptt << 1), UART_MCR(dev->ptt_out.seriobase));
+ outb(0x40 & (-ptt), UART_LCR(dev->ptt_out.seriobase));
+ }
+ if (dev->ptt_out.flags & SP_PAR) {
+ outb(ptt | (dcd << 1), LPT_DATA(dev->ptt_out.pariobase));
+ }
+ if (dev->ptt_out.flags & SP_MIDI && ptt) {
+ outb(0, MIDI_DATA(dev->ptt_out.midiiobase));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static void output_check(struct hfmodem_state *dev))
+{
+ enum uart u = c_uart_unknown;
+
+ if (dev->ptt_out.seriobase > 0 && dev->ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+ ((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown)
+ printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n",
+ hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]);
+ else {
+ if (dev->ptt_out.seriobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no uart found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.seriobase);
+ dev->ptt_out.seriobase = 0;
+ }
+ if (dev->ptt_out.pariobase > 0 && dev->ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+ !check_lpt(dev->ptt_out.pariobase))
+ printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ else {
+ if (dev->ptt_out.pariobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ dev->ptt_out.pariobase = 0;
+ }
+ if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+ check_midi(dev->ptt_out.midiiobase))
+ printk(KERN_INFO "%s: PTT output: midi port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ else {
+ if (dev->ptt_out.midiiobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no midi port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ dev->ptt_out.midiiobase = 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct hfmodem_state *dev)
+{
+ dev->ptt_out.flags = 0;
+ if (dev->ptt_out.seriobase > 0) {
+ if (!check_region(dev->ptt_out.seriobase, SER_EXTENT)) {
+ request_region(dev->ptt_out.seriobase, SER_EXTENT, "hfmodem ser ptt");
+ dev->ptt_out.flags |= SP_SER;
+ outb(0, UART_IER(dev->ptt_out.seriobase));
+ /* 5 bits, 1 stop, no parity, no break, Div latch access */
+ outb(0x80, UART_LCR(dev->ptt_out.seriobase));
+ outb(0, UART_DLM(dev->ptt_out.seriobase));
+ outb(1, UART_DLL(dev->ptt_out.seriobase)); /* as fast as possible */
+ /* LCR and MCR set by output_status */
+ } else
+ printk(KERN_WARNING "%s: PTT output: serial port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.seriobase);
+ }
+ if (dev->ptt_out.pariobase > 0) {
+ if (!check_region(dev->ptt_out.pariobase, LPT_EXTENT)) {
+ request_region(dev->ptt_out.pariobase, LPT_EXTENT, "hfmodem par ptt");
+ dev->ptt_out.flags |= SP_PAR;
+ } else
+ printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ }
+ if (dev->ptt_out.midiiobase > 0) {
+ if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) {
+ request_region(dev->ptt_out.midiiobase, MIDI_EXTENT, "hfmodem midi ptt");
+ dev->ptt_out.flags |= SP_MIDI;
+ } else
+ printk(KERN_WARNING "%s: PTT output: midi port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ }
+ output_status(dev, 0);
+ printk(KERN_INFO "%s: PTT output:", hfmodem_drvname);
+ if (dev->ptt_out.flags & SP_SER)
+ printk(" serial interface at 0x%x", dev->ptt_out.seriobase);
+ if (dev->ptt_out.flags & SP_PAR)
+ printk(" parallel interface at 0x%x", dev->ptt_out.pariobase);
+ if (dev->ptt_out.flags & SP_MIDI)
+ printk(" mpu401 (midi) interface at 0x%x", dev->ptt_out.midiiobase);
+ if (!dev->ptt_out.flags)
+ printk(" none");
+ printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct hfmodem_state *dev)
+{
+ /* release regions used for PTT output */
+ output_status(dev, 0);
+ if (dev->ptt_out.flags & SP_SER)
+ release_region(dev->ptt_out.seriobase, SER_EXTENT);
+ if (dev->ptt_out.flags & SP_PAR)
+ release_region(dev->ptt_out.pariobase, LPT_EXTENT);
+ if (dev->ptt_out.flags & SP_MIDI)
+ release_region(dev->ptt_out.midiiobase, MIDI_EXTENT);
+ dev->ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define INC_SAMPLE (1000000/HFMODEM_SRATE)
+#define INC_FRAGMENT (HFMODEM_FRAGSAMPLES*1000000/HFMODEM_SRATE)
+#define SIZE (HFMODEM_FRAGSAMPLES*HFMODEM_NUMFRAGS)
+
+static void hfmodem_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct hfmodem_state *dev = (struct hfmodem_state *)dev_id;
+ unsigned int dmaptr;
+ __s16 *s;
+ unsigned int curfrag, nfrags;
+ int i;
+ hfmodem_time_t l1time;
+
+ dmaptr = dev->scops->intack(dev);
+ l1time = hfmodem_refclock_current(dev, ((SIZE+dmaptr-dev->dma.last_dmaptr) % SIZE) *
+ INC_SAMPLE, 1);
+ curfrag = (dev->dma.last_dmaptr = dmaptr) / HFMODEM_FRAGSAMPLES;
+ l1time -= INC_SAMPLE * (SIZE+dmaptr-dev->dma.fragptr*HFMODEM_FRAGSAMPLES) % SIZE;
+ sti();
+ /*
+ * handle receiving
+ */
+ if (dev->dma.ptt_frames <= 0) {
+ while (dev->dma.fragptr != curfrag) {
+ if (dev->dma.fragptr < HFMODEM_EXCESSFRAGS) {
+ s = dev->dma.buf + SIZE + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+ memcpy(s, s - SIZE, HFMODEM_FRAGSIZE);
+ } else
+ s = dev->dma.buf + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+ if (dev->sbuf.kbuf && dev->sbuf.kptr && dev->sbuf.rem > 0) {
+ i = HFMODEM_FRAGSAMPLES;
+ if (i > dev->sbuf.rem)
+ i = dev->sbuf.rem;
+ memcpy(dev->sbuf.kptr, s, i * sizeof(s[0]));
+ dev->sbuf.rem -= i;
+ dev->sbuf.kptr += i;
+ }
+ hfmodem_input_samples(dev, l1time, INC_SAMPLE, s);
+ l1time += INC_FRAGMENT;
+ dev->dma.fragptr++;
+ if (dev->dma.fragptr >= HFMODEM_NUMFRAGS)
+ dev->dma.fragptr = 0;
+ }
+ /*
+ * check for output
+ */
+ if (hfmodem_next_tx_event(dev, l1time) > (long)INC_FRAGMENT/2)
+ goto int_return;
+ /*
+ * start output
+ */
+ output_status(dev, 1);
+ dev->scops->prepare_output(dev);
+ dev->dma.last_dmaptr = 0;
+ /*
+ * clock adjust
+ */
+ l1time = hfmodem_refclock_current(dev, 0, 0);
+ /*
+ * fill first two fragments
+ */
+ dev->dma.ptt_frames = 1;
+ for (i = 0; i < 2 && i < HFMODEM_NUMFRAGS; i++)
+ if (hfmodem_output_samples(dev, l1time+i*INC_FRAGMENT, INC_SAMPLE,
+ dev->dma.buf+i*HFMODEM_FRAGSAMPLES))
+ dev->dma.ptt_frames = i + 1;
+ dev->dma.lastfrag = 0;
+ dev->scops->trigger_output(dev);
+ /*
+ * finish already pending rx requests
+ */
+ hfmodem_finish_pending_rx_requests(dev);
+ goto int_return;
+ }
+ /*
+ * handle transmitting
+ */
+ nfrags = HFMODEM_NUMFRAGS + curfrag - dev->dma.lastfrag;
+ dev->dma.lastfrag = curfrag;
+ if (nfrags >= HFMODEM_NUMFRAGS)
+ nfrags -= HFMODEM_NUMFRAGS;
+ dev->dma.ptt_frames -= nfrags;
+ if (dev->dma.ptt_frames < 0)
+ dev->dma.ptt_frames = 0;
+ while (dev->dma.ptt_frames < HFMODEM_NUMFRAGS && dev->dma.ptt_frames < 4 &&
+ hfmodem_output_samples(dev, l1time+dev->dma.ptt_frames*INC_FRAGMENT,
+ INC_SAMPLE, dev->dma.buf + HFMODEM_FRAGSAMPLES *
+ ((curfrag + dev->dma.ptt_frames) % HFMODEM_NUMFRAGS)))
+ dev->dma.ptt_frames++;
+ if (dev->dma.ptt_frames > 0)
+ goto int_return;
+ /*
+ * start receiving
+ */
+ output_status(dev, 0);
+ dev->dma.last_dmaptr = 0;
+ dev->dma.lastfrag = 0;
+ dev->dma.fragptr = 0;
+ dev->dma.ptt_frames = 0;
+ dev->scops->prepare_input(dev);
+ dev->scops->trigger_input(dev);
+ hfmodem_refclock_current(dev, 0, 0); /* needed to reset the time difference */
+int_return:
+ hfmodem_wakeup(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_close(struct inode *inode, struct file *file)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+
+ if (!dev->active)
+ return -EPERM;
+ dev->active = 0;
+ dev->scops->stop(dev);
+ free_irq(dev->io.irq, dev);
+ disable_dma(dev->io.dma);
+ free_dma(dev->io.dma);
+ release_region(dev->io.base_addr, dev->scops->extent);
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ hfmodem_clear_rq(dev);
+ if (dev->sbuf.kbuf) {
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ }
+ output_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_open(struct inode *inode, struct file *file)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+
+ if (dev->active)
+ return -EBUSY;
+ if (!dev->scops)
+ return -EPERM;
+ /*
+ * clear vars
+ */
+ memset(&dev->l1, 0, sizeof(dev->l1));
+ dev->dma.last_dmaptr = 0;
+ dev->dma.lastfrag = 0;
+ dev->dma.fragptr = 0;
+ dev->dma.ptt_frames = 0;
+ /*
+ * allocate memory
+ */
+ if (!(dev->dma.buf = kmalloc(HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS), GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ /*
+ * allocate resources
+ */
+ if (request_dma(dev->io.dma, hfmodem_drvname)) {
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ return -EBUSY;
+ }
+ if (request_irq(dev->io.irq, hfmodem_interrupt, SA_INTERRUPT, hfmodem_drvname, dev)) {
+ free_dma(dev->io.dma);
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ return -EBUSY;
+ }
+ request_region(dev->io.base_addr, dev->scops->extent, hfmodem_drvname);
+
+ /* clear requests */
+ dev->active++;
+ MOD_INC_USE_COUNT;
+ hfmodem_refclock_init(dev);
+ output_open(dev);
+ dev->scops->init(dev);
+ dev->scops->prepare_input(dev);
+ dev->scops->trigger_input(dev);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct file_operations hfmodem_fops = {
+ NULL, /* hfmodem_seek */
+ NULL, /* hfmodem_read */
+ NULL, /* hfmodem_write */
+ NULL, /* hfmodem_readdir */
+#if LINUX_VERSION_CODE >= 0x20100
+ hfmodem_poll, /* hfmodem_poll */
+#else
+ hfmodem_select, /* hfmodem_select */
+#endif
+ hfmodem_ioctl, /* hfmodem_ioctl */
+ NULL, /* hfmodem_mmap */
+ hfmodem_open, /* hfmodem_open */
+ hfmodem_close, /* hfmodem_close */
+ NULL, /* hfmodem_fsync */
+ NULL, /* hfmodem_fasync */
+ NULL, /* hfmodem_check_media_change */
+ NULL /* hfmodem_revalidate */
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct miscdevice hfmodem_device = {
+ HFMODEM_MINOR, hfmodem_drvname, &hfmodem_fops
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * Command line parameters
+ */
+
+static int hw = 0;
+static unsigned int iobase = 0x220;
+static unsigned int irq = 7;
+static unsigned int dma = 1;
+
+static unsigned int serio = 0;
+static unsigned int pario = 0;
+static unsigned int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(hw, "i");
+MODULE_PARM_DESC(hw, "hardware type: 0=SBC, 1=WSS");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "io base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "interrupt number");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "dma number (>=4 for SB16/32/64/etc, <=3 for the rest)");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "address of serial port to output PTT");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "address of parial port to output PTT");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("HF FSK modem code");
+
+/* these are the module parameters from refclock.c */
+
+MODULE_PARM(scale_tvusec, "i");
+MODULE_PARM_DESC(scale_tvusec, "Scaling value for the tv_usec field (can be obta
+ined by refclock)");
+
+#ifdef __i386__
+MODULE_PARM(scale_rdtsc, "i");
+MODULE_PARM_DESC(scale_rdtsc, "Scaling value for the rdtsc counter (can be obtai
+ned by refclock)");
+MODULE_PARM(rdtsc_ok, "i");
+MODULE_PARM_DESC(rdtsc_ok, "Set to 0 to disable the use of the rdtsc instruction
+");
+#endif /* __i386__ */
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ int i;
+
+ printk(hfmodem_drvinfo);
+ memset(hfmodem_state, 0, sizeof(hfmodem_state));
+ memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+ hfmodem_state[0].io.base_addr = iobase;
+ hfmodem_state[0].io.irq = irq;
+ hfmodem_state[0].io.dma = dma;
+ hfmodem_state[0].ptt_out.seriobase = serio;
+ hfmodem_state[0].ptt_out.pariobase = pario;
+ hfmodem_state[0].ptt_out.midiiobase = midiio;
+ hfmodem_refclock_probe();
+ output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+ if (hw)
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+ else
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+ i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+ if (i)
+ return i;
+ if ((i = misc_register(&hfmodem_device))) {
+ printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+ return i;
+ }
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&hfmodem_device);
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+
+static int hw = 0;
+
+__initfunc(void hfmodem_setup(char *str, int *ints))
+{
+ if (ints[0] < 7) {
+ printk(KERN_WARNING "%s: setup: too few parameters\n", hfmodem_drvname);
+ return;
+ }
+ memset(hfmodem_state, 0, sizeof(hfmodem_state));
+ memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+ hw = ints[1];
+ hfmodem_state[0].io.base_addr = ints[2];
+ hfmodem_state[0].io.irq = ints[3];
+ hfmodem_state[0].io.dma = ints[4];
+ if (ints[0] >= 8)
+ hfmodem_state[0].ptt_out.seriobase = ints[5];
+ if (ints[0] >= 9)
+ hfmodem_state[0].ptt_out.pariobase = ints[6];
+ if (ints[0] >= 10)
+ hfmodem_state[0].ptt_out.midiiobase = ints[7];
+ hfmodem_refclock_setscale(ints[ints[0]-2], ints[ints[0]-1], ints[ints[0]]);
+}
+
+__initfunc(void hfmodem_init(void))
+{
+ int i;
+
+ printk(hfmodem_drvinfo);
+ hfmodem_refclock_probe();
+ output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+ if (hw)
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+ else
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+ i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+ if (i) {
+ printk(KERN_ERR "%s: soundcard probe failed\n", hfmodem_drvname);
+ return;
+ }
+ if ((i = misc_register(&hfmodem_device))) {
+ printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+ return;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* MODULE */
+
diff --git a/drivers/char/hfmodem/modem.c b/drivers/char/hfmodem/modem.c
new file mode 100644
index 000000000..4b21edd09
--- /dev/null
+++ b/drivers/char/hfmodem/modem.c
@@ -0,0 +1,792 @@
+/*****************************************************************************/
+
+/*
+ * modem.c -- Linux soundcard HF FSK driver,
+ * Modem code.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_READ, from, n);
+ if (i)
+ return i;
+ memcpy_fromfs(to, from, n);
+ return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_WRITE, to, n);
+ if (i)
+ return i;
+ memcpy_tofs(to, from, n);
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+
+#include "tables.h"
+
+#define M_PI 3.14159265358979323846 /* pi */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int isimplecos(unsigned int arg)
+{
+ return isintab[((arg+0x4000) >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+extern __inline__ int isimplesin(unsigned int arg)
+{
+ return isintab[(arg >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int itblcos(unsigned int arg)
+{
+ unsigned int x;
+ int dx;
+ int s, c;
+
+ x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+ dx = arg - x;
+ x >>= (16-SINTABBITS);
+ c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+ s = isintab[x];
+ return c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void itblcossin(unsigned int arg, int *cos, int *sin)
+{
+ unsigned int x;
+ int dx;
+ int s, c;
+
+ x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+ dx = arg - x;
+ x >>= (16-SINTABBITS);
+ c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+ s = isintab[x];
+ *cos = c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+ *sin = s + ((c * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+extern __inline__ unsigned short random_num(void)
+{
+ random_seed = 28629 * random_seed + 157;
+ return random_seed;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * correlator cache routines
+ */
+
+extern __inline__ void cc_lock(unsigned int u)
+{
+ if (u >= HFMODEM_CORRELATOR_CACHE)
+ return;
+ hfmodem_correlator_cache[u].refcnt++;
+}
+
+extern __inline__ void cc_unlock(unsigned int u)
+{
+ if (u >= HFMODEM_CORRELATOR_CACHE)
+ return;
+ if ((--hfmodem_correlator_cache[u].refcnt) <= 0) {
+ unsigned int i;
+
+ for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++)
+ if (hfmodem_correlator_cache[i].lru < 32767)
+ hfmodem_correlator_cache[i].lru++;
+ hfmodem_correlator_cache[u].lru = 0;
+ hfmodem_correlator_cache[u].refcnt = 0;
+ }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_lookup(unsigned short phinc0, unsigned short phinc1)
+{
+ unsigned int j;
+
+ /* find correlator cache entry */
+ for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++)
+ if (hfmodem_correlator_cache[j].phase_incs[0] == phinc0 &&
+ hfmodem_correlator_cache[j].phase_incs[1] == phinc1)
+ return j;
+ return ~0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_replace(void)
+{
+ unsigned int j, k = HFMODEM_CORRELATOR_CACHE;
+ int l = -1;
+
+ for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++)
+ if (hfmodem_correlator_cache[j].refcnt <= 0 && hfmodem_correlator_cache[j].lru > l) {
+ k = j;
+ l = hfmodem_correlator_cache[j].lru;
+ }
+ if (k < HFMODEM_CORRELATOR_CACHE)
+ return k;
+ printk(KERN_ERR "%s: modem: out of filter coefficient cache entries\n", hfmodem_drvname);
+ return random_num() % HFMODEM_CORRELATOR_CACHE;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SH1 8 /* min. ceil(log2(L1CORR_LEN)) - (31-(2*15-1)) */
+#define SH2 (3*15-2*SH1)
+
+#ifdef __i386__
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+ int ret, rethi, tmp1 = 0, tmp2 = 0;
+
+ __asm__("\n1:\n\t"
+ "movswl (%0),%%eax\n\t"
+ "imull (%1)\n\t"
+ "subl $2,%0\n\t"
+ "addl $4,%1\n\t"
+ "addl %%eax,%3\n\t"
+ "adcl %%edx,%4\n\t"
+ "decl %2\n\t"
+ "jne 1b\n\t"
+ : "=&S" (inp), "=&D" (coeff), "=&c" (n), "=m" (tmp1), "=m" (tmp2)
+ : "0" (inp), "1" (coeff), "2" (n)
+ : "ax", "dx");
+ __asm__("shrdl %2,%1,%0\n\t"
+ "# sarl %2,%1\n\t"
+ : "=&r" (ret), "=&r" (rethi)
+ : "i" (SH1), "0" (tmp1), "1" (tmp2));
+
+
+ return ret;
+}
+
+#else /* __i386__ */
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+ long long sum = 0;
+ int i;
+
+ for (i = n; i > 0; i--, coeff++, inp--)
+ sum += (*coeff) * (*inp);
+ sum >>= SH1;
+ return sum;
+}
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ long long isqr(int x) __attribute__ ((const));
+
+extern __inline__ long long isqr(int x)
+{
+ return ((long long)x) * ((long long)x);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ hfmodem_soft_t do_filter(struct hfmodem_l1_rxslot *slot, short *s)
+{
+ unsigned int cc = slot->corr_cache;
+ long long ll;
+
+ if (cc >= HFMODEM_CORRELATOR_CACHE) {
+ printk(KERN_ERR "do_filter: correlator cache index overrange\n");
+ return 0;
+ }
+ ll = isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][0], s)) +
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][1], s)) -
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][0], s)) -
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][1], s));
+ ll >>= SH2;
+ return (ll * slot->scale) >> 23;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void cc_prepare(struct hfmodem_l1_rxslot *slot, unsigned short phinc0, unsigned short phinc1)
+{
+ unsigned int j, k, l, ph, phinc;
+
+ slot->scale = (1<<23) / (slot->corrlen*slot->corrlen);
+
+ j = cc_lookup(phinc0, phinc1);
+ if (j >= HFMODEM_CORRELATOR_CACHE) {
+ j = cc_replace();
+ /* calculate the correlator values */
+ printk(KERN_DEBUG "%s: corr cache calc: %u phases: 0x%04x 0x%04x\n",
+ hfmodem_drvname, j, phinc0, phinc1);
+ hfmodem_correlator_cache[j].phase_incs[0] = phinc0;
+ hfmodem_correlator_cache[j].phase_incs[1] = phinc1;
+ for (k = 0; k < 2; k++) {
+ phinc = hfmodem_correlator_cache[j].phase_incs[k];
+ for (ph = l = 0; l < HFMODEM_MAXCORRLEN; l++, ph = (ph + phinc) & 0xffff)
+ itblcossin(ph, &hfmodem_correlator_cache[j].correlator[k][0][l],
+ &hfmodem_correlator_cache[j].correlator[k][1][l]);
+ }
+ hfmodem_correlator_cache[j].refcnt = 0;
+
+#if 0
+ printk(KERN_DEBUG "%s: corr: %u ph: 0x%04x 0x%04x\n", hfmodem_drvname, j,
+ hfmodem_correlator_cache[j].phase_incs[0],
+ hfmodem_correlator_cache[j].phase_incs[1]);
+ for (l = 0; l < HFMODEM_MAXCORRLEN; l++)
+ printk(KERN_DEBUG "%s: corr: %6d %6d %6d %6d\n", hfmodem_drvname,
+ hfmodem_correlator_cache[j].correlator[0][0][l],
+ hfmodem_correlator_cache[j].correlator[0][1][l],
+ hfmodem_correlator_cache[j].correlator[1][0][l],
+ hfmodem_correlator_cache[j].correlator[1][1][l]);
+#endif
+ }
+ slot->corr_cache = j;
+ cc_lock(j);
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_clear_rq(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_unused)
+ continue;
+ dev->l1.rxslots[i].state = ss_unused;
+ kfree_s(dev->l1.rxslots[i].data, dev->l1.rxslots[i].nbits * sizeof(hfmodem_soft_t));
+ }
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_unused)
+ continue;
+ dev->l1.txslots[i].state = ss_unused;
+ kfree_s(dev->l1.txslots[i].data, (dev->l1.txslots[i].nbits + 7) >> 3);
+ }
+ for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++)
+ hfmodem_correlator_cache[i].refcnt = 0;
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ struct hfmodem_ioctl_fsk_tx_request txrq;
+ struct hfmodem_ioctl_fsk_rx_request rxrq;
+ struct hfmodem_ioctl_mixer_params mix;
+ struct hfmodem_ioctl_sample_params spar;
+ unsigned long flags;
+ unsigned int len;
+ int ret, i, idx;
+ void *data, *userdata;
+ hfmodem_id_t id;
+ hfmodem_time_t tm = 0;
+
+ if (!dev->active)
+ return -EBUSY;
+ switch(cmd) {
+ default:
+ return -EINVAL;
+
+ case HFMODEM_IOCTL_FSKTXREQUEST:
+ if ((ret = copy_from_user(&txrq, (void *)arg, sizeof(txrq))))
+ return ret;
+ if (txrq.nbits > HFMODEM_MAXBITS)
+ return -EINVAL;
+ len = (txrq.nbits + 7) >> 3;
+ if (!(data = kmalloc(len, GFP_KERNEL)))
+ return -ENOMEM;
+ if (copy_from_user(data, txrq.data, len)) {
+ kfree_s(data, len);
+ return -EFAULT;
+ }
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMTXSLOTS && dev->l1.txslots[i].state != ss_unused; i++);
+ if (i >= HFMODEM_NUMTXSLOTS) {
+ restore_flags(flags);
+ kfree_s(data, len);
+ return -EBUSY;
+ }
+ dev->l1.txslots[i].state = ss_ready;
+ dev->l1.txslots[i].tstart = txrq.tstart;
+ dev->l1.txslots[i].tinc = txrq.tinc;
+ dev->l1.txslots[i].data = data;
+ dev->l1.txslots[i].nbits = txrq.nbits;
+ dev->l1.txslots[i].cntbits = 0;
+ dev->l1.txslots[i].inv = txrq.inv ? 0xff : 0;
+ dev->l1.txslots[i].id = txrq.id;
+ dev->l1.txslots[i].phase_incs[0] = ((txrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+ & 0xffff;
+ dev->l1.txslots[i].phase_incs[1] = ((txrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+ & 0xffff;
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_FSKRXREQUEST:
+ if ((ret = copy_from_user(&rxrq, (void *)arg, sizeof(rxrq))))
+ return ret;
+ if (rxrq.nbits > HFMODEM_MAXBITS)
+ return -EINVAL;
+ if (rxrq.baud < HFMODEM_MINBAUD || rxrq.baud > HFMODEM_MAXBAUD)
+ return -EINVAL;
+ len = rxrq.nbits * sizeof(hfmodem_soft_t);
+ if (verify_area(VERIFY_WRITE, rxrq.data, len))
+ return -EFAULT;
+ if (!(data = kmalloc(len, GFP_KERNEL)))
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMRXSLOTS && dev->l1.rxslots[i].state != ss_unused; i++);
+ if (i >= HFMODEM_NUMRXSLOTS) {
+ restore_flags(flags);
+ kfree_s(data, len);
+ return -EBUSY;
+ }
+ dev->l1.rxslots[i].state = ss_ready;
+ dev->l1.rxslots[i].tstart = rxrq.tstart;
+ dev->l1.rxslots[i].tinc = rxrq.tinc;
+ dev->l1.rxslots[i].data = data;
+ dev->l1.rxslots[i].userdata = rxrq.data;
+ dev->l1.rxslots[i].nbits = rxrq.nbits;
+ dev->l1.rxslots[i].cntbits = 0;
+ dev->l1.rxslots[i].id = rxrq.id;
+ dev->l1.rxslots[i].corrlen = HFMODEM_SRATE/rxrq.baud;
+ cc_prepare(dev->l1.rxslots+i,
+ ((rxrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff,
+ ((rxrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff);
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_CLEARRQ:
+ hfmodem_clear_rq(dev);
+ return 0;
+
+ case HFMODEM_IOCTL_GETCURTIME:
+ return put_user(dev->l1.last_time + 20000L, (hfmodem_time_t *)arg); /* heuristic */
+
+ case HFMODEM_IOCTL_WAITRQ:
+ save_flags(flags);
+ cli();
+ ret = 0;
+ for (idx = -1, i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_unused)
+ continue;
+ if (dev->l1.rxslots[i].state != ss_retired) {
+ ret++;
+ continue;
+ }
+ if (idx < 0 || (signed)(tm - dev->l1.rxslots[i].tstart) > 0) {
+ idx = i;
+ tm = dev->l1.rxslots[i].tstart;
+ }
+ }
+ if (idx >= 0) {
+ cc_unlock(dev->l1.rxslots[idx].corr_cache);
+ id = dev->l1.rxslots[idx].id;
+ data = dev->l1.rxslots[idx].data;
+ userdata = dev->l1.rxslots[idx].userdata;
+ len = dev->l1.rxslots[idx].nbits * sizeof(hfmodem_soft_t);
+ dev->l1.rxslots[idx].state = ss_unused;
+ restore_flags(flags);
+ ret = copy_to_user(userdata, data, len);
+ kfree_s(data, len);
+ return (put_user(id, (hfmodem_id_t *)arg)) ? -EFAULT : ret;
+ }
+ for (idx = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_unused)
+ continue;
+ if (dev->l1.txslots[i].state != ss_retired) {
+ ret++;
+ continue;
+ }
+ if (idx < 0 || (signed)(tm - dev->l1.txslots[i].tstart) > 0) {
+ idx = i;
+ tm = dev->l1.txslots[i].tstart;
+ }
+ }
+ if (idx >= 0) {
+ id = dev->l1.txslots[idx].id;
+ data = dev->l1.txslots[idx].data;
+ len = (dev->l1.txslots[idx].nbits + 7) >> 3;
+ dev->l1.txslots[idx].state = ss_unused;
+ restore_flags(flags);
+ kfree_s(data, len);
+ return put_user(id, (hfmodem_id_t *)arg);
+ }
+ restore_flags(flags);
+ return ret ? -EAGAIN : -EPIPE;
+
+ case HFMODEM_IOCTL_MIXERPARAMS:
+ if ((ret = copy_from_user(&mix, (void *)arg, sizeof(mix))))
+ return ret;
+ dev->scops->mixer(dev, mix.src, mix.igain, mix.ogain);
+ return 0;
+
+ case HFMODEM_IOCTL_SAMPLESTART:
+ save_flags(flags);
+ cli();
+ if (dev->sbuf.kbuf)
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ restore_flags(flags);
+ if ((ret = copy_from_user(&spar, (void *)arg, sizeof(spar))))
+ return ret;
+ if (spar.len == 0)
+ return 0;
+ if (spar.len < 2 || spar.len > 8192)
+ return -EINVAL;
+ if (verify_area(VERIFY_WRITE, spar.data, spar.len * sizeof(__s16)))
+ return -EFAULT;
+ if (!(dev->sbuf.kbuf = kmalloc(spar.len * sizeof(__s16), GFP_KERNEL)))
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ dev->sbuf.kptr = dev->sbuf.kbuf;
+ dev->sbuf.size = spar.len * sizeof(__s16);
+ dev->sbuf.rem = spar.len;
+ dev->sbuf.ubuf = spar.data;
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_SAMPLEFINISHED:
+ save_flags(flags);
+ cli();
+ if (dev->sbuf.rem > 0) {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ if (!dev->sbuf.kbuf || !dev->sbuf.size) {
+ restore_flags(flags);
+ return -EPIPE;
+ }
+ restore_flags(flags);
+ ret = copy_to_user(dev->sbuf.ubuf, dev->sbuf.kbuf, dev->sbuf.size);
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ return ret;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20100
+
+unsigned int hfmodem_poll(struct file *file, poll_table *wait)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ unsigned long flags;
+ int i, cnt1, cnt2;
+
+ save_flags(flags);
+ cli();
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ restore_flags(flags);
+ poll_wait(&dev->wait, wait);
+ if (cnt1 || !cnt2)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+#else
+
+int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ unsigned long flags;
+ int i, cnt1, cnt2;
+
+ if (sel_type == SEL_IN) {
+ save_flags(flags);
+ cli();
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ restore_flags(flags);
+ if (cnt1 || !cnt2)
+ return 1;
+ select_wait(&dev->wait, wait);
+ }
+ return 0;
+}
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int l1fsk_phinc(struct hfmodem_l1_txslot *txs, unsigned int nbit)
+{
+ return txs->phase_incs[!!((txs->data[nbit >> 3] ^ txs->inv) & (1 << (nbit & 7)))];
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples)
+{
+ hfmodem_time_t tst, tend;
+ __s16 *s;
+ int i, j;
+ hfmodem_soft_t sample;
+
+ dev->l1.last_time = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ struct hfmodem_l1_rxslot *rxs = dev->l1.rxslots + i;
+
+ if (rxs->state == ss_unused || rxs->state == ss_retired)
+ continue;
+ tst = tstart - (rxs->corrlen-1) * tinc;
+ tend = tst + (HFMODEM_FRAGSAMPLES-1) * tinc;
+ if (rxs->state == ss_ready) {
+ if ((signed)(rxs->tstart - tend) > 0)
+ continue;
+ rxs->state = ss_oper;
+ }
+ for (s = samples, j = 0; j < HFMODEM_FRAGSAMPLES; j++, s++, tst += tinc)
+ if ((signed)(rxs->tstart - tst) <= 0) {
+ sample = do_filter(rxs, s);
+ while ((signed)(rxs->tstart - tst) <= 0 &&
+ rxs->cntbits < rxs->nbits) {
+ rxs->data[rxs->cntbits] = sample;
+ rxs->cntbits++;
+ rxs->tstart += rxs->tinc;
+ }
+ if (rxs->cntbits >= rxs->nbits) {
+ rxs->state = ss_retired;
+ break;
+ }
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int output_one_sample(struct hfmodem_state *dev, hfmodem_time_t tm)
+{
+ int i, j, k;
+ struct hfmodem_l1_txslot *txs;
+ /*
+ * first activate new output slots
+ */
+ for (j = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ txs = dev->l1.txslots + i;
+ if (txs->state == ss_ready && (signed)(txs->tstart - tm) <= 0) {
+ for (k = 0; k < HFMODEM_NUMTXSLOTS; k++) {
+ if (dev->l1.txslots[k].state != ss_oper)
+ continue;
+ dev->l1.txslots[k].state = ss_retired;
+ }
+ txs->state = ss_oper;
+ txs->tstart += txs->tinc;
+ txs->phinc = l1fsk_phinc(txs, 0);
+ txs->cntbits = 1;
+ };
+ if (txs->state != ss_oper)
+ continue;
+ j = i;
+ }
+ if (j < 0 || j >= HFMODEM_NUMTXSLOTS)
+ return 0;
+ /*
+ * calculate the current slot
+ */
+ txs = dev->l1.txslots + j;
+ while ((signed)(txs->tstart - tm) <= 0) {
+ if (txs->cntbits >= txs->nbits) {
+ txs->state = ss_retired;
+ return 0;
+ }
+ txs->tstart += txs->tinc;
+ txs->phinc = l1fsk_phinc(txs, txs->cntbits);
+ txs->cntbits++;
+ }
+ return txs->phinc;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples)
+{
+ int i, j;
+ hfmodem_time_t tend = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_oper)
+ break;
+ if (dev->l1.txslots[i].state == ss_ready &&
+ (signed)(dev->l1.txslots[i].tstart - tend) <= 0)
+ break;
+ }
+ if (i >= HFMODEM_NUMTXSLOTS)
+ return 0;
+ for (j = 0; j < HFMODEM_FRAGSAMPLES; j++, tstart += tinc, samples++) {
+ *samples = isimplecos(dev->l1.tx_phase);
+ dev->l1.tx_phase += output_one_sample(dev, tstart);
+ }
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr)
+{
+ long diff = LONG_MAX, t;
+ int i;
+
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_oper)
+ if (diff > 0)
+ diff = 0;
+ if (dev->l1.txslots[i].state == ss_ready) {
+ t = dev->l1.txslots[i].tstart - curr;
+ if (t < diff)
+ diff = t;
+ }
+ }
+ return diff;
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev)
+{
+ int i;
+
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state != ss_oper)
+ continue;
+ while (dev->l1.rxslots[i].cntbits < dev->l1.rxslots[i].nbits) {
+ dev->l1.rxslots[i].data[dev->l1.rxslots[i].cntbits] = 0;
+ dev->l1.rxslots[i].cntbits++;
+ }
+ dev->l1.rxslots[i].state = ss_retired;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_wakeup(struct hfmodem_state *dev)
+{
+ int i, cnt1, cnt2;
+
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ if (cnt1 || !cnt2)
+ wake_up_interruptible(&dev->wait);
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/refclock.c b/drivers/char/hfmodem/refclock.c
new file mode 100644
index 000000000..6617673ca
--- /dev/null
+++ b/drivers/char/hfmodem/refclock.c
@@ -0,0 +1,189 @@
+/*****************************************************************************/
+
+/*
+ * refclock.c -- Linux soundcard HF FSK driver,
+ * Reference clock routines.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+/*
+ * command line params
+ */
+
+static unsigned int scale_tvusec = 1UL<<24;
+
+#ifdef __i386__
+static unsigned int scale_rdtsc = 0;
+static int rdtsc_ok = 1;
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+__initfunc(static void i386_capability(void))
+{
+ unsigned long flags;
+ unsigned long fl1;
+ union {
+ struct {
+ unsigned int ebx, edx, ecx;
+ } r;
+ unsigned char s[13];
+ } id;
+ unsigned int eax;
+ unsigned int x86_capability;
+
+ save_flags(flags);
+ flags |= 0x200000;
+ restore_flags(flags);
+ save_flags(flags);
+ fl1 = flags;
+ flags &= ~0x200000;
+ restore_flags(flags);
+ save_flags(flags);
+ if (!(fl1 & 0x200000) || (flags & 0x200000)) {
+ printk(KERN_WARNING "%s: cpu does not support CPUID\n", hfmodem_drvname);
+ return;
+ }
+ __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
+ "0" (0));
+ id.s[12] = 0;
+ if (eax < 1) {
+ printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
+ "list\n", hfmodem_drvname, id.s);
+ return;
+ }
+ printk(KERN_INFO "%s: cpu: vendor string %s ", hfmodem_drvname, id.s);
+ __asm__ ("cpuid" : "=a" (eax), "=d" (x86_capability) : "0" (1) : "ebx", "ecx");
+ printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, eax & 15,
+ x86_capability);
+ if (x86_capability & 0x10)
+ rdtsc_ok = 1;
+ else
+ printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname);
+}
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(void hfmodem_refclock_probe(void))
+{
+#ifdef __i386__
+ if (rdtsc_ok) {
+ rdtsc_ok = 0;
+ i386_capability();
+ if (rdtsc_ok) {
+ unsigned int tmp0, tmp1, tmp2, tmp3;
+ __asm__("rdtsc" : "=a" (tmp0), "=d" (tmp1));
+ __asm__("rdtsc" : "=a" (tmp2), "=d" (tmp3));
+ if (tmp0 == tmp2 && tmp1 == tmp3) {
+ rdtsc_ok = 0;
+ printk(KERN_WARNING "%s: rdtsc unusable, does not change\n",
+ hfmodem_drvname);
+ }
+ }
+ }
+ printk(KERN_INFO "%s: using %s as timing source\n", hfmodem_drvname,
+ rdtsc_ok ? "rdtsc" : "gettimeofday");
+#endif /* __i386__ */
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_refclock_init(struct hfmodem_state *dev)
+{
+ struct timeval tv;
+
+ dev->clk.lasttime = 0;
+#ifdef __i386__
+ if (rdtsc_ok) {
+ __asm__("rdtsc;" : "=&d" (dev->clk.starttime_hi), "=&a" (dev->clk.starttime_lo));
+ return;
+ }
+#endif /* __i386__ */
+ do_gettimeofday(&tv);
+ dev->clk.last_tvusec = tv.tv_usec;
+ dev->clk.time_cnt = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid)
+{
+ struct timeval tv;
+ hfmodem_time_t curtime;
+ long diff;
+
+#ifdef __i386__
+ if (rdtsc_ok) {
+ unsigned int tmp0, tmp1;
+ unsigned int tmp2, tmp3;
+
+ __asm__("rdtsc;\n\t"
+ "subl %2,%%eax\n\t"
+ "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1)
+ : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx");
+ __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax");
+ __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx");
+ curtime = tmp2 + tmp3;
+ goto time_known;
+ }
+#endif /* __i386__ */
+ do_gettimeofday(&tv);
+ dev->clk.time_cnt += (unsigned)(1000000 + tv.tv_usec - dev->clk.last_tvusec) % 1000000;
+ dev->clk.last_tvusec = tv.tv_usec;
+ curtime = (dev->clk.time_cnt * scale_tvusec) >> 24;
+ time_known:
+ if (exp_valid && abs(diff = (curtime - dev->clk.lasttime - expected)) >= 1000)
+ printk(KERN_DEBUG "%s: refclock adjustment %ld more than 1ms\n",
+ hfmodem_drvname, diff);
+ return (dev->clk.lasttime = curtime);
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/sbc.c b/drivers/char/hfmodem/sbc.c
new file mode 100644
index 000000000..8e4010619
--- /dev/null
+++ b/drivers/char/hfmodem/sbc.c
@@ -0,0 +1,741 @@
+/*****************************************************************************/
+
+/*
+ * sbc.c -- Linux soundcard HF FSK driver,
+ * Soundblaster specific functions.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+/*
+ * 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 DSP_INTACK_16BIT(iobase) (iobase+0xf)
+#define SBC_EXTENT 16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+
+#define SBC_OUTPUT 0x14
+#define SBC_INPUT 0x24
+#define SBC_BLOCKSIZE 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 ESS_EXTENDED_MODE 0xc6
+#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_SAMPLE_RATE_OUT 0x41
+#define SBC_SAMPLE_RATE_IN 0x42
+#define SBC_MONO_8BIT 0xa0
+#define SBC_MONO_16BIT 0xa4
+#define SBC_STEREO_8BIT 0xa8
+#define SBC_STEREO_16BIT 0xac
+
+#define SBC4_OUT8_AI 0xc6
+#define SBC4_IN8_AI 0xce
+#define SBC4_MODE_UNS_MONO 0x00
+#define SBC4_MODE_SIGN_MONO 0x10
+
+#define SBC4_OUT16_AI 0xb6
+#define SBC4_IN16_AI 0xbe
+#define SBC4_OUT16_AI_NO_FIFO 0xb4
+#define SBC4_IN16_AI_NO_FIFO 0xbc
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops sbc4_scops;
+extern const struct hfmodem_scops ess_scops;
+
+/* --------------------------------------------------------------------- */
+
+static int reset_dsp(struct hfmodem_state *dev)
+{
+ int i;
+
+ outb(1, DSP_RESET(dev->io.base_addr));
+ udelay(3);
+ outb(0, DSP_RESET(dev->io.base_addr));
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+ if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa)
+ return 1;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_dsp(struct hfmodem_state *dev, unsigned char data)
+{
+ int i;
+
+ for (i = 0; i < 0xffff; i++)
+ if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) {
+ outb(data, DSP_WRITE_DATA(dev->io.base_addr));
+ return;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_dsp(struct hfmodem_state *dev, unsigned char *data)
+{
+ int i;
+
+ if (!data)
+ return 0;
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) {
+ *data = inb(DSP_READ_DATA(dev->io.base_addr));
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+ write_dsp(dev, reg);
+ write_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data)
+{
+ write_dsp(dev, 0xc0);
+ write_dsp(dev, reg);
+ return read_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int reset_ess(struct hfmodem_state *dev)
+{
+ int i;
+
+ outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */
+ udelay(3);
+ outb(0, DSP_RESET(dev->io.base_addr));
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+ if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) {
+ write_dsp(dev, ESS_EXTENDED_MODE);
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int config_resources(struct hfmodem_state *dev)
+{
+ unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+ unsigned long flags;
+
+ switch (dev->io.irq) {
+ case 2:
+ case 9:
+ irqreg |= 0x01;
+ break;
+
+ case 5:
+ irqreg |= 0x02;
+ break;
+
+ case 7:
+ irqreg |= 0x04;
+ break;
+
+ case 10:
+ irqreg |= 0x08;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ switch (dev->io.dma) {
+ case 0:
+ dmareg |= 0x01;
+ break;
+
+ case 1:
+ dmareg |= 0x02;
+ break;
+
+ case 3:
+ dmareg |= 0x08;
+ break;
+
+ case 5:
+ dmareg |= 0x20;
+ break;
+
+ case 6:
+ dmareg |= 0x40;
+ break;
+
+ case 7:
+ dmareg |= 0x80;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+ save_flags(flags);
+ cli();
+ outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr));
+ realirq = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr));
+ realdma = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ restore_flags(flags);
+ if ((~realirq) & irqreg || (~realdma) & dmareg) {
+ printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
+ "and IRQ/DMA specified wrongly?\n", hfmodem_drvname);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev)
+{
+ inb(DSP_DATA_AVAIL(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev)
+{
+ inb(DSP_INTACK_16BIT(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+ outb(reg, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(data, DSP_MIXER_DATA(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_sbcprobe(struct hfmodem_state *dev)
+{
+ unsigned char revhi, revlo, essrevhi, essrevlo, tmp;
+ int ret;
+
+ if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT ||
+ dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2)
+ return -ENXIO;
+ if (check_region(dev->io.base_addr, SBC_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -ENODEV;
+ }
+ set_mixer(dev, 0, 0); /* reset mixer */
+ write_dsp(dev, SBC_GET_REVISION);
+ if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo))
+ return -ENODEV;
+ printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo);
+ if (revhi == 3 && revlo == 1) {
+ write_dsp(dev, ESS_GET_REVISION);
+ if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo))
+ return -ENODEV;
+ if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) {
+ printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n",
+ hfmodem_drvname, essrevlo & 0x0f);
+ return -ENODEV;
+ }
+ if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) {
+ printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n",
+ hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f);
+ if (dev->io.dma > 3) {
+ printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+ return -ENXIO;
+ }
+ printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname);
+ read_ess(dev, 0xb1, &tmp);
+ switch (tmp & 0xf) {
+ case 0:
+ printk("2, 9, \"all others\"");
+ break;
+
+ case 5:
+ printk("5");
+ break;
+
+ case 10:
+ printk("7");
+ break;
+
+ case 15:
+ printk("10");
+ break;
+
+ default:
+ printk("unknown (%d)", tmp & 0xf);
+ break;
+ }
+ printk(" dma: ");
+ read_ess(dev, 0xb2, &tmp);
+ switch (tmp & 0xf) {
+ case 0:
+ printk("\"all others\"");
+ break;
+
+ case 5:
+ printk("0");
+ break;
+
+ case 10:
+ printk("1");
+ break;
+
+ case 15:
+ printk("3");
+ break;
+
+ default:
+ printk("unknown (%d)", tmp & 0xf);
+ break;
+ }
+ printk("\n");
+ dev->scops = &ess_scops;
+ return 0;
+ }
+ }
+ if (revhi < 4) {
+ printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname);
+ return -ENODEV;
+ }
+ if (dev->io.dma < 4) {
+ printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+ return -ENXIO;
+ }
+ if ((ret = config_resources(dev)))
+ return ret;
+ dev->scops = &sbc4_scops;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_init(struct hfmodem_state *dev)
+{
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE_IN); /* set sampling rate */
+ write_dsp(dev, HFMODEM_SRATE >> 8);
+ write_dsp(dev, HFMODEM_SRATE & 0xff);
+ write_dsp(dev, SBC_SPEAKER_OFF);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_dsp(dev, SBC4_IN16_AI_NO_FIFO);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE_OUT); /* set sampling rate */
+ write_dsp(dev, HFMODEM_SRATE >> 8);
+ write_dsp(dev, HFMODEM_SRATE & 0xff);
+ write_dsp(dev, SBC_SPEAKER_ON);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_dsp(dev, SBC4_OUT16_AI_NO_FIFO);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_stop(struct hfmodem_state *dev)
+{
+ reset_dsp(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int sbc4_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr;
+ unsigned long flags;
+ unsigned char intsrc;
+
+ save_flags(flags);
+ cli();
+ outb(0x82, DSP_MIXER_ADDR(dev->io.base_addr));
+ intsrc = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ if (intsrc & 0x01)
+ sbc_int_ack_8bit(dev);
+ if (intsrc & 0x02)
+ sbc_int_ack_16bit(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+ static const unsigned char srcbits[3] = { 0x18, 0x01, 0x06 };
+
+ save_flags(flags);
+ cli();
+ if (src >= 0 && src <= 2) {
+ set_mixer(dev, 0x3d, srcbits[src]);
+ set_mixer(dev, 0x3e, srcbits[src]);
+ }
+ if (ogain >= 0 && ogain <= 255) {
+ set_mixer(dev, 0x30, ogain);
+ set_mixer(dev, 0x31, ogain);
+ }
+ if (igain >= 0 && igain <= 255) {
+ set_mixer(dev, 0x36, igain);
+ set_mixer(dev, 0x37, igain);
+ set_mixer(dev, 0x38, igain);
+ set_mixer(dev, 0x39, igain);
+ set_mixer(dev, 0x3a, igain);
+ }
+ set_mixer(dev, 0x32, 0xff);
+ set_mixer(dev, 0x33, 0xff);
+ set_mixer(dev, 0x34, 0);
+ set_mixer(dev, 0x35, 0);
+ set_mixer(dev, 0x3b, 0); /* pc spkr vol */
+ set_mixer(dev, 0x3c, 0); /* output src */
+ set_mixer(dev, 0x3f, 0); /* inp gain */
+ set_mixer(dev, 0x40, 0);
+ set_mixer(dev, 0x41, 0); /* outp gain */
+ set_mixer(dev, 0x42, 0);
+ set_mixer(dev, 0x43, 1); /* mic agc off */
+ set_mixer(dev, 0x44, 8<<4); /* treble */
+ set_mixer(dev, 0x45, 8<<4);
+ set_mixer(dev, 0x46, 8<<4); /* bass */
+ set_mixer(dev, 0x47, 8<<4);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ if (!reset_ess(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_8bit(dev);
+ write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+ /*
+ * Set filter divider register
+ * Rolloff at 90% of the half sampling rate
+ */
+ write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+ write_dsp(dev, SBC_SPEAKER_OFF);
+ write_ess(dev, 0xb8, 0x0e); /* Auto init DMA mode */
+ read_ess(dev, 0xa8, &tmp);
+ write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */
+ write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
+ /* 16 bit mono */
+ write_ess(dev, 0xb7, 0x71);
+ write_ess(dev, 0xb7, 0xf4);
+
+ read_ess(dev, 0xb1, &tmp);
+ write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+ read_ess(dev, 0xb2, &tmp);
+ write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+
+ write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+ write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ read_ess(dev, 0xb8, &tmp);
+ write_ess(dev, 0xb8, tmp | 0x0f); /* Go */
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ if (!reset_ess(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_8bit(dev);
+ write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+ /*
+ * Set filter divider register
+ * Rolloff at 90% of the half sampling rate
+ */
+ write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+ write_ess(dev, 0xb8, 0x04); /* Auto init DMA mode */
+ read_ess(dev, 0xa8, &tmp);
+ write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */
+ write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
+ /* 16 bit mono */
+ write_ess(dev, 0xb6, 0x00);
+ write_ess(dev, 0xb7, 0x71);
+ write_ess(dev, 0xb7, 0xf4);
+
+ read_ess(dev, 0xb1, &tmp);
+ write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+ read_ess(dev, 0xb2, &tmp);
+ write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+
+ write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+ write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+
+ write_dsp(dev, SBC_SPEAKER_ON);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ read_ess(dev, 0xb8, &tmp);
+ write_ess(dev, 0xb8, tmp | 0x05); /* Go */
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+unsigned int ess_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr;
+ unsigned long flags;
+ unsigned char st;
+#if 0
+ static unsigned int cnt = 0;
+#endif
+
+ save_flags(flags);
+ cli();
+ st = inb(DSP_WRITE_STATUS(dev->io.base_addr));
+ sbc_int_ack_8bit(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+#if 0
+ cnt = (cnt + 1) & 0x3f;
+ if (!cnt)
+ printk(KERN_DEBUG "%s: ess: FIFO: full:%c empty:%c half empty:%c IRQ: cpu:%c half empty:%c DMA:%c\n",
+ hfmodem_drvname, '1'-!(st&0x20), '1'-!(st&0x10), '1'-!(st&0x8),
+ '1'-!(st&0x4), '1'-!(st&0x2), '1'-!(st&0x1));
+#endif
+ if (st & 0x20) /* FIFO full, 256 bytes */
+ dmaptr += 256;
+ else if (!(st & 0x10)) /* FIFO not empty, assume half full 128 bytes */
+ dmaptr += 128;
+ if (dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr -= HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (src >= 0 && src <= 2)
+ set_mixer(dev, 0x0c, ((src+3) & 3) << 1);
+ if (ogain >= 0 && ogain <= 255)
+ set_mixer(dev, 0x22, (ogain & 0xf0) | ((ogain >> 4) & 0xf));
+ if (igain >= 0 && igain <= 255) {
+ set_mixer(dev, 0x36, igain);
+ set_mixer(dev, 0x37, igain);
+ set_mixer(dev, 0x38, igain);
+ set_mixer(dev, 0x39, igain);
+ set_mixer(dev, 0x3a, igain);
+ }
+ set_mixer(dev, 0x4, 0xff);
+ set_mixer(dev, 0xe, 0x0);
+ set_mixer(dev, 0x26, 0);
+ set_mixer(dev, 0x28, 0);
+ set_mixer(dev, 0x2e, 0);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops sbc4_scops = {
+ SBC_EXTENT, sbc4_init, sbc4_prepare_input, sbc4_trigger_input,
+ sbc4_prepare_output, sbc4_trigger_output, sbc4_stop, sbc4_intack, sbc4_mixer
+};
+
+static const struct hfmodem_scops ess_scops = {
+ SBC_EXTENT, sbc4_init, ess_prepare_input, ess_trigger_input,
+ ess_prepare_output, ess_trigger_output, sbc4_stop, ess_intack, ess_mixer
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/wss.c b/drivers/char/hfmodem/wss.c
new file mode 100644
index 000000000..c54aeadee
--- /dev/null
+++ b/drivers/char/hfmodem/wss.c
@@ -0,0 +1,437 @@
+/*****************************************************************************/
+
+/*
+ * wss.c -- Linux soundcard HF FSK driver,
+ * WindowsSoundSystem specific functions.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * 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.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#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
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops wss_scops;
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct hfmodem_state *dev, unsigned char idx,
+ unsigned char data)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+ timeout--;
+ outb(idx, WSS_CODEC_IA(dev->io.base_addr));
+ outb(data, WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct hfmodem_state *dev, unsigned char idx)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+ timeout--;
+ outb(idx & 0x1f, WSS_CODEC_IA(dev->io.base_addr));
+ return inb(WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void wss_ack_int(struct hfmodem_state *dev)
+{
+ outb(0, WSS_CODEC_STATUS(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+ 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+ -1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+ if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+ return i;
+ return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct hfmodem_state *dev, unsigned char fmt)
+{
+ unsigned long time;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Clock and data format register */
+ write_codec(dev, 0x48, fmt);
+ /* MCE and interface config reg */
+ write_codec(dev, 0x49, 0xc);
+ outb(0xb, WSS_CODEC_IA(dev->io.base_addr)); /* leave MCE */
+ /*
+ * wait for ACI start
+ */
+ time = 1000;
+ while (!(read_codec(dev, 0x0b) & 0x20))
+ if (!(--time)) {
+ printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n",
+ hfmodem_drvname);
+ restore_flags(flags);
+ return -1;
+ }
+ /*
+ * wait for ACI end
+ */
+ sti();
+ time = jiffies + HZ/4;
+ while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+ restore_flags(flags);
+ if ((signed)(jiffies - time) >= 0) {
+ printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n",
+ hfmodem_drvname);
+ return -1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct hfmodem_state *dev)
+{
+ unsigned char tmp, revwss, revid;
+ 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 };
+ int fmt;
+
+ if ((fmt = wss_srate_index(HFMODEM_SRATE)) < 0) {
+ printk(KERN_ERR "%s: WSS: sampling rate not supported\n", hfmodem_drvname);
+ return -1;
+ }
+ fmt &= 0x0f;
+#ifdef __BIG_ENDIAN
+ fmt |= 0xc0;
+#else /* __BIG_ENDIAN */
+ fmt |= 0x40;
+#endif /* __BIG_ENDIAN */
+ tmp = inb(WSS_STATUS(dev->io.base_addr));
+ if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
+ (tmp & 0x3f) != 0x0f) {
+ printk(KERN_WARNING "%s: WSS card id register not found, "
+ "address 0x%x, ID register 0x%02x\n", hfmodem_drvname,
+ dev->io.base_addr, (int)tmp);
+ /* return -1; */
+ revwss = 0;
+ } else {
+ if ((tmp & 0x80) && ((dev->io.dma == 0) || ((dev->io.irq >= 8) && (dev->io.irq != 9)))) {
+ printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+ "(except IRQ9) cannot be used on an 8bit "
+ "card\n", hfmodem_drvname);
+ return -1;
+ }
+ if (dev->io.irq > 15 || irqtab[dev->io.irq] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid interrupt %d\n",
+ hfmodem_drvname, (int)dev->io.irq);
+ return -1;
+ }
+ if (dev->io.dma > 3 || dmatab[dev->io.dma] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid dma channel %d\n",
+ hfmodem_drvname, (int)dev->io.dma);
+ return -1;
+ }
+ tmp = irqtab[dev->io.irq] | dmatab[dev->io.dma];
+ /* irq probe */
+ outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->io.base_addr));
+ if (!(inb(WSS_STATUS(dev->io.base_addr)) & 0x40)) {
+ outb(0, WSS_CONFIG(dev->io.base_addr));
+ printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n",
+ hfmodem_drvname, dev->io.irq);
+ }
+ outb(tmp, WSS_CONFIG(dev->io.base_addr));
+ revwss = inb(WSS_STATUS(dev->io.base_addr)) & 0x3f;
+ }
+ /*
+ * initialize the codec
+ */
+ write_codec(dev, 9, 0);
+ write_codec(dev, 12, 0);
+ write_codec(dev, 0, 0x45);
+ if (read_codec(dev, 0) != 0x45)
+ goto codec_err;
+ write_codec(dev, 0, 0xaa);
+ if (read_codec(dev, 0) != 0xaa)
+ goto codec_err;
+ if (wss_set_codec_fmt(dev, fmt))
+ goto codec_err;
+ write_codec(dev, 0, 0x40); /* left input control */
+ write_codec(dev, 1, 0x40); /* 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, 0x80); /* left dac control */
+ write_codec(dev, 7, 0x80); /* right dac control */
+ write_codec(dev, 0xa, 0x2); /* pin control register */
+ write_codec(dev, 0xd, 0x0); /* digital mix control */
+ revid = read_codec(dev, 0xc) & 0xf;
+ /*
+ * print revisions
+ */
+ printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n",
+ hfmodem_drvname, (int)revwss, (int)revid);
+ return 0;
+ codec_err:
+ outb(0, WSS_CONFIG(dev->io.base_addr));
+ printk(KERN_ERR "%s: no WSS soundcard found at address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_wssprobe(struct hfmodem_state *dev)
+{
+ if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-WSS_EXTENT ||
+ dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 3 || dev->io.dma == 2)
+ return -ENXIO;
+ if (check_region(dev->io.base_addr, WSS_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (wss_init_codec(dev)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -ENODEV;
+ }
+ dev->scops = &wss_scops;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_init(struct hfmodem_state *dev)
+{
+ wss_init_codec(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_stop(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char oldcodecmode;
+ long abrt;
+
+ 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 (read_codec(dev, 11) & 0x10) {
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, (oldcodecmode & 1) ?
+ (DMA_MODE_WRITE | DMA_MODE_AUTOINIT) : (DMA_MODE_READ | DMA_MODE_AUTOINIT));
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ abrt = 0;
+ while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+ }
+ disable_dma(dev->io.dma);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ wss_stop(dev);
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_codec(dev, 9, 0x0e);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ wss_stop(dev);
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_codec(dev, 9, 0x0d);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int wss_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr, nums;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ wss_ack_int(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ nums = (((dmaptr - 1) % HFMODEM_FRAGSIZE) - 1) / 2;
+ write_codec(dev, 15, nums & 0xff);
+ write_codec(dev, 14, nums >> 8);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+ static const unsigned char srctoreg[3] = { 1, 2, 0 };
+ static const unsigned char regtosrc[4] = { 2, 0, 1, 0 };
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ tmp = read_codec(dev, 0x00);
+ if (src < 0 || src > 2)
+ src = regtosrc[(tmp >> 6) & 3];
+ if (igain < 0 || igain > 255) {
+ if (src == 1)
+ igain = ((tmp & 0xf) + ((tmp & 0x20) ? 13 : 0)) << 3;
+ else
+ igain = (tmp & 0xf) << 4;
+ }
+ if (src == 1) {
+ if (igain > (28<<3))
+ tmp = 0x2f;
+ else if (igain >= (13<<3))
+ tmp = 0x20 + (((igain >> 3) - 13) & 0xf);
+ else
+ tmp = (igain >> 3) & 0xf;
+ } else
+ tmp = (igain >> 4) & 0xf;
+ tmp |= srctoreg[src] << 6;
+ write_codec(dev, 0, tmp);
+ write_codec(dev, 1, tmp);
+ if (ogain > 0 && ogain <= 255) {
+ tmp = 63 - (ogain >> 2);
+ write_codec(dev, 6, tmp);
+ write_codec(dev, 7, tmp);
+ } else if (ogain == 0) {
+ write_codec(dev, 6, 0x80);
+ write_codec(dev, 7, 0x80);
+ }
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops wss_scops = {
+ WSS_EXTENT, wss_init, wss_prepare_input, wss_trigger_input,
+ wss_prepare_output, wss_trigger_output, wss_stop, wss_intack, wss_mixer
+};
+
+/* --------------------------------------------------------------------- */