summaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 06:32:09 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 06:32:09 +0000
commit1d68c48efe2d331857dfd4dfa4104fb366c100c5 (patch)
treee6c894ae6b00193b25948fa58cf596ee633053d7 /drivers/net
parentaa944aa3453e47706685bc562711a9e87375941e (diff)
Merge with Linux 2.1.72, part 3.
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/hamradio/.cvsignore1
-rw-r--r--drivers/net/hamradio/Config.in60
-rw-r--r--drivers/net/hamradio/Makefile120
-rw-r--r--drivers/net/hamradio/baycom_par.c661
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c762
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c792
-rw-r--r--drivers/net/hamradio/bpqether.c669
-rw-r--r--drivers/net/hamradio/hdlcdrv.c1024
-rw-r--r--drivers/net/hamradio/mkiss.c1134
-rw-r--r--drivers/net/hamradio/mkiss.h56
-rw-r--r--drivers/net/hamradio/pi2.c1677
-rw-r--r--drivers/net/hamradio/pt.c1778
-rw-r--r--drivers/net/hamradio/scc.c2257
-rw-r--r--drivers/net/hamradio/soundmodem/.cvsignore1
-rw-r--r--drivers/net/hamradio/soundmodem/Makefile60
-rw-r--r--drivers/net/hamradio/soundmodem/gentbl.c676
-rw-r--r--drivers/net/hamradio/soundmodem/sm.c898
-rw-r--r--drivers/net/hamradio/soundmodem/sm.h382
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk1200.c272
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk2400_7.c296
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk2400_8.c296
-rw-r--r--drivers/net/hamradio/soundmodem/sm_fsk9600.c391
-rw-r--r--drivers/net/hamradio/soundmodem/sm_hapn4800.c560
-rw-r--r--drivers/net/hamradio/soundmodem/sm_sbc.c941
-rw-r--r--drivers/net/hamradio/soundmodem/sm_wss.c965
-rw-r--r--drivers/net/hamradio/soundmodem/smdma.h217
-rw-r--r--drivers/net/hamradio/z8530.h243
27 files changed, 17189 insertions, 0 deletions
diff --git a/drivers/net/hamradio/.cvsignore b/drivers/net/hamradio/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/net/hamradio/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in
new file mode 100644
index 000000000..94a86238f
--- /dev/null
+++ b/drivers/net/hamradio/Config.in
@@ -0,0 +1,60 @@
+#
+# Amateur Radio protocols and AX.25 device configuration
+#
+# 19971130 Now in an own category to make correct compilation of the
+# AX.25 stuff easier...
+# Joerg Reuter DL1BKE <jreuter@poboxes.com>
+
+mainmenu_option next_comment
+comment 'Amateur Radio support'
+bool 'Amateur Radio support' CONFIG_HAMRADIO
+
+if [ "$CONFIG_HAMRADIO" != "n" ] ; then
+ if [ "$CONFIG_NET" != "n" ] ; then
+ comment 'Packet Radio protocols'
+ tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25
+ if [ "$CONFIG_AX25" != "n" ]; then
+ bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
+ dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
+ dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
+ fi
+
+ if [ "$CONFIG_AX25" != "n" ]; then
+ comment 'AX.25 network device drivers'
+ tristate 'Serial port KISS driver' CONFIG_MKISS
+# tristate 'Serial port 6PACK driver' CONFIG_6PACK
+ tristate 'BPQ Ethernet driver' CONFIG_BPQETHER
+
+ tristate 'Z8530 SCC driver' CONFIG_SCC
+ if [ "$CONFIG_SCC" != "n" ]; then
+ bool ' additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
+ bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
+ fi
+
+ tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
+ tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
+ tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR
+
+ tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM
+ if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
+ bool ' soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
+ bool ' soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
+ bool ' soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
+ bool ' soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
+ bool ' soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
+ bool ' soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
+ bool ' soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
+ fi
+ fi
+ fi
+
+ comment 'Misc. hamradio protocols'
+ tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+ if [ "$CONFIG_HFMODEM" != "n" ]; then
+ bool ' HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+ bool ' HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
+ fi
+fi
+
+endmenu
diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile
new file mode 100644
index 000000000..349371dd0
--- /dev/null
+++ b/drivers/net/hamradio/Makefile
@@ -0,0 +1,120 @@
+# File: drivers/hamradio/Makefile
+#
+# Makefile for the Linux AX.25 and HFMODEM device drivers.
+#
+# 19971130 Moved the amateur radio related network drivers from
+# drivers/net/ to drivers/hamradio for easier maintainance.
+# Joerg Reuter DL1BKE <jreuter@poboxes.com>
+
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := hamradio.a
+L_OBJS :=
+M_OBJS :=
+MOD_LIST_NAME := HAM_MODULES
+
+# Need these to keep track of whether the hdlc module should
+# really go in the kernel or a module.
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE :=
+
+ifeq ($(CONFIG_SCC),y)
+L_OBJS += scc.o
+else
+ ifeq ($(CONFIG_SCC),m)
+ M_OBJS += scc.o
+ endif
+endif
+
+ifeq ($(CONFIG_MKISS),y)
+L_OBJS += mkiss.o
+else
+ ifeq ($(CONFIG_MKISS),m)
+ M_OBJS += mkiss.o
+ endif
+endif
+
+ifeq ($(CONFIG_PI),y)
+L_OBJS += pi2.o
+else
+ ifeq ($(CONFIG_PI),m)
+ M_OBJS += pi2.o
+ endif
+endif
+
+ifeq ($(CONFIG_PT),y)
+L_OBJS += pt.o
+else
+ ifeq ($(CONFIG_PT),m)
+ M_OBJS += pt.o
+ endif
+endif
+
+ifeq ($(CONFIG_BPQETHER),y)
+L_OBJS += bpqether.o
+else
+ ifeq ($(CONFIG_BPQETHER),m)
+ M_OBJS += bpqether.o
+ endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_SER_FDX),y)
+L_OBJS += baycom_ser_fdx.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_BAYCOM_SER_FDX),m)
+ CONFIG_HDLCDRV_MODULE = y
+ M_OBJS += baycom_ser_fdx.o
+ endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_SER_HDX),y)
+L_OBJS += baycom_ser_hdx.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_BAYCOM_SER_HDX),m)
+ CONFIG_HDLCDRV_MODULE = y
+ M_OBJS += baycom_ser_hdx.o
+ endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_PAR),y)
+L_OBJS += baycom_par.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_BAYCOM_PAR),m)
+ CONFIG_HDLCDRV_MODULE = y
+ M_OBJS += baycom_par.o
+ endif
+endif
+
+ifeq ($(CONFIG_SOUNDMODEM),y)
+ALL_SUB_DIRS += soundmodem
+SUB_DIRS += soundmodem
+L_OBJS += soundmodem/soundmodem.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_SOUNDMODEM),m)
+ CONFIG_HDLCDRV_MODULE = y
+ ALL_SUB_DIRS += soundmodem
+ MOD_SUB_DIRS += soundmodem
+ endif
+endif
+
+# If anything built-in uses the hdlcdrv, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_HDLCDRV_BUILTIN
+LX_OBJS += hdlcdrv.o
+else
+ ifdef CONFIG_HDLCDRV_MODULE
+ MX_OBJS += hdlcdrv.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+clean:
+ rm -f core *.o *.a *.s
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
new file mode 100644
index 000000000..0b470e2f3
--- /dev/null
+++ b/drivers/net/hamradio/baycom_par.c
@@ -0,0 +1,661 @@
+/*****************************************************************************/
+
+/*
+ * baycom_par.c -- baycom par96 and picpar radio modem driver.
+ *
+ * Copyright (C) 1997 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.
+ *
+ *
+ * Supported modems
+ *
+ * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ * The modem does all the filtering and regenerates the receiver clock.
+ * Data is transferred from and to the PC via a shift register.
+ * The shift register is filled with 16 bits and an interrupt is
+ * signalled. The PC then empties the shift register in a burst. This
+ * modem connects to the parallel port, hence the name. The modem
+ * leaves the implementation of the HDLC protocol and the scrambler
+ * polynomial to the PC. This modem is no longer available (at least
+ * from Baycom) and has been replaced by the PICPAR modem (see below).
+ * You may however still build one from the schematics published in
+ * cq-DL :-).
+ *
+ * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ * modem is protocol compatible to par96, but uses only three low
+ * power ICs and can therefore be fed from the parallel port and
+ * does not require an additional power supply. It features
+ * built in DCD circuitry. The driver should therefore be configured
+ * for hardware DCD.
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * mode driver mode string. Valid choices are par96 and picpar.
+ * iobase base address of the port; common values are 0x378, 0x278, 0x3bc
+ *
+ *
+ * History:
+ * 0.1 26.06.96 Adapted from baycom.c and made network driver interface
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ * 0.3 26.04.97 init code/data tagged
+ * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints)
+ * 0.5 11.11.97 split into separate files for ser12/par96
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.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 <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+#include <linux/parport.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
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD 1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_par";
+static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_par: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+ const char *mode;
+ int iobase;
+} baycom_ports[NR_PORTS] = { { NULL, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define SER12_EXTENT 8
+
+#define LPT_DATA(dev) ((dev)->base_addr+0)
+#define LPT_STATUS(dev) ((dev)->base_addr+1)
+#define LPT_CONTROL(dev) ((dev)->base_addr+2)
+#define LPT_IRQ_ENABLE 0x10
+
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST 4
+#define PAR96_PTT 2
+#define PAR96_TXBIT 1
+#define PAR96_ACK 0x40
+#define PAR96_RXBIT 0x20
+#define PAR96_DCD 0x10
+#define PAR97_POWER 0xf8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+ struct hdlcdrv_state hdrv;
+
+ struct pardevice *pdev;
+ unsigned int options;
+
+ struct modem_state {
+ short arb_divider;
+ unsigned char flags;
+ unsigned int shreg;
+ struct modem_state_par96 {
+ int dcd_count;
+ unsigned int dcd_shreg;
+ unsigned long descram;
+ unsigned long scram;
+ } par96;
+ } modem;
+
+#ifdef BAYCOM_DEBUG
+ struct debug_vals {
+ unsigned long last_jiffies;
+ unsigned cur_intcnt;
+ unsigned last_intcnt;
+ int cur_pllcorr;
+ int last_pllcorr;
+ } debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void __inline__ baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ bc->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+ bc->debug_vals.last_jiffies = cur_jiffies;
+ bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+ bc->debug_vals.cur_intcnt = 0;
+ bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+ bc->debug_vals.cur_pllcorr = 0;
+ }
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_tx(struct device *dev, struct baycom_state *bc)
+{
+ int i;
+ unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+
+ for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+ unsigned char val = PAR97_POWER;
+ bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+ (bc->modem.par96.scram & 1));
+ if (!(data & 1))
+ bc->modem.par96.scram ^= 1;
+ if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+ bc->modem.par96.scram ^=
+ (PAR96_SCRAM_TAPN << 1);
+ if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+ val |= PAR96_TXBIT;
+ outb(val, LPT_DATA(dev));
+ outb(val | PAR96_BURST, LPT_DATA(dev));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_rx(struct device *dev, struct baycom_state *bc)
+{
+ int i;
+ unsigned int data, mask, mask2, descx;
+
+ /*
+ * do receiver; differential decode and descramble on the fly
+ */
+ for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+ bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+ if (inb(LPT_STATUS(dev)) & PAR96_RXBIT)
+ bc->modem.par96.descram |= 1;
+ descx = bc->modem.par96.descram ^
+ (bc->modem.par96.descram >> 1);
+ /* now the diff decoded data is inverted in descram */
+ outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev));
+ descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+ (descx >> PAR96_DESCRAM_TAPSH2));
+ data >>= 1;
+ if (!(descx & 1))
+ data |= 0x8000;
+ outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, LPT_DATA(dev));
+ }
+ hdlcdrv_putbits(&bc->hdrv, data);
+ /*
+ * do DCD algorithm
+ */
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+ | (data << 16);
+ /* search for flags and set the dcd counter appropriately */
+ for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
+ i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+ if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+ bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+ /* check for abort/noise sequences */
+ for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
+ i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+ if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+ (bc->modem.par96.dcd_count >= 0))
+ bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+ /* decrement and set the dcd variable */
+ if (bc->modem.par96.dcd_count >= 0)
+ bc->modem.par96.dcd_count -= 2;
+ hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+ } else {
+ hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev)) & PAR96_DCD));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct parport *pp = (struct parport *)dev_id;
+ struct pardevice *pd = pp->cad;
+ struct device *dev = (struct device *)pd->private;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ baycom_int_freq(bc);
+ /*
+ * check if transmitter active
+ */
+ if (hdlcdrv_ptt(&bc->hdrv))
+ par96_tx(dev, bc);
+ else {
+ par96_rx(dev, bc);
+ if (--bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = 6;
+ sti();
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ }
+ sti();
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_preempt(void *handle)
+{
+ /* we cannot relinquish the port in the middle of an operation */
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_wakeup(void *handle)
+{
+ struct device *dev = (struct device *)handle;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);
+ if (!parport_claim(bc->pdev))
+ printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ struct parport *pp = parport_enumerate();
+
+ if (!dev || !bc)
+ return -ENXIO;
+ while (pp && pp->base != dev->base_addr)
+ pp = pp->next;
+ if (!pp) {
+ printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);
+ return -ENXIO;
+ }
+ if (pp->irq < 0) {
+ printk(KERN_ERR "baycom_par: parport at 0x%x has no irq\n", pp->base);
+ return -ENXIO;
+ }
+ memset(&bc->modem, 0, sizeof(bc->modem));
+ bc->hdrv.par.bitrate = 9600;
+ if (!(bc->pdev = parport_register_device(pp, dev->name, par96_preempt, par96_wakeup,
+ par96_interrupt, PARPORT_DEV_LURK, dev))) {
+ printk(KERN_ERR "baycom_par: cannot register parport at 0x%x\n", pp->base);
+ return -ENXIO;
+ }
+ if (parport_claim(bc->pdev)) {
+ printk(KERN_ERR "baycom_par: parport at 0x%x busy\n", pp->base);
+ parport_unregister_device(bc->pdev);
+ return -EBUSY;
+ }
+ dev->irq = pp->irq;
+ /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP); not yet implemented */
+ /* switch off PTT */
+ outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
+ /*bc->pdev->port->ops->enable_irq(bc->pdev->port); not yet implemented */
+ outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev));
+ printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
+ bc_drvname, dev->base_addr, dev->irq, bc->options);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -EINVAL;
+ /* disable interrupt */
+ outb(0, LPT_CONTROL(dev));
+ /*bc->pdev->port->ops->disable_irq(bc->pdev->port); not yet implemented */
+ /* switch off PTT */
+ outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
+ parport_release(bc->pdev);
+ parport_unregister_device(bc->pdev);
+ printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
+ bc_drvname, dev->base_addr, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+ bc_drvname,
+ bc_drvinfo,
+ par96_open,
+ par96_close,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+ if (!strncmp(modestr, "picpar", 6))
+ bc->options = 0;
+ else if (!strncmp(modestr, "par96", 5))
+ bc->options = BAYCOM_OPTIONS_SOFTDCD;
+ else
+ bc->options = !!strchr(modestr, '*');
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct baycom_state *bc;
+ struct baycom_ioctl bi;
+ int cmd2;
+
+ if (!dev || !dev->priv ||
+ ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ bc = (struct baycom_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (get_user(cmd2, (int *)ifr->ifr_data))
+ return -EFAULT;
+ switch (hi->cmd) {
+ default:
+ break;
+
+ case HDLCDRVCTL_GETMODE:
+ strcpy(hi->data.modename, bc->options ? "par96" : "picpar");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_SETMODE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+ return baycom_setmode(bc, hi->data.modename);
+
+ case HDLCDRVCTL_MODELIST:
+ strcpy(hi->data.modename, "par96,picpar");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_MODEMPARMASK:
+ return HDLCDRV_PARMASK_IOBASE;
+
+ }
+
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+ case BAYCOMCTL_GETDEBUG:
+ bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+ bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+ bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+ break;
+#endif /* BAYCOM_DEBUG */
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_par_init(void))
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct baycom_state *bc;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+
+ printk(bc_drvinfo);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ sprintf(ifname, "bcp%d", i);
+
+ if (!baycom_ports[i].mode)
+ set_hw = 0;
+ if (!set_hw)
+ baycom_ports[i].iobase = 0;
+ j = hdlcdrv_register_hdlcdrv(dev, &par96_ops,
+ sizeof(struct baycom_state),
+ ifname, baycom_ports[i].iobase, 0, 0);
+ if (!j) {
+ bc = (struct baycom_state *)dev->priv;
+ if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+ set_hw = 0;
+ found++;
+ } else {
+ printk(KERN_WARNING "%s: cannot register net device\n",
+ bc_drvname);
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static const char *mode[NR_PORTS] = { "picpar", };
+static int iobase[NR_PORTS] = { 0x378, };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+ baycom_ports[i].mode = mode[i];
+ baycom_ports[i].iobase = iobase[i];
+ }
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+ return baycom_par_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (bc) {
+ if (bc->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "baycom: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_par=io,mode
+ * mode: par96,picpar
+ */
+
+__initfunc(void baycom_par_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+ if ((i >= NR_PORTS) || (ints[0] < 1)) {
+ printk(KERN_INFO "%s: too many or invalid interface "
+ "specifications\n", bc_drvname);
+ return;
+ }
+ baycom_ports[i].mode = str;
+ baycom_ports[i].iobase = ints[1];
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
new file mode 100644
index 000000000..9005356d3
--- /dev/null
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -0,0 +1,762 @@
+/*****************************************************************************/
+
+/*
+ * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver.
+ *
+ * Copyright (C) 1997 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.
+ *
+ *
+ * Supported modems
+ *
+ * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
+ * of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ * is responsible for regenerating the receiver bit clock, as well as
+ * for handling the HDLC protocol. The modem connects to a serial port,
+ * hence the name. Since the serial port is not used as an async serial
+ * port, the kernel driver for serial ports cannot be used, and this
+ * driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * mode * enables software DCD.
+ * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ * baud baud rate (between 300 and 4800)
+ * irq interrupt line of the port; common values are 4,3
+ *
+ *
+ * History:
+ * 0.1 26.06.96 Adapted from baycom.c and made network driver interface
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ * 0.3 26.04.97 init code/data tagged
+ * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints)
+ * 0.5 11.11.97 ser12/par96 split into separate files
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.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 <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.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
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD 1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_fdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_fdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+ char *mode;
+ int iobase, irq, baud;
+} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+ struct hdlcdrv_state hdrv;
+
+ unsigned int baud, baud_us8, baud_arbdiv;
+ unsigned int options;
+
+ struct modem_state {
+ short arb_divider;
+ unsigned char flags;
+ unsigned int shreg;
+ struct modem_state_ser12 {
+ unsigned char tx_bit;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned char last_sample;
+ unsigned char last_rxbit;
+ unsigned int dcd_shreg;
+ unsigned int dcd_time;
+ unsigned int bit_pll;
+ unsigned long last_jiffies;
+ unsigned int pll_time;
+ unsigned int txshreg;
+ } ser12;
+ } modem;
+
+#ifdef BAYCOM_DEBUG
+ struct debug_vals {
+ unsigned long last_jiffies;
+ unsigned cur_intcnt;
+ unsigned last_intcnt;
+ int cur_pllcorr;
+ int last_pllcorr;
+ } debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ bc->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+ bc->debug_vals.last_jiffies = cur_jiffies;
+ bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+ bc->debug_vals.cur_intcnt = 0;
+ bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+ bc->debug_vals.cur_pllcorr = 0;
+ }
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned int hweight16(unsigned short w)
+ __attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+ __attribute__ ((unused));
+
+extern 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);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+ unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
+{
+ bc->modem.ser12.dcd_shreg <<= 1;
+ bc->modem.ser12.bit_pll += 0x2000;
+ if (bc->modem.ser12.last_sample != news) {
+ bc->modem.ser12.last_sample = news;
+ bc->modem.ser12.dcd_shreg |= 1;
+ if (bc->modem.ser12.bit_pll < 0x9000)
+ bc->modem.ser12.bit_pll += 0x1000;
+ else
+ bc->modem.ser12.bit_pll -= 0x1000;
+ bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
+ - hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
+ }
+ hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
+ if ((--bc->modem.ser12.dcd_time) <= 0) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
+ bc->modem.ser12.dcd_time = 120;
+ }
+ if (bc->modem.ser12.bit_pll >= 0x10000) {
+ bc->modem.ser12.bit_pll &= 0xffff;
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
+ if (bc->modem.shreg & 1) {
+ hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+ bc->modem.shreg = 0x10000;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
+{
+ unsigned long curjiff;
+ struct timeval tv;
+ unsigned int timediff;
+
+ /*
+ * get current time
+ */
+ curjiff = jiffies;
+ do_gettimeofday(&tv);
+ if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
+ /* long inactivity; clear HDLC and DCD */
+ bc->modem.ser12.dcd_sum1 = 0;
+ bc->modem.ser12.dcd_sum2 = 0;
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = 120;
+ hdlcdrv_setdcd(&bc->hdrv, 0);
+ hdlcdrv_putbits(&bc->hdrv, 0xffff);
+ bc->modem.ser12.last_jiffies = curjiff;
+ bc->modem.ser12.pll_time = tv.tv_usec;
+ }
+ bc->modem.ser12.last_jiffies = curjiff;
+ timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
+ timediff %= 1000000;
+ timediff /= bc->baud_us8;
+ bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (bc->baud_us8)) % 1000000;
+ for (; timediff > 1; timediff--)
+ ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
+ if (timediff >= 1)
+ ser12_rxsample(dev, bc, curs);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ unsigned char iir, msr = 0;
+ unsigned int txcount = 0;
+ unsigned int rxcount = 0;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ for (;;) {
+ iir = inb(IIR(dev->base_addr));
+ if (iir & 1)
+ break;
+ switch (iir & 6) {
+ case 6:
+ inb(LSR(dev->base_addr));
+ continue;
+
+ case 4:
+ inb(RBR(dev->base_addr));
+ continue;
+
+ case 2:
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ bc->modem.arb_divider--;
+ baycom_int_freq(bc);
+ if (hdlcdrv_ptt(&bc->hdrv)) {
+ /*
+ * first output the last bit (!) then call HDLC transmitter,
+ * since this may take quite long
+ */
+ outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+ txcount++;
+ } else
+ outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
+ continue;
+
+ default:
+ msr = inb(MSR(dev->base_addr));
+ if (msr & 1) /* delta CTS interrupt */
+ rxcount++;
+ continue;
+ }
+ }
+ if (rxcount)
+ ser12_rx(dev, bc, msr & 0x10);
+ if (txcount) {
+#ifdef BAYCOM_DEBUG
+ if (bc->debug_vals.cur_pllcorr < txcount)
+ bc->debug_vals.cur_pllcorr = txcount;
+#endif /* BAYCOM_DEBUG */
+ if (bc->modem.ser12.txshreg <= 1)
+ bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+ bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
+ bc->modem.ser12.txshreg >>= 1;
+ }
+ sti();
+ if (bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = bc->baud_arbdiv;
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+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 ser12_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 };
+
+ b1 = inb(MCR(iobase));
+ outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
+ b2 = inb(MSR(iobase));
+ outb(0x1a, MCR(iobase));
+ b3 = inb(MSR(iobase)) & 0xf0;
+ outb(b1, MCR(iobase)); /* restore old values */
+ outb(b2, MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(RBR(iobase));
+ inb(RBR(iobase));
+ outb(0x01, FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, SCR(iobase));
+ b1 = inb(SCR(iobase));
+ outb(0xa5, SCR(iobase));
+ b2 = inb(SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ enum uart u;
+
+ if (!dev || !bc)
+ return -ENXIO;
+ if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+ dev->irq < 2 || dev->irq > 15)
+ return -ENXIO;
+ if (bc->baud < 300 || bc->baud > 4800)
+ return -EINVAL;
+ if (check_region(dev->base_addr, SER12_EXTENT))
+ return -EACCES;
+ memset(&bc->modem, 0, sizeof(bc->modem));
+ bc->hdrv.par.bitrate = bc->baud;
+ bc->baud_us8 = 125000/bc->baud;
+ bc->baud_arbdiv = bc->baud/100;
+ if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+ return -EIO;
+ outb(0, FCR(dev->base_addr)); /* disable FIFOs */
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0, IER(dev->base_addr));
+ if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+ "baycom_ser_fdx", dev))
+ return -EBUSY;
+ request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx");
+ /*
+ * set the SIO to 6 Bits/character and 19600 baud, so that
+ * we get exactly (hopefully) one interrupt per radio symbol
+ */
+ outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
+ outb(115200/8/bc->baud, DLL(dev->base_addr));
+ outb(0, DLM(dev->base_addr));
+ outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
+ /*
+ * enable transmitter empty interrupt and modem status interrupt
+ */
+ outb(0x0a, IER(dev->base_addr));
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u options "
+ "0x%x baud %u uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+ bc->options, bc->baud, uart_str[u]);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ outb(0, IER(dev->base_addr));
+ outb(1, MCR(dev->base_addr));
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, SER12_EXTENT);
+ printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
+ bc_drvname, dev->base_addr, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+ bc_drvname,
+ bc_drvinfo,
+ ser12_open,
+ ser12_close,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+ unsigned int baud;
+
+ if (!strncmp(modestr, "ser", 3)) {
+ baud = simple_strtoul(modestr+3, NULL, 10);
+ if (baud >= 3 && baud <= 48)
+ bc->baud = baud*100;
+ }
+ bc->options = !!strchr(modestr, '*');
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct baycom_state *bc;
+ struct baycom_ioctl bi;
+ int cmd2;
+
+ if (!dev || !dev->priv ||
+ ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ bc = (struct baycom_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (get_user(cmd2, (int *)ifr->ifr_data))
+ return -EFAULT;
+ switch (hi->cmd) {
+ default:
+ break;
+
+ case HDLCDRVCTL_GETMODE:
+ sprintf(hi->data.modename, "ser%u", bc->baud / 100);
+ if (bc->options & 1)
+ strcat(hi->data.modename, "*");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_SETMODE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+ return baycom_setmode(bc, hi->data.modename);
+
+ case HDLCDRVCTL_MODELIST:
+ strcpy(hi->data.modename, "ser12,ser3,ser24");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_MODEMPARMASK:
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+ }
+
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+ case BAYCOMCTL_GETDEBUG:
+ bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+ bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+ bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+ break;
+#endif /* BAYCOM_DEBUG */
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_ser_fdx_init(void))
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct baycom_state *bc;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+
+ printk(bc_drvinfo);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ sprintf(ifname, "bcsf%d", i);
+
+ if (!baycom_ports[i].mode)
+ set_hw = 0;
+ if (!set_hw)
+ baycom_ports[i].iobase = baycom_ports[i].irq = 0;
+ j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
+ sizeof(struct baycom_state),
+ ifname, baycom_ports[i].iobase,
+ baycom_ports[i].irq, 0);
+ if (!j) {
+ bc = (struct baycom_state *)dev->priv;
+ if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+ set_hw = 0;
+ bc->baud = baycom_ports[i].baud;
+ found++;
+ } else {
+ printk(KERN_WARNING "%s: cannot register net device\n",
+ bc_drvname);
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+MODULE_PARM(baud, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+ baycom_ports[i].mode = mode[i];
+ baycom_ports[i].iobase = iobase[i];
+ baycom_ports[i].irq = irq[i];
+ baycom_ports[i].baud = baud[i];
+ }
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+ return baycom_ser_fdx_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (bc) {
+ if (bc->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "baycom: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_ser_=io,irq,mode
+ * mode: [*]
+ * * indicates sofware DCD
+ */
+
+__initfunc(void baycom_ser_fdx_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+ if ((i >= NR_PORTS) || (ints[0] < 2)) {
+ printk(KERN_INFO "%s: too many or invalid interface "
+ "specifications\n", bc_drvname);
+ return;
+ }
+ baycom_ports[i].mode = str;
+ baycom_ports[i].iobase = ints[1];
+ baycom_ports[i].irq = ints[2];
+ if (ints[0] >= 3)
+ baycom_ports[i].baud = ints[3];
+ else
+ baycom_ports[i].baud = 1200;
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
new file mode 100644
index 000000000..e3d7ac998
--- /dev/null
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -0,0 +1,792 @@
+/*****************************************************************************/
+
+/*
+ * baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver.
+ *
+ * Copyright (C) 1997 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.
+ *
+ *
+ * Supported modems
+ *
+ * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
+ * of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ * is responsible for regenerating the receiver bit clock, as well as
+ * for handling the HDLC protocol. The modem connects to a serial port,
+ * hence the name. Since the serial port is not used as an async serial
+ * port, the kernel driver for serial ports cannot be used, and this
+ * driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * mode * enables software DCD.
+ * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ * irq interrupt line of the port; common values are 4,3
+ *
+ *
+ * History:
+ * 0.1 26.06.96 Adapted from baycom.c and made network driver interface
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ * 0.3 26.04.97 init code/data tagged
+ * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints)
+ * 0.5 11.11.97 ser12/par96 split into separate files
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.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 <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.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
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD 1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_hdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_hdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+ char *mode;
+ int iobase, irq;
+} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+ struct hdlcdrv_state hdrv;
+
+ unsigned int options;
+
+ struct modem_state {
+ short arb_divider;
+ unsigned char flags;
+ unsigned int shreg;
+ struct modem_state_ser12 {
+ unsigned char tx_bit;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned char last_sample;
+ unsigned char last_rxbit;
+ unsigned int dcd_shreg;
+ unsigned int dcd_time;
+ unsigned int bit_pll;
+ unsigned char interm_sample;
+ } ser12;
+ } modem;
+
+#ifdef BAYCOM_DEBUG
+ struct debug_vals {
+ unsigned long last_jiffies;
+ unsigned cur_intcnt;
+ unsigned last_intcnt;
+ int cur_pllcorr;
+ int last_pllcorr;
+ } debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ bc->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+ bc->debug_vals.last_jiffies = cur_jiffies;
+ bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+ bc->debug_vals.cur_intcnt = 0;
+ bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+ bc->debug_vals.cur_pllcorr = 0;
+ }
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static void inline ser12_set_divisor(struct device *dev,
+ unsigned char divisor)
+{
+ outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
+ outb(divisor, DLL(dev->base_addr));
+ outb(0, DLM(dev->base_addr));
+ outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ /*
+ * it is important not to set the divider while transmitting;
+ * this reportedly makes some UARTs generating interrupts
+ * in the hundredthousands per second region
+ * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+ */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+ 36 : 24)
+#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+ 240 : 12)
+
+static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
+{
+ /* one interrupt per channel bit */
+ ser12_set_divisor(dev, 12);
+ /*
+ * first output the last bit (!) then call HDLC transmitter,
+ * since this may take quite long
+ */
+ outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+ if (bc->modem.shreg <= 1)
+ bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+ bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
+ (bc->modem.shreg & 1));
+ bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
+{
+ unsigned char cur_s;
+ /*
+ * do demodulator
+ */
+ cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */
+ hdlcdrv_channelbit(&bc->hdrv, cur_s);
+ bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+ (cur_s != bc->modem.ser12.last_sample);
+ bc->modem.ser12.last_sample = cur_s;
+ if(bc->modem.ser12.dcd_shreg & 1) {
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ unsigned int dcdspos, dcdsneg;
+
+ dcdspos = dcdsneg = 0;
+ dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+ if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+ dcdspos += 2;
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+ bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+ } else
+ bc->modem.ser12.dcd_sum0--;
+ }
+ if(!bc->modem.ser12.dcd_time) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ /* offset to ensure DCD off on silent input */
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+ }
+ bc->modem.ser12.dcd_time--;
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ /*
+ * PLL code for the improved software DCD algorithm
+ */
+ if (bc->modem.ser12.interm_sample) {
+ /*
+ * intermediate sample; set timing correction to normal
+ */
+ ser12_set_divisor(dev, 4);
+ } else {
+ /*
+ * do PLL correction and call HDLC receiver
+ */
+ switch (bc->modem.ser12.dcd_shreg & 7) {
+ case 1: /* transition too late */
+ ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+ break;
+ case 4: /* transition too early */
+ ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+ break;
+ default:
+ ser12_set_divisor(dev, 4);
+ break;
+ }
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_sample ==
+ bc->modem.ser12.last_rxbit)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit =
+ bc->modem.ser12.last_sample;
+ }
+ if (++bc->modem.ser12.interm_sample >= 3)
+ bc->modem.ser12.interm_sample = 0;
+ /*
+ * DCD stuff
+ */
+ if (bc->modem.ser12.dcd_shreg & 1) {
+ unsigned int dcdspos, dcdsneg;
+
+ dcdspos = dcdsneg = 0;
+ dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+ dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+ << 1;
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+ bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+ }
+ } else {
+ /*
+ * PLL algorithm for the hardware squelch DCD algorithm
+ */
+ if (bc->modem.ser12.interm_sample) {
+ /*
+ * intermediate sample; set timing correction to normal
+ */
+ ser12_set_divisor(dev, 6);
+ } else {
+ /*
+ * do PLL correction and call HDLC receiver
+ */
+ switch (bc->modem.ser12.dcd_shreg & 3) {
+ case 1: /* transition too late */
+ ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+ break;
+ case 2: /* transition too early */
+ ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+ break;
+ default:
+ ser12_set_divisor(dev, 6);
+ break;
+ }
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_sample ==
+ bc->modem.ser12.last_rxbit)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit =
+ bc->modem.ser12.last_sample;
+ }
+ bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+ /*
+ * DCD stuff
+ */
+ bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+ }
+ outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
+ if (bc->modem.shreg & 1) {
+ hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+ bc->modem.shreg = 0x10000;
+ }
+ if(!bc->modem.ser12.dcd_time) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ /* offset to ensure DCD off on silent input */
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+ }
+ bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ baycom_int_freq(bc);
+ /*
+ * check if transmitter active
+ */
+ if (hdlcdrv_ptt(&bc->hdrv))
+ ser12_tx(dev, bc);
+ else {
+ ser12_rx(dev, bc);
+ if (--bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+ sti();
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ }
+ sti();
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+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 ser12_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 };
+
+ b1 = inb(MCR(iobase));
+ outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
+ b2 = inb(MSR(iobase));
+ outb(0x1a, MCR(iobase));
+ b3 = inb(MSR(iobase)) & 0xf0;
+ outb(b1, MCR(iobase)); /* restore old values */
+ outb(b2, MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(RBR(iobase));
+ inb(RBR(iobase));
+ outb(0x01, FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, SCR(iobase));
+ b1 = inb(SCR(iobase));
+ outb(0xa5, SCR(iobase));
+ b2 = inb(SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ enum uart u;
+
+ if (!dev || !bc)
+ return -ENXIO;
+ if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+ dev->irq < 2 || dev->irq > 15)
+ return -ENXIO;
+ if (check_region(dev->base_addr, SER12_EXTENT))
+ return -EACCES;
+ memset(&bc->modem, 0, sizeof(bc->modem));
+ bc->hdrv.par.bitrate = 1200;
+ if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+ return -EIO;
+ outb(0, FCR(dev->base_addr)); /* disable FIFOs */
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0, IER(dev->base_addr));
+ if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+ "baycom_ser12", dev))
+ return -EBUSY;
+ request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+ /*
+ * enable transmitter empty interrupt
+ */
+ outb(2, IER(dev->base_addr));
+ /*
+ * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+ * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+ * depending on the usage of the software DCD routine
+ */
+ ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
+ printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
+ "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+ bc->options, uart_str[u]);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ outb(0, IER(dev->base_addr));
+ outb(1, MCR(dev->base_addr));
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, SER12_EXTENT);
+ printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
+ bc_drvname, dev->base_addr, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+ bc_drvname,
+ bc_drvinfo,
+ ser12_open,
+ ser12_close,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+ bc->options = !!strchr(modestr, '*');
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct baycom_state *bc;
+ struct baycom_ioctl bi;
+ int cmd2;
+
+ if (!dev || !dev->priv ||
+ ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ bc = (struct baycom_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (get_user(cmd2, (int *)ifr->ifr_data))
+ return -EFAULT;
+ switch (hi->cmd) {
+ default:
+ break;
+
+ case HDLCDRVCTL_GETMODE:
+ strcpy(hi->data.modename, "ser12");
+ if (bc->options & 1)
+ strcat(hi->data.modename, "*");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_SETMODE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+ return baycom_setmode(bc, hi->data.modename);
+
+ case HDLCDRVCTL_MODELIST:
+ strcpy(hi->data.modename, "ser12");
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_MODEMPARMASK:
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+ }
+
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+ case BAYCOMCTL_GETDEBUG:
+ bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+ bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+ bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+ break;
+#endif /* BAYCOM_DEBUG */
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_ser_hdx_init(void))
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct baycom_state *bc;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+
+ printk(bc_drvinfo);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ sprintf(ifname, "bcsh%d", i);
+
+ if (!baycom_ports[i].mode)
+ set_hw = 0;
+ if (!set_hw)
+ baycom_ports[i].iobase = baycom_ports[i].irq = 0;
+ j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
+ sizeof(struct baycom_state),
+ ifname, baycom_ports[i].iobase,
+ baycom_ports[i].irq, 0);
+ if (!j) {
+ bc = (struct baycom_state *)dev->priv;
+ if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+ set_hw = 0;
+ found++;
+ } else {
+ printk(KERN_WARNING "%s: cannot register net device\n",
+ bc_drvname);
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+ baycom_ports[i].mode = mode[i];
+ baycom_ports[i].iobase = iobase[i];
+ baycom_ports[i].irq = irq[i];
+ }
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+ return baycom_ser_hdx_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (bc) {
+ if (bc->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "baycom: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_ser_=io,irq,mode
+ * mode: [*]
+ * * indicates sofware DCD
+ */
+
+__initfunc(void baycom_ser_hdx_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+ if ((i >= NR_PORTS) || (ints[0] < 2)) {
+ printk(KERN_INFO "%s: too many or invalid interface "
+ "specifications\n", bc_drvname);
+ return;
+ }
+ baycom_ports[i].mode = str;
+ baycom_ports[i].iobase = ints[1];
+ baycom_ports[i].irq = ints[2];
+ if (i < NR_PORTS-1)
+ baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
new file mode 100644
index 000000000..54d63121e
--- /dev/null
+++ b/drivers/net/hamradio/bpqether.c
@@ -0,0 +1,669 @@
+/*
+ * G8BPQ compatible "AX.25 via ethernet" driver release 003
+ *
+ * This code REQUIRES 2.0.0 or higher/ NET3.029
+ *
+ * This module:
+ * This module 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 is a "pseudo" network driver to allow AX.25 over Ethernet
+ * using G8BPQ encapsulation. It has been extracted from the protocol
+ * implementation because
+ *
+ * - things got unreadable within the protocol stack
+ * - to cure the protocol stack from "feature-ism"
+ * - a protocol implementation shouldn't need to know on
+ * which hardware it is running
+ * - user-level programs like the AX.25 utilities shouldn't
+ * need to know about the hardware.
+ * - IP over ethernet encapsulated AX.25 was impossible
+ * - rxecho.c did not work
+ * - to have room for extensions
+ * - it just deserves to "live" as an own driver
+ *
+ * This driver can use any ethernet destination address, and can be
+ * limited to accept frames from one dedicated ethernet card only.
+ *
+ * Note that the driver sets up the BPQ devices automagically on
+ * startup or (if started before the "insmod" of an ethernet device)
+ * on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
+ * the ethernet device (in fact: as soon as another ethernet or bpq
+ * device gets "ifconfig"ured).
+ *
+ * I have heard that several people are thinking of experiments
+ * with highspeed packet radio using existing ethernet cards.
+ * Well, this driver is prepared for this purpose, just add
+ * your tx key control and a txdelay / tailtime algorithm,
+ * probably some buffering, and /voila/...
+ *
+ * History
+ * BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25
+ * protocol stack and added my own
+ * yet existing patches
+ * BPQ 002 Joerg(DL1BKE) Scan network device list on
+ * startup.
+ * BPQ 003 Joerg(DL1BKE) Ethernet destination address
+ * and accepted source address
+ * can be configured by an ioctl()
+ * call.
+ * Fixed to match Linux networking
+ * changes - 2.1.15.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/bpqether.h>
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+ {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
+ {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char bpq_eth_addr[6];
+
+static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *);
+static int bpq_device_event(struct notifier_block *, unsigned long, void *);
+static char *bpq_print_ethaddr(unsigned char *);
+
+static struct packet_type bpq_packet_type = {
+ 0, /* ntohs(ETH_P_BPQ),*/
+ 0, /* copy */
+ bpq_rcv,
+ NULL,
+ NULL,
+};
+
+static struct notifier_block bpq_dev_notifier = {
+ bpq_device_event,
+ 0
+};
+
+
+#define MAXBPQDEV 100
+
+static struct bpqdev {
+ struct bpqdev *next;
+ char ethname[14]; /* ether device name */
+ struct device *ethdev; /* link to ethernet device */
+ struct device axdev; /* bpq device (bpq#) */
+ struct net_device_stats stats; /* some statistics */
+ char dest_addr[6]; /* ether destination address */
+ char acpt_addr[6]; /* accept ether frames from this address only */
+} *bpq_devices = NULL;
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ * Get the ethernet device for a BPQ device
+ */
+static __inline__ struct device *bpq_get_ether_dev(struct device *dev)
+{
+ struct bpqdev *bpq;
+
+ bpq = (struct bpqdev *)dev->priv;
+
+ return (bpq != NULL) ? bpq->ethdev : NULL;
+}
+
+/*
+ * Get the BPQ device for the ethernet device
+ */
+static __inline__ struct device *bpq_get_ax25_dev(struct device *dev)
+{
+ struct bpqdev *bpq;
+
+ for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+ if (bpq->ethdev == dev)
+ return &bpq->axdev;
+
+ return NULL;
+}
+
+static __inline__ int dev_is_ethdev(struct device *dev)
+{
+ return (
+ dev->type == ARPHRD_ETHER
+ && strncmp(dev->name, "dummy", 5)
+ );
+}
+
+/*
+ * Sanity check: remove all devices that ceased to exists and
+ * return '1' if the given BPQ device was affected.
+ */
+static int bpq_check_devices(struct device *dev)
+{
+ struct bpqdev *bpq, *bpq_prev;
+ int result = 0;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ bpq_prev = NULL;
+
+ for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
+ if (!dev_get(bpq->ethname)) {
+ if (bpq_prev)
+ bpq_prev->next = bpq->next;
+ else
+ bpq_devices = bpq->next;
+
+ if (&bpq->axdev == dev)
+ result = 1;
+
+ unregister_netdev(&bpq->axdev);
+ kfree(bpq);
+ }
+
+ bpq_prev = bpq;
+ }
+
+ restore_flags(flags);
+
+ return result;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ * Receive an AX.25 frame via an ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+ int len;
+ char * ptr;
+ struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
+ struct bpqdev *bpq;
+
+ skb->sk = NULL; /* Initially we don't know who it's for */
+
+ dev = bpq_get_ax25_dev(dev);
+
+ if (dev == NULL || dev->start == 0) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /*
+ * if we want to accept frames from just one ethernet device
+ * we check the source address of the sender.
+ */
+
+ bpq = (struct bpqdev *)dev->priv;
+
+ if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) {
+ printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source));
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ len = skb->data[0] + skb->data[1] * 256 - 5;
+
+ skb_pull(skb, 2); /* Remove the length bytes */
+ skb_trim(skb, len); /* Set the length of the data */
+
+ ((struct bpqdev *)dev->priv)->stats.rx_packets++;
+ ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len;
+
+ ptr = skb_push(skb, 1);
+ *ptr = 0;
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+
+ return 0;
+}
+
+/*
+ * Send an AX.25 frame via an ethernet interface
+ */
+static int bpq_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct sk_buff *newskb;
+ unsigned char *ptr;
+ struct bpqdev *bpq;
+ int size;
+
+ /*
+ * Just to be *really* sure not to send anything if the interface
+ * is down, the ethernet device may have gone.
+ */
+ if (!dev->start) {
+ bpq_check_devices(dev);
+ kfree_skb(skb, FREE_WRITE);
+ return -ENODEV;
+ }
+
+ skb_pull(skb, 1);
+ size = skb->len;
+
+ /*
+ * The AX.25 code leaves enough room for the ethernet header, but
+ * sendto() does not.
+ */
+ if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) { /* Ough! */
+ if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
+ printk(KERN_WARNING "bpqether: out of memory\n");
+ kfree_skb(skb, FREE_WRITE);
+ return -ENOMEM;
+ }
+
+ if (skb->sk != NULL)
+ skb_set_owner_w(newskb, skb->sk);
+
+ kfree_skb(skb, FREE_WRITE);
+ skb = newskb;
+ }
+
+ skb->protocol = htons(ETH_P_AX25);
+
+ ptr = skb_push(skb, 2);
+
+ *ptr++ = (size + 5) % 256;
+ *ptr++ = (size + 5) / 256;
+
+ bpq = (struct bpqdev *)dev->priv;
+
+ if ((dev = bpq_get_ether_dev(dev)) == NULL) {
+ bpq->stats.tx_dropped++;
+ kfree_skb(skb, FREE_WRITE);
+ return -ENODEV;
+ }
+
+ skb->dev = dev;
+ dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+ bpq->stats.tx_packets++;
+ bpq->stats.tx_bytes+=skb->len;
+
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+/*
+ * Statistics
+ */
+static struct net_device_stats *bpq_get_stats(struct device *dev)
+{
+ struct bpqdev *bpq;
+
+ bpq = (struct bpqdev *)dev->priv;
+
+ return &bpq->stats;
+}
+
+/*
+ * Set AX.25 callsign
+ */
+static int bpq_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+ return 0;
+}
+
+/* Ioctl commands
+ *
+ * SIOCSBPQETHOPT reserved for enhancements
+ * SIOCSBPQETHADDR set the destination and accepted
+ * source ethernet address (broadcast
+ * or multicast: accept all)
+ */
+static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int err;
+ struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
+ struct bpqdev *bpq = dev->priv;
+ struct bpq_req req;
+
+ if (!suser())
+ return -EPERM;
+
+ if (bpq == NULL) /* woops! */
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOCSBPQETHOPT:
+ if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
+ return err;
+ copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req));
+ switch (req.cmd) {
+ case SIOCGBPQETHPARAM:
+ case SIOCSBPQETHPARAM:
+ default:
+ return -EINVAL;
+ }
+
+ break;
+
+ case SIOCSBPQETHADDR:
+ if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
+ return err;
+ copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
+ copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int bpq_open(struct device *dev)
+{
+ if (bpq_check_devices(dev))
+ return -ENODEV; /* oops, it's gone */
+
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int bpq_close(struct device *dev)
+{
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * currently unused
+ */
+static int bpq_dev_init(struct device *dev)
+{
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ * Proc filesystem
+ */
+static char * bpq_print_ethaddr(unsigned char *e)
+{
+ static char buf[18];
+
+ sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ e[0], e[1], e[2], e[3], e[4], e[5]);
+
+ return buf;
+}
+
+int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct bpqdev *bpqdev;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ cli();
+
+ len += sprintf(buffer, "dev ether destination accept from\n");
+
+ for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) {
+ len += sprintf(buffer + len, "%-5s %-10s %s ",
+ bpqdev->axdev.name, bpqdev->ethname,
+ bpq_print_ethaddr(bpqdev->dest_addr));
+
+ len += sprintf(buffer + len, "%s\n",
+ (bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr));
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ * Setup a new device.
+ */
+static int bpq_new_device(struct device *dev)
+{
+ int k;
+ unsigned char *buf;
+ struct bpqdev *bpq, *bpq2;
+
+ if ((bpq = kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ memset(bpq, 0, sizeof(struct bpqdev));
+
+ bpq->ethdev = dev;
+
+ bpq->ethname[sizeof(bpq->ethname)-1] = '\0';
+ strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1);
+
+ memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
+ memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
+
+ dev = &bpq->axdev;
+ buf = kmalloc(14, GFP_KERNEL);
+
+ for (k = 0; k < MAXBPQDEV; k++) {
+ struct device *odev;
+
+ sprintf(buf, "bpq%d", k);
+
+ if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev))
+ break;
+ }
+
+ if (k == MAXBPQDEV) {
+ kfree(bpq);
+ return -ENODEV;
+ }
+
+ dev->priv = (void *)bpq; /* pointer back */
+ dev->name = buf;
+ dev->init = bpq_dev_init;
+
+ if (register_netdev(dev) != 0) {
+ kfree(bpq);
+ return -EIO;
+ }
+
+ dev_init_buffers(dev);
+
+ dev->hard_start_xmit = bpq_xmit;
+ dev->open = bpq_open;
+ dev->stop = bpq_close;
+ dev->set_mac_address = bpq_set_mac_address;
+ dev->get_stats = bpq_get_stats;
+ dev->do_ioctl = bpq_ioctl;
+
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN);
+
+ dev->flags = 0;
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+#endif
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ cli();
+
+ if (bpq_devices == NULL) {
+ bpq_devices = bpq;
+ } else {
+ for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next);
+ bpq2->next = bpq;
+ }
+
+ sti();
+
+ return 0;
+}
+
+
+/*
+ * Handle device status changes.
+ */
+static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+ struct device *dev = (struct device *)ptr;
+
+ if (!dev_is_ethdev(dev))
+ return NOTIFY_DONE;
+
+ bpq_check_devices(NULL);
+
+ switch (event) {
+ case NETDEV_UP: /* new ethernet device -> new BPQ interface */
+ if (bpq_get_ax25_dev(dev) == NULL)
+ bpq_new_device(dev);
+ break;
+
+ case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */
+ if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+ dev_close(dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+__initfunc(int bpq_init(void))
+{
+ struct device *dev;
+
+ bpq_packet_type.type = htons(ETH_P_BPQ);
+ dev_add_pack(&bpq_packet_type);
+
+ register_netdevice_notifier(&bpq_dev_notifier);
+
+ printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_AX25_BPQETHER, 8, "bpqether",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ bpq_get_info
+ });
+#endif
+
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev_is_ethdev(dev))
+ bpq_new_device(dev);
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
+
+int init_module(void)
+{
+ return bpq_init();
+}
+
+void cleanup_module(void)
+{
+ struct bpqdev *bpq;
+
+ dev_remove_pack(&bpq_packet_type);
+
+ unregister_netdevice_notifier(&bpq_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_AX25_BPQETHER);
+#endif
+
+ for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+ unregister_netdev(&bpq->axdev);
+}
+#endif
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
new file mode 100644
index 000000000..148ed14f4
--- /dev/null
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -0,0 +1,1024 @@
+/*****************************************************************************/
+
+/*
+ * hdlcdrv.c -- HDLC packet radio network 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.
+ *
+ * The driver was derived from Donald Beckers skeleton.c
+ * Written 1993-94 by Donald Becker.
+ *
+ * History:
+ * 0.1 21.09.96 Started
+ * 18.10.96 Changed to new user space access routines
+ * (copy_{to,from}_user)
+ * 0.2 21.11.96 various small changes
+ * 0.3 03.03.97 fixed (hopefully) IP not working with ax.25 as a module
+ * 0.4 16.04.97 init code/data tagged
+ * 0.5 30.07.97 made HDLC buffers bigger (solves a problem with the
+ * soundmodem driver)
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h>
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.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 < 0x20115
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+ int i;
+ for(i=0;i<DEV_NUMBUFFS;i++)
+ {
+ skb_queue_head_init(&dev->buffs[i]);
+ }
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20125
+#define test_and_set_bit set_bit
+#define test_and_clear_bit clear_bit
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#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 min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ unsigned int crc = 0xffff;
+
+ for (;len>0;len--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+ crc ^= 0xffff;
+ *buffer++ = crc;
+ *buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+ unsigned int crc = 0xffff;
+
+ for (; cnt > 0; cnt--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+ return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+ unsigned int crc = 0xffff;
+
+ for (; cnt > 0; cnt--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+ crc ^= 0xffff;
+ return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits,
+ int num)
+{
+ int added = 0;
+
+ while (s->hdlcrx.rx_state && num >= 8) {
+ if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+ s->hdlcrx.rx_state = 0;
+ return 0;
+ }
+ *s->hdlcrx.bp++ = bits >> (32-num);
+ s->hdlcrx.len++;
+ num -= 8;
+ added += 8;
+ }
+ return added;
+}
+
+static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
+{
+ struct sk_buff *skb;
+ int pkt_len;
+ unsigned char *cp;
+
+ if (s->hdlcrx.len < 4)
+ return;
+ if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len))
+ return;
+ pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+ if (!(skb = dev_alloc_skb(pkt_len))) {
+ printk("%s: memory squeeze, dropping packet\n",
+ s->ifname);
+ s->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = dev;
+ cp = skb_put(skb, pkt_len);
+ *cp++ = 0; /* KISS kludge */
+ memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
+{
+ int i;
+ unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+
+ if (!s || s->magic != HDLCDRV_MAGIC)
+ return;
+ if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
+ return;
+
+ while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+ word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);
+
+#ifdef HDLCDRV_DEBUG
+ hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+ s->hdlcrx.bitstream >>= 16;
+ s->hdlcrx.bitstream |= word << 16;
+ s->hdlcrx.bitbuf >>= 16;
+ s->hdlcrx.bitbuf |= word << 16;
+ s->hdlcrx.numbits += 16;
+ for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+ mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff;
+ i >= 0;
+ i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1,
+ mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+ if ((s->hdlcrx.bitstream & mask1) == mask1)
+ s->hdlcrx.rx_state = 0; /* abort received */
+ else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+ /* flag received */
+ if (s->hdlcrx.rx_state) {
+ hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf
+ << (8+i),
+ s->hdlcrx.numbits
+ -8-i);
+ hdlc_rx_flag(dev, s);
+ }
+ s->hdlcrx.len = 0;
+ s->hdlcrx.bp = s->hdlcrx.buffer;
+ s->hdlcrx.rx_state = 1;
+ s->hdlcrx.numbits = i;
+ } else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+ /* stuffed bit */
+ s->hdlcrx.numbits--;
+ s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+ ((s->hdlcrx.bitbuf & mask6) << 1);
+ }
+ }
+ s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+ s->hdlcrx.numbits);
+ }
+ clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct hdlcdrv_state *s,
+ unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
+#else /* KISS_VERBOSE */
+#define PKP(a,b)
+#endif /* KISS_VERBOSE */
+
+ if (len < 2)
+ return;
+ switch(data[0]) {
+ case PARAM_TXDELAY:
+ s->ch_params.tx_delay = data[1];
+ PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+ break;
+ case PARAM_PERSIST:
+ s->ch_params.ppersist = data[1];
+ PKP("p persistence = %u", s->ch_params.ppersist);
+ break;
+ case PARAM_SLOTTIME:
+ s->ch_params.slottime = data[1];
+ PKP("slot time = %ums", s->ch_params.slottime);
+ break;
+ case PARAM_TXTAIL:
+ s->ch_params.tx_tail = data[1];
+ PKP("TX tail = %ums", s->ch_params.tx_tail);
+ break;
+ case PARAM_FULLDUP:
+ s->ch_params.fulldup = !!data[1];
+ PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+ break;
+ default:
+ break;
+ }
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
+{
+ unsigned int mask1, mask2, mask3;
+ int i;
+ struct sk_buff *skb;
+ int pkt_len;
+
+ if (!s || s->magic != HDLCDRV_MAGIC)
+ return;
+ if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
+ return;
+ for (;;) {
+ if (s->hdlctx.numbits >= 16) {
+ if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ }
+ hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+ s->hdlctx.bitbuf >>= 16;
+ s->hdlctx.numbits -= 16;
+ }
+ switch (s->hdlctx.tx_state) {
+ default:
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ case 0:
+ case 1:
+ if (s->hdlctx.numflags) {
+ s->hdlctx.numflags--;
+ s->hdlctx.bitbuf |=
+ 0x7e7e << s->hdlctx.numbits;
+ s->hdlctx.numbits += 16;
+ break;
+ }
+ if (s->hdlctx.tx_state == 1) {
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ }
+ if (!(skb = skb_dequeue(&s->send_queue))) {
+ int flgs = tenms_to_2flags
+ (s, s->ch_params.tx_tail);
+ if (flgs < 2)
+ flgs = 2;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = flgs;
+ break;
+ }
+ if (skb->data[0] != 0) {
+ do_kiss_params(s, skb->data, skb->len);
+ dev_kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+ pkt_len = skb->len-1; /* strip KISS byte */
+ if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+ memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+ dev_kfree_skb(skb, FREE_WRITE);
+ s->hdlctx.bp = s->hdlctx.buffer;
+ append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+ s->hdlctx.len = pkt_len+2; /* the appended CRC */
+ s->hdlctx.tx_state = 2;
+ s->hdlctx.bitstream = 0;
+ s->stats.tx_packets++;
+ break;
+ case 2:
+ if (!s->hdlctx.len) {
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = 1;
+ break;
+ }
+ s->hdlctx.len--;
+ s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+ s->hdlctx.numbits;
+ s->hdlctx.bitstream >>= 8;
+ s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+ mask1 = 0x1f000;
+ mask2 = 0x10000;
+ mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+ s->hdlctx.numbits += 8;
+ for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1,
+ mask3 = (mask3 << 1) | 1) {
+ if ((s->hdlctx.bitstream & mask1) != mask1)
+ continue;
+ s->hdlctx.bitstream &= ~mask2;
+ s->hdlctx.bitbuf =
+ (s->hdlctx.bitbuf & mask3) |
+ ((s->hdlctx.bitbuf &
+ (~mask3)) << 1);
+ s->hdlctx.numbits++;
+ mask3 = (mask3 << 1) | 1;
+ }
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct device *dev, struct hdlcdrv_state *s)
+{
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+ s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+ hdlcdrv_transmitter(dev, s);
+ s->hdlctx.ptt = 1;
+ s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+ random_seed = 28629 * random_seed + 157;
+ return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
+{
+ if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt ||
+ skb_queue_empty(&s->send_queue))
+ return;
+ if (s->ch_params.fulldup) {
+ start_tx(dev, s);
+ return;
+ }
+ if (s->hdlcrx.dcd) {
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ return;
+ }
+ if ((--s->hdlctx.slotcnt) > 0)
+ return;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ if ((random_num() % 256) > s->ch_params.ppersist)
+ return;
+ start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static inline int hdlcdrv_paranoia_check(struct device *dev,
+ const char *routine)
+{
+ if (!dev || !dev->priv ||
+ ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
+ "struct in routine %s\n", routine);
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct hdlcdrv_state *sm;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
+ return 0;
+ sm = (struct hdlcdrv_state *)dev->priv;
+ skb_queue_tail(&sm->send_queue, skb);
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ /* addr is an AX.25 shifted ASCII mac address */
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20119
+static struct net_device_stats *hdlcdrv_get_stats(struct device *dev)
+#else
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+#endif
+{
+ struct hdlcdrv_state *sm;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
+ return NULL;
+ sm = (struct hdlcdrv_state *)dev->priv;
+ /*
+ * Get the current statistics. This may be called with the
+ * card open or closed.
+ */
+ return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+ int i;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (dev->start)
+ return 0;
+ if (!s->ops || !s->ops->open)
+ return -ENODEV;
+
+ dev->start = 1;
+ /*
+ * initialise some variables
+ */
+ s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+ s->hdlcrx.in_hdlc_rx = 0;
+ s->hdlcrx.rx_state = 0;
+
+ s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+ s->hdlctx.in_hdlc_tx = 0;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = 0;
+ s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+ s->hdlctx.ptt = 0;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ s->hdlctx.calibrate = 0;
+
+ i = s->ops->open(dev);
+ if (i) {
+ dev->start = 0;
+ return i;
+ }
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * The inverse routine to hdlcdrv_open().
+ */
+
+static int hdlcdrv_close(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+ struct sk_buff *skb;
+ int i = 0;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (!dev->start)
+ return 0;
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (s->ops && s->ops->close)
+ i = s->ops->close(dev);
+ /* Free any buffers left in the hardware transmit queue */
+ while ((skb = skb_dequeue(&s->send_queue)))
+ dev_kfree_skb(skb, FREE_WRITE);
+ return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct hdlcdrv_state *s;
+ struct hdlcdrv_ioctl bi;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE) {
+ if (s->ops && s->ops->ioctl)
+ return s->ops->ioctl(dev, ifr, &bi, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ if (s->ops && s->ops->ioctl)
+ return s->ops->ioctl(dev, ifr, &bi, cmd);
+ return -ENOIOCTLCMD;
+
+ case HDLCDRVCTL_GETCHANNELPAR:
+ bi.data.cp.tx_delay = s->ch_params.tx_delay;
+ bi.data.cp.tx_tail = s->ch_params.tx_tail;
+ bi.data.cp.slottime = s->ch_params.slottime;
+ bi.data.cp.ppersist = s->ch_params.ppersist;
+ bi.data.cp.fulldup = s->ch_params.fulldup;
+ break;
+
+ case HDLCDRVCTL_SETCHANNELPAR:
+ if (!suser())
+ return -EACCES;
+ s->ch_params.tx_delay = bi.data.cp.tx_delay;
+ s->ch_params.tx_tail = bi.data.cp.tx_tail;
+ s->ch_params.slottime = bi.data.cp.slottime;
+ s->ch_params.ppersist = bi.data.cp.ppersist;
+ s->ch_params.fulldup = bi.data.cp.fulldup;
+ s->hdlctx.slotcnt = 1;
+ return 0;
+
+ case HDLCDRVCTL_GETMODEMPAR:
+ bi.data.mp.iobase = dev->base_addr;
+ bi.data.mp.irq = dev->irq;
+ bi.data.mp.dma = dev->dma;
+ bi.data.mp.dma2 = s->ptt_out.dma2;
+ bi.data.mp.seriobase = s->ptt_out.seriobase;
+ bi.data.mp.pariobase = s->ptt_out.pariobase;
+ bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+ break;
+
+ case HDLCDRVCTL_SETMODEMPAR:
+ if ((!suser()) || dev->start)
+ return -EACCES;
+ dev->base_addr = bi.data.mp.iobase;
+ dev->irq = bi.data.mp.irq;
+ dev->dma = bi.data.mp.dma;
+ s->ptt_out.dma2 = bi.data.mp.dma2;
+ s->ptt_out.seriobase = bi.data.mp.seriobase;
+ s->ptt_out.pariobase = bi.data.mp.pariobase;
+ s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+ return 0;
+
+ case HDLCDRVCTL_GETSTAT:
+ bi.data.cs.ptt = hdlcdrv_ptt(s);
+ bi.data.cs.dcd = s->hdlcrx.dcd;
+ bi.data.cs.ptt_keyed = s->ptt_keyed;
+ bi.data.cs.tx_packets = s->stats.tx_packets;
+ bi.data.cs.tx_errors = s->stats.tx_errors;
+ bi.data.cs.rx_packets = s->stats.rx_packets;
+ bi.data.cs.rx_errors = s->stats.rx_errors;
+ break;
+
+ case HDLCDRVCTL_OLDGETSTAT:
+ bi.data.ocs.ptt = hdlcdrv_ptt(s);
+ bi.data.ocs.dcd = s->hdlcrx.dcd;
+ bi.data.ocs.ptt_keyed = s->ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+ bi.data.ocs.stats = s->stats;
+#endif
+ break;
+
+ case HDLCDRVCTL_CALIBRATE:
+ s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
+ return 0;
+
+ case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+ return -EPERM;
+#else /* HDLCDRV_DEBUG */
+ if (s->bitbuf_channel.rd == s->bitbuf_channel.wr)
+ return -EAGAIN;
+ bi.data.bits =
+ s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+ s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+ sizeof(s->bitbuf_channel.buffer);
+ break;
+#endif /* HDLCDRV_DEBUG */
+
+ case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+ return -EPERM;
+#else /* HDLCDRV_DEBUG */
+ if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr)
+ return -EAGAIN;
+ bi.data.bits =
+ s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+ s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+ sizeof(s->bitbuf_hdlc.buffer);
+ break;
+#endif /* HDLCDRV_DEBUG */
+
+ case HDLCDRVCTL_DRIVERNAME:
+ if (s->ops && s->ops->drvname) {
+ strncpy(bi.data.drivername, s->ops->drvname,
+ sizeof(bi.data.drivername));
+ break;
+ }
+ bi.data.drivername[0] = '\0';
+ break;
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int hdlcdrv_probe(struct device *dev)
+{
+ const struct hdlcdrv_channel_params dflt_ch_params = {
+ 20, 2, 10, 40, 0
+ };
+ struct hdlcdrv_state *s;
+
+ if (!dev)
+ return -ENXIO;
+ /*
+ * not a real probe! only initialize data structures
+ */
+ s = (struct hdlcdrv_state *)dev->priv;
+ /*
+ * initialize the hdlcdrv_state struct
+ */
+ s->ch_params = dflt_ch_params;
+ s->ptt_keyed = 0;
+
+ s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+ s->hdlcrx.in_hdlc_rx = 0;
+ s->hdlcrx.rx_state = 0;
+
+ s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+ s->hdlctx.in_hdlc_tx = 0;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = 0;
+ s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+ s->hdlctx.ptt = 0;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+ s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+ s->bitbuf_channel.shreg = 0x80;
+
+ s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+ s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+ /*
+ * initialize the device struct
+ */
+ dev->open = hdlcdrv_open;
+ dev->stop = hdlcdrv_close;
+ dev->do_ioctl = hdlcdrv_ioctl;
+ dev->hard_start_xmit = hdlcdrv_send_packet;
+ dev->get_stats = hdlcdrv_get_stats;
+
+ /* Fill in the fields of the device structure */
+
+ dev_init_buffers(dev);
+
+ skb_queue_head_init(&s->send_queue);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+ dev->set_mac_address = hdlcdrv_set_mac_address;
+
+ dev->type = ARPHRD_AX25; /* AF_AX25 device */
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */
+ dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+
+ /* New style flags */
+ dev->flags = 0;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+ unsigned int privsize, char *ifname,
+ unsigned int baseaddr, unsigned int irq,
+ unsigned int dma)
+{
+ struct hdlcdrv_state *s;
+
+ if (!dev || !ops)
+ return -EACCES;
+ if (privsize < sizeof(struct hdlcdrv_state))
+ privsize = sizeof(struct hdlcdrv_state);
+ memset(dev, 0, sizeof(struct device));
+ if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
+ return -ENOMEM;
+ /*
+ * initialize part of the hdlcdrv_state struct
+ */
+ memset(s, 0, privsize);
+ s->magic = HDLCDRV_MAGIC;
+ strncpy(s->ifname, ifname, sizeof(s->ifname));
+ s->ops = ops;
+ /*
+ * initialize part of the device struct
+ */
+ dev->name = s->ifname;
+ dev->if_port = 0;
+ dev->init = hdlcdrv_probe;
+ dev->start = 0;
+ dev->tbusy = 1;
+ dev->base_addr = baseaddr;
+ dev->irq = irq;
+ dev->dma = dma;
+ if (register_netdev(dev)) {
+ printk(KERN_WARNING "hdlcdrv: cannot register net "
+ "device %s\n", s->ifname);
+ kfree(dev->priv);
+ return -ENXIO;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_unregister_hdlcdrv(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+
+ if (!dev)
+ return -EINVAL;
+ if (!(s = (struct hdlcdrv_state *)dev->priv))
+ return -EINVAL;
+ if (s->magic != HDLCDRV_MAGIC)
+ return -EINVAL;
+ if (dev->start && s->ops->close)
+ s->ops->close(dev);
+ unregister_netdev(dev);
+ kfree(s);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+EXPORT_SYMBOL(hdlcdrv_receiver);
+EXPORT_SYMBOL(hdlcdrv_transmitter);
+EXPORT_SYMBOL(hdlcdrv_arbitrate);
+EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv);
+EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
+
+#else
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+ X(hdlcdrv_receiver),
+ X(hdlcdrv_transmitter),
+ X(hdlcdrv_arbitrate),
+ X(hdlcdrv_register_hdlcdrv),
+ X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int init_module(void))
+{
+ printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+ printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
+#if LINUX_VERSION_CODE < 0x20115
+ register_symtab(&hdlcdrv_syms);
+#endif
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
new file mode 100644
index 000000000..562a27681
--- /dev/null
+++ b/drivers/net/hamradio/mkiss.c
@@ -0,0 +1,1134 @@
+/*
+ * MKISS Driver
+ *
+ * This module:
+ * This module 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 module implements the AX.25 protocol for kernel-based
+ * devices like TTYs. It interfaces between a raw TTY, and the
+ * kernel's AX.25 protocol layers, just like slip.c.
+ * AX.25 needs to be seperated from slip.c while slip.c is no
+ * longer a static kernel device since it is a module.
+ * This method clears the way to implement other kiss protocols
+ * like mkiss smack g8bpq ..... so far only mkiss is implemented.
+ *
+ * Hans Alblas <hans@esrac.ele.tue.nl>
+ *
+ * History
+ * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/major.h>
+#include <linux/init.h>
+
+#include <linux/timer.h>
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <net/ax25.h>
+
+#include "mkiss.h"
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+#ifdef MODULE
+#define AX25_VERSION "AX25-MODULAR-NET3.019-NEWTTY"
+#define min(a,b) (a < b ? a : b)
+#else
+#define AX25_VERSION "AX25-NET3.019-NEWTTY"
+#endif
+
+#define NR_MKISS 4
+#define MKISS_SERIAL_TYPE_NORMAL 1
+
+struct mkiss_channel {
+ int magic; /* magic word */
+ int init; /* channel exists? */
+ struct tty_struct *tty; /* link to tty control structure */
+};
+
+typedef struct ax25_ctrl {
+ char if_name[8]; /* "ax0\0" .. "ax99999\0" */
+ struct ax_disp ctrl; /* */
+ struct device dev; /* the device */
+} ax25_ctrl_t;
+
+static ax25_ctrl_t **ax25_ctrls = NULL;
+
+int ax25_maxdev = AX25_MAXDEV; /* Can be overridden with insmod! */
+
+static struct tty_ldisc ax_ldisc;
+static struct tty_driver mkiss_driver;
+static int mkiss_refcount;
+static struct tty_struct *mkiss_table[NR_MKISS];
+static struct termios *mkiss_termios[NR_MKISS];
+static struct termios *mkiss_termios_locked[NR_MKISS];
+struct mkiss_channel MKISS_Info[NR_MKISS];
+
+static int ax25_init(struct device *);
+static int mkiss_init(void);
+static int mkiss_write(struct tty_struct *, int, const unsigned char *, int);
+static int kiss_esc(unsigned char *, unsigned char *, int);
+static void kiss_unesc(struct ax_disp *, unsigned char);
+
+/* Find a free channel, and link in this `tty' line. */
+static inline struct ax_disp *ax_alloc(void)
+{
+ ax25_ctrl_t *axp;
+ int i;
+
+ if (ax25_ctrls == NULL) /* Master array missing ! */
+ return NULL;
+
+ for (i = 0; i < ax25_maxdev; i++) {
+ axp = ax25_ctrls[i];
+
+ /* Not allocated ? */
+ if (axp == NULL)
+ break;
+
+ /* Not in use ? */
+ if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags))
+ break;
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= ax25_maxdev)
+ return NULL;
+
+ /* If no channels are available, allocate one */
+ if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+ axp = ax25_ctrls[i];
+ memset(axp, 0, sizeof(ax25_ctrl_t));
+
+ /* Initialize channel control data */
+ set_bit(AXF_INUSE, &axp->ctrl.flags);
+ sprintf(axp->if_name, "ax%d", i++);
+ axp->ctrl.tty = NULL;
+ axp->dev.name = axp->if_name;
+ axp->dev.base_addr = i;
+ axp->dev.priv = (void *)&axp->ctrl;
+ axp->dev.next = NULL;
+ axp->dev.init = ax25_init;
+ }
+
+ if (axp != NULL) {
+ /*
+ * register device so that it can be ifconfig'ed
+ * ax25_init() will be called as a side-effect
+ * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
+ */
+ if (register_netdev(&axp->dev) == 0) {
+ /* (Re-)Set the INUSE bit. Very Important! */
+ set_bit(AXF_INUSE, &axp->ctrl.flags);
+ axp->ctrl.dev = &axp->dev;
+ axp->dev.priv = (void *)&axp->ctrl;
+
+ return &axp->ctrl;
+ } else {
+ clear_bit(AXF_INUSE,&axp->ctrl.flags);
+ printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
+ }
+ }
+
+ return NULL;
+}
+
+/* Free an AX25 channel. */
+static inline void ax_free(struct ax_disp *ax)
+{
+ /* Free all AX25 frame buffers. */
+ if (ax->rbuff)
+ kfree(ax->rbuff);
+ ax->rbuff = NULL;
+ if (ax->xbuff)
+ kfree(ax->xbuff);
+ ax->xbuff = NULL;
+ if (!test_and_clear_bit(AXF_INUSE, &ax->flags))
+ printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
+}
+
+static void ax_changedmtu(struct ax_disp *ax)
+{
+ struct device *dev = ax->dev;
+ unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+ int len;
+ unsigned long flags;
+
+ len = dev->mtu * 2;
+
+ /*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2)
+ len = 576 * 2;
+
+ xbuff = kmalloc(len + 4, GFP_ATOMIC);
+ rbuff = kmalloc(len + 4, GFP_ATOMIC);
+
+ if (xbuff == NULL || rbuff == NULL) {
+ printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
+ ax->dev->name);
+ dev->mtu = ax->mtu;
+ if (xbuff != NULL)
+ kfree(xbuff);
+ if (rbuff != NULL)
+ kfree(rbuff);
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+
+ oxbuff = ax->xbuff;
+ ax->xbuff = xbuff;
+ orbuff = ax->rbuff;
+ ax->rbuff = rbuff;
+
+ if (ax->xleft) {
+ if (ax->xleft <= len) {
+ memcpy(ax->xbuff, ax->xhead, ax->xleft);
+ } else {
+ ax->xleft = 0;
+ ax->tx_dropped++;
+ }
+ }
+
+ ax->xhead = ax->xbuff;
+
+ if (ax->rcount) {
+ if (ax->rcount <= len) {
+ memcpy(ax->rbuff, orbuff, ax->rcount);
+ } else {
+ ax->rcount = 0;
+ ax->rx_over_errors++;
+ set_bit(AXF_ERROR, &ax->flags);
+ }
+ }
+
+ ax->mtu = dev->mtu + 73;
+ ax->buffsize = len;
+
+ restore_flags(flags);
+
+ if (oxbuff != NULL)
+ kfree(oxbuff);
+ if (orbuff != NULL)
+ kfree(orbuff);
+}
+
+
+/* Set the "sending" flag. This must be atomic, hence the ASM. */
+static inline void ax_lock(struct ax_disp *ax)
+{
+ if (test_and_set_bit(0, (void *)&ax->dev->tbusy))
+ printk(KERN_ERR "mkiss: %s: trying to lock already locked device!\n", ax->dev->name);
+}
+
+
+/* Clear the "sending" flag. This must be atomic, hence the ASM. */
+static inline void ax_unlock(struct ax_disp *ax)
+{
+ if (!test_and_clear_bit(0, (void *)&ax->dev->tbusy))
+ printk(KERN_ERR "mkiss: %s: trying to unlock already unlocked device!\n", ax->dev->name);
+}
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+ struct ax_disp *tmp_ax;
+ struct sk_buff *skb;
+ struct mkiss_channel *mkiss;
+ int count;
+
+ tmp_ax = ax;
+
+ if (ax->rbuff[0] > 0x0f) {
+ if (ax->mkiss != NULL) {
+ mkiss= ax->mkiss->tty->driver_data;
+ if (mkiss->magic == MKISS_DRIVER_MAGIC)
+ tmp_ax = ax->mkiss;
+ }
+ }
+
+ count = ax->rcount;
+
+ if ((skb = dev_alloc_skb(count)) == NULL) {
+ printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
+ ax->rx_dropped++;
+ return;
+ }
+
+ skb->dev = tmp_ax->dev;
+ memcpy(skb_put(skb,count), ax->rbuff, count);
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_AX25);
+ netif_rx(skb);
+ tmp_ax->rx_packets++;
+}
+
+/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
+static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
+{
+ unsigned char *p;
+ int actual, count;
+ struct mkiss_channel *mkiss = ax->tty->driver_data;
+
+ if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */
+ ax_changedmtu(ax);
+
+ if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */
+ len = ax->mtu;
+ printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
+ ax->tx_dropped++;
+ ax_unlock(ax);
+ return;
+ }
+
+ p = icp;
+
+ if (mkiss->magic != MKISS_DRIVER_MAGIC) {
+ count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+ ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
+ ax->tx_packets++;
+ ax->dev->trans_start = jiffies;
+ ax->xleft = count - actual;
+ ax->xhead = ax->xbuff + actual;
+ } else {
+ count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
+ ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
+ ax->tx_packets++;
+ ax->mkiss->dev->trans_start = jiffies;
+ ax->mkiss->xleft = count - actual;
+ ax->mkiss->xhead = ax->mkiss->xbuff + actual;
+ }
+}
+
+/*
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void ax25_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+ struct mkiss_channel *mkiss;
+
+ /* First make sure we're connected. */
+ if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+ return;
+ if (ax->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet
+ */
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+ if (ax->mkiss != NULL) {
+ mkiss= ax->mkiss->tty->driver_data;
+ if (mkiss->magic == MKISS_DRIVER_MAGIC)
+ ax_unlock(ax->mkiss);
+ }
+
+ ax_unlock(ax);
+ mark_bh(NET_BH);
+ return;
+ }
+
+ actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
+ ax->xleft -= actual;
+ ax->xhead += actual;
+}
+
+/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
+static int ax_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+ struct mkiss_channel *mkiss = ax->tty->driver_data;
+ struct ax_disp *tmp_ax;
+
+ tmp_ax = NULL;
+
+ if (mkiss->magic == MKISS_DRIVER_MAGIC) {
+ if (skb->data[0] < 0x10)
+ skb->data[0] = skb->data[0] + 0x10;
+ tmp_ax = ax->mkiss;
+ }
+
+ if (!dev->start) {
+ printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
+ return 1;
+ }
+
+ if (tmp_ax != NULL)
+ if (tmp_ax->dev->tbusy)
+ return 1;
+
+ if (tmp_ax != NULL)
+ if (dev->tbusy) {
+ printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
+ ax_unlock(ax);
+ }
+
+ if (dev->tbusy) {
+ /*
+ * May be we must check transmitter timeout here ?
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ if (jiffies - dev->trans_start < 20 * HZ) {
+ /* 20 sec timeout not reached */
+ return 1;
+ }
+
+ printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
+ (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
+ "bad line quality" : "driver error");
+
+ ax->xleft = 0;
+ ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ ax_unlock(ax);
+ }
+
+ /* We were not busy, so we are now... :-) */
+ if (skb != NULL) {
+ ax_lock(ax);
+ if (tmp_ax != NULL)
+ ax_lock(tmp_ax);
+ ax_encaps(ax, skb->data, skb->len);
+ kfree_skb(skb, FREE_WRITE);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+
+/* Return the frame type ID */
+static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+ if (type != htons(ETH_P_AX25))
+ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+ return 0;
+}
+
+
+static int ax_rebuild_header(struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+ return ax25_rebuild_header(skb);
+#else
+ return 0;
+#endif
+}
+
+#endif /* CONFIG_{AX25,AX25_MODULE} */
+
+/* Open the low-level part of the AX25 channel. Easy! */
+static int ax_open(struct device *dev)
+{
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+ unsigned long len;
+
+ if (ax->tty == NULL)
+ return -ENODEV;
+
+ /*
+ * Allocate the frame buffers:
+ *
+ * rbuff Receive buffer.
+ * xbuff Transmit buffer.
+ * cbuff Temporary compression buffer.
+ */
+ len = dev->mtu * 2;
+
+ /*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2)
+ len = 576 * 2;
+
+ if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+ goto norbuff;
+
+ if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+ goto noxbuff;
+
+ ax->mtu = dev->mtu + 73;
+ ax->buffsize = len;
+ ax->rcount = 0;
+ ax->xleft = 0;
+
+ ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ return 0;
+
+ /* Cleanup */
+ kfree(ax->xbuff);
+
+noxbuff:
+ kfree(ax->rbuff);
+
+norbuff:
+ return -ENOMEM;
+}
+
+
+/* Close the low-level part of the AX25 channel. Easy! */
+static int ax_close(struct device *dev)
+{
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+ if (ax->tty == NULL)
+ return -EBUSY;
+
+ ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ return 0;
+}
+
+static int ax25_receive_room(struct tty_struct *tty)
+{
+ return 65536; /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of data has been received, which can now be decapsulated
+ * and sent on to the AX.25 layer for further processing.
+ */
+static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+ struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+
+ if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+ return;
+
+ /*
+ * Argh! mtu change time! - costs us the packet part received
+ * at the change
+ */
+ if (ax->mtu != ax->dev->mtu + 73)
+ ax_changedmtu(ax);
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ if (fp != NULL && *fp++) {
+ if (!test_and_set_bit(AXF_ERROR, &ax->flags))
+ ax->rx_errors++;
+ cp++;
+ continue;
+ }
+
+ kiss_unesc(ax, *cp++);
+ }
+}
+
+static int ax25_open(struct tty_struct *tty)
+{
+ struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+ struct ax_disp *tmp_ax;
+ struct mkiss_channel *mkiss;
+ int err, cnt;
+
+ /* First make sure we're not already connected. */
+ if (ax && ax->magic == AX25_MAGIC)
+ return -EEXIST;
+
+ /* OK. Find a free AX25 channel to use. */
+ if ((ax = ax_alloc()) == NULL)
+ return -ENFILE;
+
+ ax->tty = tty;
+ tty->disc_data = ax;
+
+ ax->mkiss = NULL;
+ tmp_ax = NULL;
+
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+
+ /* Restore default settings */
+ ax->dev->type = ARPHRD_AX25;
+
+ /* Perform the low-level AX25 initialization. */
+ if ((err = ax_open(ax->dev)))
+ return err;
+
+ mkiss= ax->tty->driver_data;
+
+ if (mkiss->magic == MKISS_DRIVER_MAGIC) {
+ for (cnt = 1; cnt < ax25_maxdev; cnt++) {
+ if (ax25_ctrls[cnt]) {
+ if (ax25_ctrls[cnt]->dev.start) {
+ if (ax == &ax25_ctrls[cnt]->ctrl) {
+ cnt--;
+ tmp_ax = &ax25_ctrls[cnt]->ctrl;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (tmp_ax != NULL) {
+ ax->mkiss = tmp_ax;
+ tmp_ax->mkiss = ax;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ /* Done. We have linked the TTY line to a channel. */
+ return ax->dev->base_addr;
+}
+
+static void ax25_close(struct tty_struct *tty)
+{
+ struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+ int mkiss ;
+
+ /* First make sure we're connected. */
+ if (ax == NULL || ax->magic != AX25_MAGIC)
+ return;
+
+ mkiss = ax->mode;
+ if (ax->dev->flags & IFF_UP)
+ {
+ dev_lock_wait();
+ dev_close(ax->dev);
+ dev_unlock_list();
+ }
+
+ tty->disc_data = 0;
+ ax->tty = NULL;
+
+ /* VSV = very important to remove timers */
+ ax_free(ax);
+ unregister_netdev(ax->dev);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct net_device_stats *ax_get_stats(struct device *dev)
+{
+ static struct net_device_stats stats;
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+ memset(&stats, 0, sizeof(struct net_device_stats));
+
+ stats.rx_packets = ax->rx_packets;
+ stats.tx_packets = ax->tx_packets;
+ stats.rx_dropped = ax->rx_dropped;
+ stats.tx_dropped = ax->tx_dropped;
+ stats.tx_errors = ax->tx_errors;
+ stats.rx_errors = ax->rx_errors;
+ stats.rx_over_errors = ax->rx_over_errors;
+
+ return &stats;
+}
+
+
+/************************************************************************
+ * STANDARD ENCAPSULATION *
+ ************************************************************************/
+
+int kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+ unsigned char *ptr = d;
+ unsigned char c;
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+
+ *ptr++ = END;
+
+ while (len-- > 0) {
+ switch (c = *s++) {
+ case END:
+ *ptr++ = ESC;
+ *ptr++ = ESC_END;
+ break;
+ case ESC:
+ *ptr++ = ESC;
+ *ptr++ = ESC_ESC;
+ break;
+ default:
+ *ptr++ = c;
+ break;
+ }
+ }
+
+ *ptr++ = END;
+
+ return ptr - d;
+}
+
+static void kiss_unesc(struct ax_disp *ax, unsigned char s)
+{
+ switch (s) {
+ case END:
+ /* drop keeptest bit = VSV */
+ if (test_bit(AXF_KEEPTEST, &ax->flags))
+ clear_bit(AXF_KEEPTEST, &ax->flags);
+
+ if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
+ ax_bump(ax);
+
+ clear_bit(AXF_ESCAPE, &ax->flags);
+ ax->rcount = 0;
+ return;
+
+ case ESC:
+ set_bit(AXF_ESCAPE, &ax->flags);
+ return;
+ case ESC_ESC:
+ if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+ s = ESC;
+ break;
+ case ESC_END:
+ if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+ s = END;
+ break;
+ }
+
+ if (!test_bit(AXF_ERROR, &ax->flags)) {
+ if (ax->rcount < ax->buffsize) {
+ ax->rbuff[ax->rcount++] = s;
+ return;
+ }
+
+ ax->rx_over_errors++;
+ set_bit(AXF_ERROR, &ax->flags);
+ }
+}
+
+
+int ax_set_mac_address(struct device *dev, void *addr)
+{
+ int err;
+
+ if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0)
+ return err;
+
+ /* addr is an AX.25 shifted ASCII mac address */
+ copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN);
+
+ return 0;
+}
+
+static int ax_set_dev_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+
+ return 0;
+}
+
+
+/* Perform I/O control on an active ax25 channel. */
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+ struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+ int err;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (ax == NULL || ax->magic != AX25_MAGIC)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGIFNAME:
+ if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0)
+ return err;
+ copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1);
+ return 0;
+
+ case SIOCGIFENCAP:
+ if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0)
+ return err;
+ put_user(4, (int *)arg);
+ return 0;
+
+ case SIOCSIFENCAP:
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
+ return err;
+ get_user(tmp, (int *)arg);
+ ax->mode = tmp;
+ ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */
+ ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+ ax->dev->type = ARPHRD_AX25;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return ax_set_mac_address(ax->dev, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int ax_open_dev(struct device *dev)
+{
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+ if (ax->tty==NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+/* Initialize AX25 control device -- register AX25 line discipline */
+__initfunc(int mkiss_init_ctrl_dev(void))
+{
+ int status;
+
+ if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
+
+ if ((ax25_ctrls = kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+ printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array ! No mkiss available\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
+
+ /* Fill in our line protocol discipline, and register it */
+ memset(&ax_ldisc, 0, sizeof(ax_ldisc));
+ ax_ldisc.magic = TTY_LDISC_MAGIC;
+ ax_ldisc.name = "mkiss";
+ ax_ldisc.flags = 0;
+ ax_ldisc.open = ax25_open;
+ ax_ldisc.close = ax25_close;
+ ax_ldisc.read = NULL;
+ ax_ldisc.write = NULL;
+ ax_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl;
+ ax_ldisc.poll = NULL;
+
+ ax_ldisc.receive_buf = ax25_receive_buf;
+ ax_ldisc.receive_room = ax25_receive_room;
+ ax_ldisc.write_wakeup = ax25_write_wakeup;
+
+ if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0)
+ printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
+
+ mkiss_init();
+
+#ifdef MODULE
+ return status;
+#else
+ /*
+ * Return "not found", so that dev_init() will unlink
+ * the placeholder device entry for us.
+ */
+ return ENODEV;
+#endif
+}
+
+
+/* Initialize the driver. Called by network startup. */
+
+static int ax25_init(struct device *dev)
+{
+ struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+ static char ax25_bcast[AX25_ADDR_LEN] =
+ {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+ static char ax25_test[AX25_ADDR_LEN] =
+ {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+ if (ax == NULL) /* Allocation failed ?? */
+ return -ENODEV;
+
+ /* Set up the "AX25 Control Block". (And clear statistics) */
+ memset(ax, 0, sizeof (struct ax_disp));
+ ax->magic = AX25_MAGIC;
+ ax->dev = dev;
+
+ /* Finish setting up the DEVICE info. */
+ dev->mtu = AX_MTU;
+ dev->hard_start_xmit = ax_xmit;
+ dev->open = ax_open_dev;
+ dev->stop = ax_close;
+ dev->get_stats = ax_get_stats;
+#ifdef HAVE_SET_MAC_ADDR
+ dev->set_mac_address = ax_set_dev_mac_address;
+#endif
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->type = ARPHRD_AX25;
+ dev->tx_queue_len = 10;
+
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ dev->hard_header = ax_header;
+ dev->rebuild_header = ax_rebuild_header;
+#endif
+
+ dev_init_buffers(dev);
+
+ /* New-style flags. */
+ dev->flags = 0;
+
+ return 0;
+}
+
+static int mkiss_open(struct tty_struct *tty, struct file *filp)
+{
+ struct mkiss_channel *mkiss;
+ int chan;
+
+ chan = MINOR(tty->device) - tty->driver.minor_start;
+
+ if (chan < 0 || chan >= NR_MKISS)
+ return -ENODEV;
+
+ mkiss = &MKISS_Info[chan];
+
+ mkiss->magic = MKISS_DRIVER_MAGIC;
+ mkiss->init = 1;
+ mkiss->tty = tty;
+
+ tty->driver_data = mkiss;
+
+ tty->termios->c_iflag = IGNBRK | IGNPAR;
+ tty->termios->c_cflag = B9600 | CS8 | CLOCAL;
+ tty->termios->c_cflag &= ~CBAUD;
+
+ return 0;
+}
+
+static void mkiss_close(struct tty_struct *tty, struct file * filp)
+{
+ struct mkiss_channel *mkiss = tty->driver_data;
+
+ if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC)
+ return;
+
+ mkiss->tty = NULL;
+ mkiss->init = 0;
+ tty->stopped = 0;
+}
+
+static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+ return 0;
+}
+
+static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ /* Ignore serial ioctl's */
+ switch (cmd) {
+ case TCSBRK:
+ case TIOCMGET:
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ case TCSETS:
+ case TCSETSF: /* should flush first, but... */
+ case TCSETSW: /* should wait until flush, but... */
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+static void mkiss_dummy(struct tty_struct *tty)
+{
+ struct mkiss_channel *mkiss = tty->driver_data;
+
+ if (tty == NULL)
+ return;
+
+ if (mkiss == NULL)
+ return;
+}
+
+static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch)
+{
+ struct mkiss_channel *mkiss = tty->driver_data;
+
+ if (tty == NULL)
+ return;
+
+ if (mkiss == NULL)
+ return;
+}
+
+
+static int mkiss_write_room(struct tty_struct * tty)
+{
+ struct mkiss_channel *mkiss = tty->driver_data;
+
+ if (tty == NULL)
+ return 0;
+
+ if (mkiss == NULL)
+ return 0;
+
+ return 65536; /* We can handle an infinite amount of data. :-) */
+}
+
+
+static int mkiss_chars_in_buffer(struct tty_struct *tty)
+{
+ struct mkiss_channel *mkiss = tty->driver_data;
+
+ if (tty == NULL)
+ return 0;
+
+ if (mkiss == NULL)
+ return 0;
+
+ return 0;
+}
+
+
+static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ /* we don't do termios */
+}
+
+/* ******************************************************************** */
+/* * Init MKISS driver * */
+/* ******************************************************************** */
+
+__initfunc(static int mkiss_init(void))
+{
+ memset(&mkiss_driver, 0, sizeof(struct tty_driver));
+
+ mkiss_driver.magic = MKISS_DRIVER_MAGIC;
+ mkiss_driver.name = "mkiss";
+ mkiss_driver.major = MKISS_MAJOR;
+ mkiss_driver.minor_start = 0;
+ mkiss_driver.num = NR_MKISS;
+ mkiss_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ mkiss_driver.subtype = MKISS_SERIAL_TYPE_NORMAL; /* not needed */
+
+ mkiss_driver.init_termios = tty_std_termios;
+ mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
+ mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL;
+
+ mkiss_driver.flags = TTY_DRIVER_REAL_RAW;
+ mkiss_driver.refcount = &mkiss_refcount;
+ mkiss_driver.table = mkiss_table;
+ mkiss_driver.termios = (struct termios **)mkiss_termios;
+ mkiss_driver.termios_locked = (struct termios **)mkiss_termios_locked;
+
+ mkiss_driver.ioctl = mkiss_ioctl;
+ mkiss_driver.open = mkiss_open;
+ mkiss_driver.close = mkiss_close;
+ mkiss_driver.write = mkiss_write;
+ mkiss_driver.write_room = mkiss_write_room;
+ mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer;
+ mkiss_driver.set_termios = mkiss_set_termios;
+
+ /* some unused functions */
+ mkiss_driver.flush_buffer = mkiss_dummy;
+ mkiss_driver.throttle = mkiss_dummy;
+ mkiss_driver.unthrottle = mkiss_dummy;
+ mkiss_driver.stop = mkiss_dummy;
+ mkiss_driver.start = mkiss_dummy;
+ mkiss_driver.hangup = mkiss_dummy;
+ mkiss_driver.flush_chars = mkiss_dummy;
+ mkiss_driver.put_char = mkiss_dummy2;
+
+ if (tty_register_driver(&mkiss_driver)) {
+ printk(KERN_ERR "mkiss: couldn't register Mkiss device\n");
+ return -EIO;
+ }
+
+ printk(KERN_INFO "AX.25 Multikiss device enabled\n");
+
+ return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_PARM(ax25_maxdev, "i");
+MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
+
+MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
+MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
+
+int init_module(void)
+{
+ return mkiss_init_ctrl_dev();
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+ if (ax25_ctrls != NULL) {
+ for (i = 0; i < ax25_maxdev; i++) {
+ if (ax25_ctrls[i]) {
+ /*
+ * VSV = if dev->start==0, then device
+ * unregistred while close proc.
+ */
+ if (ax25_ctrls[i]->dev.start)
+ unregister_netdev(&(ax25_ctrls[i]->dev));
+
+ kfree(ax25_ctrls[i]);
+ ax25_ctrls[i] = NULL;
+ }
+ }
+
+ kfree(ax25_ctrls);
+ ax25_ctrls = NULL;
+ }
+
+ if ((i = tty_register_ldisc(N_AX25, NULL)))
+ printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
+
+ if (tty_unregister_driver(&mkiss_driver)) /* remove devive */
+ printk(KERN_ERR "mkiss: can't unregister MKISS device\n");
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h
new file mode 100644
index 000000000..0e32aa82b
--- /dev/null
+++ b/drivers/net/hamradio/mkiss.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+ * Defines for the Multi-KISS driver.
+ ****************************************************************************/
+
+#define AX25_MAXDEV 16 /* MAX number of AX25 channels;
+ This can be overridden with
+ insmod -oax25_maxdev=nnn */
+#define AX_MTU 236
+
+/* SLIP/KISS protocol characters. */
+#define END 0300 /* indicates end of frame */
+#define ESC 0333 /* indicates byte stuffing */
+#define ESC_END 0334 /* ESC ESC_END means END 'data' */
+#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
+
+struct ax_disp {
+ int magic;
+
+ /* Various fields. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct device *dev; /* easy for intr handling */
+ struct ax_disp *mkiss; /* mkiss txport if mkiss channel*/
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char *rbuff; /* receiver buffer */
+ int rcount; /* received chars counter */
+ unsigned char *xbuff; /* transmitter buffer */
+ unsigned char *xhead; /* pointer to next byte to XMIT */
+ int xleft; /* bytes left in XMIT queue */
+
+ /* SLIP interface statistics. */
+ unsigned long rx_packets; /* inbound frames counter */
+ unsigned long tx_packets; /* outbound frames counter */
+ unsigned long rx_errors; /* Parity, etc. errors */
+ unsigned long tx_errors; /* Planned stuff */
+ unsigned long rx_dropped; /* No memory for skb */
+ unsigned long tx_dropped; /* When MTU change */
+ unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */
+
+ /* Detailed SLIP statistics. */
+ int mtu; /* Our mtu (to spot changes!) */
+ int buffsize; /* Max buffers sizes */
+
+
+ unsigned char flags; /* Flag values/ mode etc */
+#define AXF_INUSE 0 /* Channel in use */
+#define AXF_ESCAPE 1 /* ESC received */
+#define AXF_ERROR 2 /* Parity, etc. error */
+#define AXF_KEEPTEST 3 /* Keepalive test flag */
+#define AXF_OUTWAIT 4 /* is outpacket was flag */
+
+ int mode;
+};
+
+#define AX25_MAGIC 0x5316
+#define MKISS_DRIVER_MAGIC 1215
diff --git a/drivers/net/hamradio/pi2.c b/drivers/net/hamradio/pi2.c
new file mode 100644
index 000000000..3eb3a9223
--- /dev/null
+++ b/drivers/net/hamradio/pi2.c
@@ -0,0 +1,1677 @@
+/*
+ pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface.
+ Copyright (c) 1994 David Perry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2, as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The file skeleton.c by Donald Becker was used as a starting point
+ for this driver.
+
+ Revision History
+
+ April 6, 1994 (dp) Created
+ version 0.0 ALPHA
+ April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison.
+ version 0.1 ALPHA
+ April 13, 1994 (dp) Included address probing from JPM, autoirq
+ version 0.2 ALPHA
+ April 14, 1994 (ac) Sketched in the NET3 changes.
+ April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev()
+ instead of kmalloc() to ensure that DMA buffers will
+ reside under the 16 meg line.
+ version 0.4 ALPHA
+ April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions.
+ Fixed a nasty problem with DMA.
+ version 0.5 ALPHA
+ June 6, 1994 (ac) Fixed to match the buffer locking changes. Added a hack to
+ fix a funny I see (search for HACK) and fixed the calls in
+ init() so it doesn't migrate module based ethernet cards up
+ to eth2 Took out the old module ideas as they are no longer
+ relevant to the PI driver.
+ July 16, 1994 (dp) Fixed the B channel rx overrun problem ac referred to
+ above. Also added a bit of a hack to improve the maximum
+ baud rate on the B channel (Search for STUFF2). Included
+ ioctl stuff from John Paul Morrison. version 0.6 ALPHA
+ Feb 9, 1995 (dp) Updated for 1.1.90 kernel
+ version 0.7 ALPHA
+ Apr 6, 1995 (ac) Tweaks for NET3 pre snapshot 002 AX.25
+ April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program
+ when changing the baud rate or clock mode.
+ version 0.8 ALPHA
+ July 17, 1995 (ac) Finally polishing of AX25.030+ support
+ Oct 29, 1995 (ac) A couple of minor fixes before this, and this release changes
+ to the proper set_mac_address semantics which will break
+ a few programs I suspect.
+ Aug 18, 1996 (jsn) Converted to be used as a module.
+ Dec 13, 1996 (jsn) Fixed to match Linux networking changes.
+*/
+
+/* The following #define invokes a hack that will improve performance (baud)
+ for the B port. The up side is it makes 9600 baud work ok on the B port.
+ It may do 38400, depending on the host. The down side is it burns up
+ CPU cycles with ints locked for up to 1 character time, at the beginning
+ of each transmitted packet. If this causes you to lose sleep, #undefine it.
+*/
+
+/*#define STUFF2 1*/
+
+/* The default configuration */
+#define PI_DMA 3
+
+#define DEF_A_SPEED 0 /* 0 means external clock */
+#define DEF_A_TXDELAY 15 /* 15 mS transmit delay */
+#define DEF_A_PERSIST 128 /* 50% persistence */
+#define DEF_A_SLOTIME 15 /* 15 mS slot time */
+#define DEF_A_SQUELDELAY 1 /* 1 mS squelch delay - allows fcs and flag */
+#define DEF_A_CLOCKMODE 0 /* clock mode - 0 is normal */
+
+#define DEF_B_SPEED 1200 /* 1200 baud */
+#define DEF_B_TXDELAY 40 /* 400 mS */
+#define DEF_B_PERSIST 128 /* 50% */
+#define DEF_B_SLOTIME 30 /* 300 mS */
+#define DEF_B_SQUELDELAY 3 /* 30 mS */
+#define DEF_B_CLOCKMODE 0 /* Normal clock mode */
+
+/* The following #define is only really required for the PI card, not
+ the PI2 - but it's safer to leave it in. */
+#define REALLY_SLOW_IO 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/if_arp.h>
+#include <linux/pi2.h>
+#include <linux/init.h>
+#include "z8530.h"
+#include <net/ax25.h>
+
+struct mbuf {
+ struct mbuf *next;
+ int cnt;
+ char data[0];
+};
+
+/*
+ * The actual devices we will use
+ */
+
+/*
+ * PI device declarations.
+ */
+
+static int pi0_preprobe(struct device *dev){return 0;} /* Dummy probe function */
+static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+
+
+/* The number of low I/O ports used by the card. */
+#define PI_TOTAL_SIZE 8
+
+
+/* Index to functions, as function prototypes. */
+
+static int pi_probe(struct device *dev, int card_type);
+static int pi_open(struct device *dev);
+static int pi_send_packet(struct sk_buff *skb, struct device *dev);
+static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs);
+static int pi_close(struct device *dev);
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct net_device_stats *pi_get_stats(struct device *dev);
+static void rts(struct pi_local *lp, int x);
+static void b_rxint(struct device *dev, struct pi_local *lp);
+static void b_txint(struct pi_local *lp);
+static void b_exint(struct pi_local *lp);
+static void a_rxint(struct device *dev, struct pi_local *lp);
+static void a_txint(struct pi_local *lp);
+static void a_exint(struct pi_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int ext2_secrm_seed = 152; /* Random generator base */
+
+extern inline unsigned char random(void)
+{
+ return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed
+ * 69069l + 1);
+}
+
+extern inline void wrtscc(int cbase, int ctl, int sccreg, int val)
+{
+ /* assume caller disables interrupts! */
+ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */
+ outb_p(sccreg, ctl); /* Select register */
+ outb_p(val, ctl); /* Output value */
+ outb_p(1, cbase + DMAEN); /* Enable DMA */
+}
+
+extern inline int rdscc(int cbase, int ctl, int sccreg)
+{
+ int retval;
+
+ /* assume caller disables interrupts! */
+ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */
+ outb_p(sccreg, ctl); /* Select register */
+ retval = inb_p(ctl);
+ outb_p(1, cbase + DMAEN); /* Enable DMA */
+ return retval;
+}
+
+static void switchbuffers(struct pi_local *lp)
+{
+ if (lp->rcvbuf == lp->rxdmabuf1)
+ lp->rcvbuf = lp->rxdmabuf2;
+ else
+ lp->rcvbuf = lp->rxdmabuf1;
+}
+
+static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb)
+{
+ char kickflag;
+ unsigned long flags;
+
+ lp->stats.tx_packets++;
+
+ save_flags(flags);
+ cli();
+ kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+ restore_flags(flags);
+
+ skb_queue_tail(&lp->sndq, skb);
+ if (kickflag)
+ {
+ /* simulate interrupt to xmit */
+ switch (lp->base & 2)
+ {
+ case 2:
+ a_txint(lp); /* process interrupt */
+ break;
+ case 0:
+ save_flags(flags);
+ cli();
+ if (lp->tstate == IDLE)
+ b_txint(lp);
+ restore_flags(flags);
+ break;
+ }
+ }
+}
+
+static void setup_rx_dma(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned long dma_abs;
+ unsigned dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dma_abs = (unsigned long) (lp->rcvbuf->data);
+ dmachan = lp->dmachan;
+ cmd = lp->base + CTL;
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PI: RX buffer violates DMA boundary!");
+
+ /* Get ready for RX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ disable_dma(dmachan);
+ clear_dma_ff(dmachan);
+
+ /* Set DMA mode register to single transfers, incrementing address,
+ * auto init, writes
+ */
+ set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+ set_dma_addr(dmachan, dma_abs);
+ set_dma_count(dmachan, lp->bufsiz);
+ enable_dma(dmachan);
+
+ /* If a packet is already coming in, this line is supposed to
+ avoid receiving a partial packet.
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+ /* Enable RX dma */
+ wrtscc(lp->cardbase, cmd, R1,
+ WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pi_local *lp, int length)
+{
+ unsigned long dma_abs;
+ unsigned long flags;
+ unsigned long dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dmachan = lp->dmachan;
+ dma_abs = (unsigned long) (lp->txdmabuf);
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PI: TX buffer violates DMA boundary!");
+
+ disable_dma(dmachan);
+ /* Set DMA mode register to single transfers, incrementing address,
+ * no auto init, reads
+ */
+ set_dma_mode(dmachan, DMA_MODE_WRITE);
+ clear_dma_ff(dmachan);
+ set_dma_addr(dmachan, dma_abs);
+ /* output byte count */
+ set_dma_count(dmachan, length);
+
+ restore_flags(flags);
+}
+
+static void tdelay(struct pi_local *lp, int time)
+{
+ int port;
+ unsigned int t1;
+ unsigned char sc;
+
+ if (lp->base & 2) { /* If A channel */
+ sc = SC1;
+ t1 = time;
+ port = lp->cardbase + TMR1;
+ } else {
+ sc = SC2;
+ t1 = 10 * time; /* 10s of milliseconds for the B channel */
+ port = lp->cardbase + TMR2;
+ wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+ }
+
+ /* Setup timer sc */
+ outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD);
+
+ /* times 2 to make millisecs */
+ outb_p((t1 << 1) & 0xFF, port);
+ outb_p((t1 >> 7) & 0xFF, port);
+
+ /* Enable correct int for timeout */
+ wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE);
+ wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);
+}
+
+static void a_txint(struct pi_local *lp)
+{
+ int cmd;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ cmd = CTL + lp->base;
+
+ switch (lp->tstate) {
+ case IDLE:
+ /* Transmitter idle. Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ rts(lp, OFF);
+ restore_flags(flags);
+ return;
+ }
+ /* If a buffer to send, we drop thru here */
+ case DEFER:
+ /* we may have deferred prev xmit attempt */
+ /* Check DCD - debounce it
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ /* Assert RTS early minimize collision window */
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+ default:
+ break;
+ } /* end switch(lp->state) */
+
+ restore_flags(flags);
+} /*a_txint */
+
+static void a_exint(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ char st;
+ int length;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+
+ st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */
+
+ /* reset external status latch */
+ wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT);
+ cmd = lp->base + CTL;
+
+ if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) {
+ setup_rx_dma(lp);
+ lp->rstate = ACTIVE;
+ }
+ switch (lp->tstate) {
+ case ACTIVE:
+ kfree_skb(lp->sndbuf, FREE_WRITE);
+ lp->sndbuf = NULL;
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ break;
+ case FLAGOUT:
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode */
+ lp->tstate = IDLE;
+ rts(lp, OFF);
+ restore_flags(flags);
+ return;
+ }
+ /* NOTE - fall through if more to send */
+ case ST_TXDELAY:
+ /* Disable DMA chan */
+ disable_dma(lp->dmachan);
+
+ /* Set up for TX dma */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+
+
+ /* Get all chars */
+ /* Strip KISS control byte */
+ length = lp->sndbuf->len - 1;
+ memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+
+
+ /* Setup DMA controller for tx */
+ setup_tx_dma(lp, length);
+
+ /* select transmit interrupts to enable */
+ /* Allow DMA on chan */
+ enable_dma(lp->dmachan);
+
+ /* reset CRC, Txint pend*/
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+
+ /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+ /* Enable TX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+
+ /* Send CRC on underrun */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+
+ /* packet going out now */
+ lp->tstate = ACTIVE;
+ break;
+ case DEFER:
+ /* we have deferred prev xmit attempt
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* Defer until dcd transition or 100mS timeout */
+ wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ /* Assert RTS early minimize collision window */
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+ } /* switch(lp->tstate) */
+
+ restore_flags(flags);
+} /* a_exint() */
+
+/* Receive interrupt handler for the A channel
+ */
+static void a_rxint(struct device *dev, struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ int bytecount;
+ char rse;
+ struct sk_buff *skb;
+ int sksize, pkt_len;
+ struct mbuf *cur_buf;
+ unsigned char *cfix;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ cmd = lp->base + CTL;
+
+ rse = rdscc(lp->cardbase, cmd, R1); /* Get special condition bits from R1 */
+ if (rse & Rx_OVR)
+ lp->rstate = RXERROR;
+
+ if (rse & END_FR) {
+ /* If end of frame */
+ /* figure length of frame from 8237 */
+ clear_dma_ff(lp->dmachan);
+ bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+
+ if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) {
+ if ((bytecount >= 10) && (rse & CRC_ERR)) {
+ lp->stats.rx_crc_errors++;
+ }
+ if (lp->rstate == RXERROR) {
+ lp->stats.rx_errors++;
+ lp->stats.rx_over_errors++;
+ }
+ /* Reset buffer pointers */
+ lp->rstate = ACTIVE;
+ setup_rx_dma(lp);
+ } else {
+ /* Here we have a valid frame */
+ /* Toss 2 crc bytes , add one for KISS */
+ pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1;
+
+ /* Get buffer for next frame */
+ cur_buf = lp->rcvbuf;
+ switchbuffers(lp);
+ setup_rx_dma(lp);
+
+
+ /* Malloc up new buffer. */
+ sksize = pkt_len;
+
+ skb = dev_alloc_skb(sksize);
+ if (skb == NULL) {
+ printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ restore_flags(flags);
+ return;
+ }
+ skb->dev = dev;
+
+ /* KISS kludge - prefix with a 0 byte */
+ cfix=skb_put(skb,pkt_len);
+ *cfix++=0;
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(cfix, (char *) cur_buf->data,
+ pkt_len - 1);
+ skb->protocol=htons(ETH_P_AX25);
+ skb->mac.raw=skb->data;
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ } /* end good frame */
+ } /* end EOF check */
+ wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES); /* error reset */
+ restore_flags(flags);
+}
+
+static void b_rxint(struct device *dev, struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ char rse;
+ struct sk_buff *skb;
+ int sksize;
+ int pkt_len;
+ unsigned char *cfix;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ cmd = CTL + lp->base;
+
+ rse = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */
+
+ if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) {
+ /* there is a char to be stored
+ * read special condition bits before reading the data char
+ */
+ if (rse & Rx_OVR) {
+ /* Rx overrun - toss buffer */
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ lp->rstate = RXERROR; /* set error flag */
+ lp->stats.rx_errors++;
+ lp->stats.rx_over_errors++;
+ } else if (lp->rcvbuf->cnt >= lp->bufsiz) {
+ /* Too large -- toss buffer */
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ lp->rstate = TOOBIG;/* when set, chars are not stored */
+ }
+ /* ok, we can store the received character now */
+ if (lp->rstate == ACTIVE) { /* If no errors... */
+ *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); /* char to rcv buff */
+ lp->rcvbuf->cnt++; /* bump count */
+ } else {
+ /* got to empty FIFO */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */
+ lp->rstate = ACTIVE;
+ }
+ }
+ if (rse & END_FR) {
+ /* END OF FRAME -- Make sure Rx was active */
+ if (lp->rcvbuf->cnt > 0) {
+ if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) {
+ if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) {
+ lp->stats.rx_crc_errors++;
+ }
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ } else {
+ /* Here we have a valid frame */
+ pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */
+ pkt_len += 1; /* Make room for KISS control byte */
+
+ /* Malloc up new buffer. */
+ sksize = pkt_len;
+ skb = dev_alloc_skb(sksize);
+ if (skb == NULL) {
+ printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ restore_flags(flags);
+ return;
+ }
+ skb->dev = dev;
+
+ /* KISS kludge - prefix with a 0 byte */
+ cfix=skb_put(skb,pkt_len);
+ *cfix++=0;
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
+ skb->protocol=ntohs(ETH_P_AX25);
+ skb->mac.raw=skb->data;
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ /* packet queued - initialize buffer for next frame */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ } /* end good frame queued */
+ } /* end check for active receive upon EOF */
+ lp->rstate = ACTIVE; /* and clear error status */
+ } /* end EOF check */
+ restore_flags(flags);
+}
+
+
+static void b_txint(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned char c;
+
+ save_flags(flags);
+ cli();
+ cmd = CTL + lp->base;
+
+ switch (lp->tstate) {
+ case CRCOUT:
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ case IDLE:
+ /* Transmitter idle. Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode
+ * Tx OFF now - flag should have gone
+ */
+ rts(lp, OFF);
+
+ restore_flags(flags);
+ return;
+ }
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ /* If a buffer to send, we drop thru here */
+ case DEFER: /* we may have deferred prev xmit attempt */
+ /* Check DCD - debounce it */
+ /* See Intel Microcommunications Handbook, p2-308 */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ case ACTIVE:
+ /* Here we are actively sending a frame */
+ if (lp->txcnt--) {
+ c = *lp->txptr++;
+ /* next char is gone */
+ wrtscc(lp->cardbase, cmd, R8, c);
+ /* stuffing a char satisfies Interrupt condition */
+ } else {
+ /* No more to send */
+ kfree_skb(lp->sndbuf, FREE_WRITE);
+ lp->sndbuf = NULL;
+ if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) {
+ /* Did we underrun? */
+ /* unexpected underrun */
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ }
+ lp->tstate = UNDERRUN; /* Now we expect to underrun */
+ /* Send flags on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS);
+ }
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); /* reset Tx Int Pend */
+ }
+ restore_flags(flags);
+ return; /* back to wait for interrupt */
+ } /* end switch */
+ restore_flags(flags);
+}
+
+/* Pi SIO External/Status interrupts (for the B channel)
+ * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
+ * Receiver automatically goes to Hunt on an abort.
+ *
+ * If the Tx Underrun interrupt hits, change state and
+ * issue a reset command for it, and return.
+ */
+static void b_exint(struct pi_local *lp)
+{
+ unsigned long flags;
+ char st;
+ int cmd;
+ char c;
+
+ cmd = CTL + lp->base;
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ st = rdscc(lp->cardbase, cmd, R0); /* Fetch status */
+ /* reset external status latch */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+
+ switch (lp->tstate) {
+ case ACTIVE: /* Unexpected underrun */
+ kfree_skb(lp->sndbuf, FREE_WRITE);
+ lp->sndbuf = NULL;
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->tstate = FLAGOUT;
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ case UNDERRUN:
+ lp->tstate = CRCOUT;
+ restore_flags(flags);
+ return;
+ case FLAGOUT:
+ /* Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode
+ * Tx OFF now - flag should have gone
+ */
+ rts(lp, OFF);
+ lp->tstate = IDLE;
+ restore_flags(flags);
+ return;
+ }
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ /* Get first char to send */
+ lp->txcnt--;
+ c = *lp->txptr++;
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */
+
+ /* Send abort on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+ }
+
+ wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */
+
+#ifdef STUFF2
+ /* stuff an extra one if we can */
+ if (lp->txcnt) {
+ lp->txcnt--;
+ c = *lp->txptr++;
+ /* Wait for tx buffer empty */
+ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+ ;
+ wrtscc(lp->cardbase, cmd, R8, c);
+ }
+#endif
+
+ /* select transmit interrupts to enable */
+
+ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); /* Tx/Ext ints */
+
+ lp->tstate = ACTIVE; /* char going out now */
+ restore_flags(flags);
+ return;
+
+ case DEFER:
+ /* Check DCD - debounce it
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ case ST_TXDELAY:
+
+ /* Get first char to send */
+ lp->txcnt--;
+ c = *lp->txptr++;
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */
+
+ /* Send abort on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+ }
+
+ wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */
+
+#ifdef STUFF2
+ /* stuff an extra one if we can */
+ if (lp->txcnt) {
+ lp->txcnt--;
+ c = *lp->txptr++;
+ /* Wait for tx buffer empty */
+ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+ ;
+ wrtscc(lp->cardbase, cmd, R8, c);
+ }
+#endif
+
+ /* select transmit interrupts to enable */
+
+ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ /* Tx/Extern ints on */
+ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+
+ lp->tstate = ACTIVE; /* char going out now */
+ restore_flags(flags);
+ return;
+ }
+
+ /* Receive Mode only
+ * This triggers when hunt mode is entered, & since an ABORT
+ * automatically enters hunt mode, we use that to clean up
+ * any waiting garbage
+ */
+ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) {
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0; /* rewind on DCD transition */
+ }
+ restore_flags(flags);
+}
+
+/* Probe for a PI card. */
+/* This routine also initializes the timer chip */
+
+__initfunc(static int hw_probe(int ioaddr))
+{
+ int time = 1000; /* Number of milliseconds for test */
+ unsigned long start_time, end_time;
+
+ int base, tmr0, tmr1, tmrcmd;
+ int a = 1;
+ int b = 1;
+
+ base = ioaddr & 0x3f0;
+ tmr0 = TMR0 + base;
+ tmr1 = TMR1 + base;
+ tmrcmd = TMRCMD + base;
+
+ /* Set up counter chip timer 0 for 500 uS period square wave */
+ /* assuming a 3.68 mhz clock for now */
+ outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+ outb_p(922 & 0xFF, tmr0);
+ outb_p(922 >> 8, tmr0);
+
+ /* Setup timer control word for timer 1*/
+ outb_p(SC1 | LSB_MSB | MODE0, tmrcmd);
+ outb_p((time << 1) & 0xFF, tmr1);
+ outb_p((time >> 7) & 0XFF, tmr1);
+
+ /* wait until counter reg is loaded */
+ do {
+ /* Latch count for reading */
+ outb_p(SC1, tmrcmd);
+ a = inb_p(tmr1);
+ b = inb_p(tmr1);
+ } while (b == 0);
+ start_time = jiffies;
+ while (b != 0) {
+ /* Latch count for reading */
+ outb_p(SC1, tmrcmd);
+ a = inb_p(tmr1);
+ b = inb_p(tmr1);
+ end_time = jiffies;
+ /* Don't wait forever - there may be no card here */
+ if ((end_time - start_time) > 200)
+ return 0; /* No card found */
+ }
+ end_time = jiffies;
+ /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */
+ if ((end_time - start_time) > 65) {
+ return (1); /* PI card found */
+ } else {
+ /* Faster crystal - tmr0 needs adjusting */
+ /* Set up counter chip */
+ /* 500 uS square wave */
+ outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+ outb_p(1844 & 0xFF, tmr0);
+ outb_p(1844 >> 8, tmr0);
+ return (2); /* PI2 card found */
+ }
+}
+
+static void rts(struct pi_local *lp, int x)
+{
+ int tc;
+ long br;
+ int cmd;
+ int dummy;
+
+ /* assumes interrupts are off */
+ cmd = CTL + lp->base;
+
+ /* Reprogram BRG and turn on transmitter to send flags */
+ if (x == ON) { /* Turn Tx ON and Receive OFF */
+ /* Exints off first to avoid abort int */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* Rx off */
+ lp->rstate = IDLE;
+ if (cmd & 2) { /* if channel a */
+ /* Set up for TX dma */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+ } else {
+ wrtscc(lp->cardbase, cmd, R1, 0); /* No interrupts */
+ }
+
+ if (!lp->clockmode) {
+ if (lp->speed) { /* if internally clocked */
+ br = lp->speed; /* get desired speed */
+ tc = (lp->xtal / br) - 2; /* calc 1X BRG divisor */
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+ }
+ }
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+ /* Transmitter now on */
+ } else { /* Tx OFF and Rx ON */
+ lp->tstate = IDLE;
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); /* TX off */
+
+ if (!lp->clockmode) {
+ if (lp->speed) { /* if internally clocked */
+ /* Reprogram BRG for 32x clock for receive DPLL */
+ /* BRG off, keep Pclk source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC);
+ br = lp->speed; /* get desired speed */
+ /* calc 32X BRG divisor */
+ tc = ((lp->xtal / 32) / br) - 2;
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+ /* SEARCH mode, BRG source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+ /* Enable the BRG */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+ }
+ }
+ /* Flush rx fifo */
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* Make sure rx is off */
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */
+ dummy = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ /* Now, turn on the receiver and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8);
+ lp->rstate = ACTIVE; /* Normal state */
+
+ if (cmd & 2) { /* if channel a */
+ setup_rx_dma(lp);
+ } else {
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+ }
+ wrtscc(lp->cardbase, cmd, R15, BRKIE); /* allow ABORT int */
+ }
+}
+
+static void scc_init(struct device *dev)
+{
+ unsigned long flags;
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ int tc;
+ long br;
+ register int cmd;
+
+ /* Initialize 8530 channel for SDLC operation */
+
+ cmd = CTL + lp->base;
+ save_flags(flags);
+ cli();
+
+ switch (cmd & CHANA) {
+ case CHANA:
+ wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */
+ wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialize interrupt vector */
+ break;
+ default:
+ wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */
+ break;
+ }
+
+ /* Deselect all Rx and Tx interrupts */
+ wrtscc(lp->cardbase, cmd, R1, 0);
+
+ /* Turn off external interrupts (like CTS/CD) */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+
+ /* X1 clock, SDLC mode */
+ wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+
+ /* Tx/Rx parameters */
+ if (lp->speed) { /* Use internal clocking */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ if (!lp->clockmode)
+ /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */
+ wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+ else
+ /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */
+ wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI);
+ } else { /* Use external clocking */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS);
+ /* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */
+ wrtscc(lp->cardbase, cmd, R11, TCTRxCP);
+ }
+
+ /* Null out SDLC start address */
+ wrtscc(lp->cardbase, cmd, R6, 0);
+
+ /* SDLC flag */
+ wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+ /* Set up the Transmitter but don't enable it
+ * DTR, 8 bit TX chars only
+ */
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+ /* Receiver initial setup */
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* 8 bits/char */
+
+ /* Setting up BRG now - turn it off first */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC); /* BRG off, keep Pclk source */
+
+ /* set the 32x time constant for the BRG in Receive mode */
+
+ if (lp->speed) {
+ br = lp->speed; /* get desired speed */
+ tc = ((lp->xtal / 32) / br) - 2; /* calc 32X BRG divisor */
+ } else {
+ tc = 14;
+ }
+
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+
+ /* Following subroutine sets up and ENABLES the receiver */
+ rts(lp, OFF); /* TX OFF and RX ON */
+
+ if (lp->speed) {
+ /* DPLL frm BRG, BRG src PCLK */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+ } else {
+ /* DPLL frm rtxc,BRG src PCLK */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);
+ }
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */
+
+ if (!(cmd & 2)) /* if channel b */
+ wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+ wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */
+
+ /* Now, turn on the receiver and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8);
+
+ restore_flags(flags);
+}
+
+static void chipset_init(struct device *dev)
+{
+ int cardbase;
+ unsigned long flags;
+
+ cardbase = dev->base_addr & 0x3f0;
+
+ save_flags(flags);
+ cli();
+ wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */
+ /* Disable interrupts with master interrupt ctrl reg */
+ wrtscc(cardbase, dev->base_addr + CTL, R9, 0);
+ restore_flags(flags);
+
+}
+
+
+__initfunc(int pi_init(void))
+{
+ int *port;
+ int ioaddr = 0;
+ int card_type = 0;
+ int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
+
+ printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n");
+
+ /* Only one card supported for now */
+ for (port = &ports[0]; *port && !card_type; port++) {
+ ioaddr = *port;
+
+ if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
+ printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr);
+ card_type = hw_probe(ioaddr);
+ }
+ }
+
+ switch (card_type) {
+ case 1:
+ printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr);
+ break;
+ case 2:
+ printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr);
+ break;
+ default:
+ printk(KERN_ERR "PI: ERROR: No card found\n");
+ return -EIO;
+ }
+
+ /* Link a couple of device structures into the chain */
+ /* For the A port */
+ /* Allocate space for 4 buffers even though we only need 3,
+ because one of them may cross a DMA page boundary and
+ be rejected by get_dma_buffer().
+ */
+ register_netdev(&pi0a);
+
+ pi0a.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+ pi0a.dma = PI_DMA;
+ pi0a.base_addr = ioaddr + 2;
+ pi0a.irq = 0;
+
+ /* And the B port */
+ register_netdev(&pi0b);
+ pi0b.base_addr = ioaddr;
+ pi0b.irq = 0;
+
+ pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+ /* Now initialize them */
+ pi_probe(&pi0a, card_type);
+ pi_probe(&pi0b, card_type);
+
+ pi0b.irq = pi0a.irq; /* IRQ is shared */
+
+ return 0;
+}
+
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize)
+{
+ if (((addr & 0xffff) + dev_buffsize) <= 0x10000)
+ return 1;
+ else
+ return 0;
+}
+
+static int pi_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); /* addr is an AX.25 shifted ASCII */
+ return 0; /* mac address */
+}
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char *
+get_dma_buffer(unsigned long *mem_ptr)
+{
+ char *ret;
+
+ ret = (char *)*mem_ptr;
+
+ if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ ret = (char *)*mem_ptr;
+ }
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ return (ret);
+}
+
+static int pi_probe(struct device *dev, int card_type)
+{
+ short ioaddr;
+ struct pi_local *lp;
+ unsigned long flags;
+ unsigned long mem_ptr;
+
+ ioaddr = dev->base_addr;
+
+ /* Initialize the device structure. */
+ /* Must be done before chipset_init */
+ /* Make certain the data structures used by the PI2 are aligned. */
+ dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+ lp = (struct pi_local *) dev->priv;
+
+ memset(dev->priv, 0, sizeof(struct pi_local));
+
+ /* Allocate some buffers which do not cross DMA page boundaries */
+ mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local);
+ lp->txdmabuf = get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+ /* Initialize rx buffer */
+ lp->rcvbuf = lp->rxdmabuf1;
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Initialize the transmit queue head structure */
+ skb_queue_head_init(&lp->sndq);
+
+ /* These need to be initialized before scc_init is called. */
+ if (card_type == 1)
+ lp->xtal = (unsigned long) SINGLE / 2;
+ else
+ lp->xtal = (unsigned long) DOUBLE / 2;
+ lp->base = dev->base_addr;
+ lp->cardbase = dev->base_addr & 0x3f0;
+ if (dev->base_addr & CHANA) {
+ lp->speed = DEF_A_SPEED;
+ /* default channel access Params */
+ lp->txdelay = DEF_A_TXDELAY;
+ lp->persist = DEF_A_PERSIST;
+ lp->slotime = DEF_A_SLOTIME;
+ lp->squeldelay = DEF_A_SQUELDELAY;
+ lp->clockmode = DEF_A_CLOCKMODE;
+
+ } else {
+ lp->speed = DEF_B_SPEED;
+ /* default channel access Params */
+ lp->txdelay = DEF_B_TXDELAY;
+ lp->persist = DEF_B_PERSIST;
+ lp->slotime = DEF_B_SLOTIME;
+ lp->squeldelay = DEF_B_SQUELDELAY;
+ lp->clockmode = DEF_B_CLOCKMODE;
+ }
+ lp->bufsiz = DMA_BUFF_SIZE;
+ lp->tstate = IDLE;
+
+ chipset_init(dev);
+
+ if (dev->base_addr & CHANA) { /* Do these things only for the A port */
+ /* Note that a single IRQ services 2 devices (A and B channels) */
+
+ lp->dmachan = dev->dma;
+ if (lp->dmachan < 1 || lp->dmachan > 3)
+ printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan);
+
+ /* chipset_init() was already called */
+
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB);
+ /* enable PI card interrupts */
+ wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+ restore_flags(flags);
+ /* request a timer interrupt for 1 mS hence */
+ tdelay(lp, 1);
+ /* 20 "jiffies" should be plenty of time... */
+ dev->irq = autoirq_report(20);
+ if (!dev->irq) {
+ printk(KERN_ERR "PI: Failed to detect IRQ line.\n");
+ }
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */
+ /* Disable interrupts with master interrupt ctrl reg */
+ wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0);
+ restore_flags(flags);
+ }
+
+ printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n",
+ dev->irq, dev->dma);
+
+ /* This board has jumpered interrupts. Snarf the interrupt vector
+ now. There is no point in waiting since no other device can use
+ the interrupt, and this marks the 'irqaction' as busy. */
+ {
+ int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev);
+ if (irqval) {
+ printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ /* Grab the region */
+ request_region(ioaddr & 0x3f0, PI_TOTAL_SIZE, "pi2" );
+
+
+ } /* Only for A port */
+ dev->open = pi_open;
+ dev->stop = pi_close;
+ dev->do_ioctl = pi_ioctl;
+ dev->hard_start_xmit = pi_send_packet;
+ dev->get_stats = pi_get_stats;
+
+ /* Fill in the fields of the device structure */
+
+ dev_init_buffers(dev);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+#endif
+
+ dev->set_mac_address = pi_set_mac_address;
+
+ dev->type = ARPHRD_AX25; /* AF_AX25 device */
+ dev->hard_header_len = 73; /* We do digipeaters now */
+ dev->mtu = 1500; /* eth_mtu is the default */
+ dev->addr_len = 7; /* sizeof an ax.25 address */
+ memcpy(dev->broadcast, ax25_bcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+
+ /* New-style flags. */
+ dev->flags = 0;
+ return 0;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int pi_open(struct device *dev)
+{
+ unsigned long flags;
+ static first_time = 1;
+
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ if (dev->base_addr & 2) { /* if A channel */
+ if (first_time) {
+ if (request_dma(dev->dma,"pi2")) {
+ free_irq(dev->irq, dev);
+ return -EAGAIN;
+ }
+ }
+ /* Reset the hardware here. */
+ chipset_init(dev);
+ }
+ lp->tstate = IDLE;
+
+ if (dev->base_addr & 2) { /* if A channel */
+ scc_init(dev); /* Called once for each channel */
+ scc_init(dev->next);
+ }
+ /* master interrupt enable */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+ restore_flags(flags);
+
+ lp->open_time = jiffies;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ first_time = 0;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int pi_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ hardware_send_packet(lp, skb);
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs)
+{
+/* int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/
+ struct pi_local *lp;
+ int st;
+ unsigned long flags;
+
+/* dev_b = dev_a->next; Relies on the order defined in Space.c */
+
+#if 0
+ if (dev_a == NULL) {
+ printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+#endif
+ /* Read interrupt status register (only valid from channel A)
+ * Process all pending interrupts in while loop
+ */
+ lp = (struct pi_local *) pi0a.priv; /* Assume channel A */
+ while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) {
+ if (st & CHBTxIP) {
+ /* Channel B Transmit Int Pending */
+ lp = (struct pi_local *) pi0b.priv;
+ b_txint(lp);
+ } else if (st & CHARxIP) {
+ /* Channel A Rcv Interrupt Pending */
+ lp = (struct pi_local *) pi0a.priv;
+ a_rxint(&pi0a, lp);
+ } else if (st & CHATxIP) {
+ /* Channel A Transmit Int Pending */
+ lp = (struct pi_local *) pi0a.priv;
+ a_txint(lp);
+ } else if (st & CHAEXT) {
+ /* Channel A External Status Int */
+ lp = (struct pi_local *) pi0a.priv;
+ a_exint(lp);
+ } else if (st & CHBRxIP) {
+ /* Channel B Rcv Interrupt Pending */
+ lp = (struct pi_local *) pi0b.priv;
+ b_rxint(&pi0b, lp);
+ } else if (st & CHBEXT) {
+ /* Channel B External Status Int */
+ lp = (struct pi_local *) pi0b.priv;
+ b_exint(lp);
+ }
+ /* Reset highest interrupt under service */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+ restore_flags(flags);
+ } /* End of while loop on int processing */
+ return;
+}
+
+/* The inverse routine to pi_open(). */
+static int pi_close(struct device *dev)
+{
+ unsigned long flags;
+ struct pi_local *lp;
+ struct sk_buff *ptr;
+
+ save_flags(flags);
+ cli();
+
+ lp = (struct pi_local *) dev->priv;
+ ptr = NULL;
+
+ chipset_init(dev); /* reset the scc */
+ disable_dma(lp->dmachan);
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Free any buffers left in the hardware transmit queue */
+ while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+ kfree_skb(ptr, FREE_WRITE);
+
+ restore_flags(flags);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ unsigned long flags;
+ struct pi_req rq;
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req));
+ if (ret)
+ return ret;
+
+ if(cmd!=SIOCDEVPRIVATE)
+ return -EINVAL;
+
+ copy_from_user(&rq, ifr->ifr_data, sizeof(struct pi_req));
+
+ switch (rq.cmd) {
+ case SIOCSPIPARAM:
+
+ if (!suser())
+ return -EPERM;
+ save_flags(flags);
+ cli();
+ lp->txdelay = rq.txdelay;
+ lp->persist = rq.persist;
+ lp->slotime = rq.slotime;
+ lp->squeldelay = rq.squeldelay;
+ lp->clockmode = rq.clockmode;
+ lp->speed = rq.speed;
+ pi_open(&pi0a); /* both channels get reset %%% */
+ restore_flags(flags);
+ ret = 0;
+ break;
+
+ case SIOCSPIDMA:
+
+ if (!suser())
+ return -EPERM;
+ ret = 0;
+ if (dev->base_addr & 2) { /* if A channel */
+ if (rq.dmachan < 1 || rq.dmachan > 3)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ pi_close(dev);
+ free_dma(lp->dmachan);
+ dev->dma = lp->dmachan = rq.dmachan;
+ if (request_dma(lp->dmachan,"pi2"))
+ ret = -EAGAIN;
+ pi_open(dev);
+ restore_flags(flags);
+ }
+ break;
+
+ case SIOCSPIIRQ:
+ ret = -EINVAL; /* add this later */
+ break;
+
+ case SIOCGPIPARAM:
+ case SIOCGPIDMA:
+ case SIOCGPIIRQ:
+
+ rq.speed = lp->speed;
+ rq.txdelay = lp->txdelay;
+ rq.persist = lp->persist;
+ rq.slotime = lp->slotime;
+ rq.squeldelay = lp->squeldelay;
+ rq.clockmode = lp->clockmode;
+ rq.dmachan = lp->dmachan;
+ rq.irq = dev->irq;
+ copy_to_user(ifr->ifr_data, &rq, sizeof(struct pi_req));
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct net_device_stats *pi_get_stats(struct device *dev)
+{
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ return &lp->stats;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("David Perry <dp@hydra.carleton.ca>");
+MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards");
+
+int init_module(void)
+{
+ return pi_init();
+}
+
+void cleanup_module(void)
+{
+ free_irq(pi0a.irq, &pi0a); /* IRQs and IO Ports are shared */
+ release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
+
+ kfree(pi0a.priv);
+ pi0a.priv = NULL;
+ unregister_netdev(&pi0a);
+
+ kfree(pi0b.priv);
+ pi0b.priv = NULL;
+ unregister_netdev(&pi0b);
+}
+#endif
diff --git a/drivers/net/hamradio/pt.c b/drivers/net/hamradio/pt.c
new file mode 100644
index 000000000..11bef1d6f
--- /dev/null
+++ b/drivers/net/hamradio/pt.c
@@ -0,0 +1,1778 @@
+#undef PT_DEBUG 1
+/*
+ * pt.c: Linux device driver for the Gracilis PackeTwin.
+ * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * This driver is largely based upon the PI driver by David Perry.
+ *
+ * Revision History
+ * 23/02/95 cs Started again on driver, last one scrapped
+ * 27/02/95 cs Program works, we have chan A only. Tx stays on
+ * 28/02/95 cs Fix Tx problem (& TxUIE instead of | )
+ * Fix Chan B Tx timer problem, used TMR2 instead of TMR1
+ * 03/03/95 cs Painfully found out (after 3 days) SERIAL_CFG is write only
+ * created image of it and DMA_CFG
+ * 21/06/95 cs Upgraded to suit PI driver 0.8 ALPHA
+ * 22/08/95 cs Changed it all around to make it like pi driver
+ * 23/08/95 cs It now works, got caught again by TMR2 and we must have
+ * auto-enables for daughter boards.
+ * 07/10/95 cs Fixed for 1.3.30 (hopefully)
+ * 26/11/95 cs Fixed for 1.3.43, ala 29/10 for pi2.c by ac
+ * 21/12/95 cs Got rid of those nasty warnings when compiling, for 1.3.48
+ * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and
+ * pt_loopback functions - they were unused.
+ * 13/12/96 jsn Fixed to match Linux networking changes.
+ */
+
+/*
+ * default configuration of the PackeTwin,
+ * ie What Craig uses his PT for.
+ */
+#define PT_DMA 3
+
+#define DEF_A_SPEED 4800 /* 4800 baud */
+#define DEF_A_TXDELAY 350 /* 350 mS */
+#define DEF_A_PERSIST 64 /* 25% persistence */
+#define DEF_A_SLOTIME 10 /* 10 mS */
+#define DEF_A_SQUELDELAY 30 /* 30 mS */
+#define DEF_A_CLOCKMODE 0 /* Normal clock mode */
+#define DEF_A_NRZI 1 /* NRZI mode */
+
+#define DEF_B_SPEED 0 /* 0 means external clock */
+#define DEF_B_TXDELAY 250 /* 250 mS */
+#define DEF_B_PERSIST 64 /* 25% */
+#define DEF_B_SLOTIME 10 /* 10 mS */
+#define DEF_B_SQUELDELAY 30 /* 30 mS */
+#define DEF_B_CLOCKMODE 0 /* Normal clock mode ?!? */
+#define DEF_B_NRZI 1 /* NRZI mode */
+
+
+#define PARAM_TXDELAY 1
+#define PARAM_PERSIST 2
+#define PARAM_SLOTTIME 3
+#define PARAM_FULLDUP 5
+#define PARAM_HARDWARE 6
+#define PARAM_RETURN 255
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/if_arp.h>
+#include <linux/pt.h>
+#include <linux/init.h>
+#include "z8530.h"
+#include <net/ax25.h>
+
+struct mbuf {
+ struct mbuf *next;
+ int cnt;
+ char data[0];
+};
+
+/*
+ * The actual PT devices we will use
+ */
+static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */
+static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+
+/* Ok, they shouldn't be here, but both channels share them */
+/* The Images of the Serial and DMA config registers */
+static unsigned char pt_sercfg = 0;
+static unsigned char pt_dmacfg = 0;
+
+/* The number of IO ports used by the card */
+#define PT_TOTAL_SIZE 16
+
+/* Index to functions, as function prototypes. */
+
+static int pt_probe(struct device *dev);
+static int pt_open(struct device *dev);
+static int pt_send_packet(struct sk_buff *skb, struct device *dev);
+static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int pt_close(struct device *dev);
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct net_device_stats *pt_get_stats(struct device *dev);
+static void pt_rts(struct pt_local *lp, int x);
+static void pt_rxisr(struct device *dev);
+static void pt_txisr(struct pt_local *lp);
+static void pt_exisr(struct pt_local *lp);
+static void pt_tmrisr(struct pt_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+static int hw_probe(int ioaddr);
+static void tdelay(struct pt_local *lp, int time);
+static void chipset_init(struct device *dev);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+
+
+static int ext2_secrm_seed = 152;
+
+static inline unsigned char random(void)
+{
+ return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1);
+}
+
+static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val)
+{
+ outb_p(sccreg, ctl); /* Select register */
+ outb_p(val, ctl); /* Output value */
+}
+
+static inline unsigned char rdscc(int cbase, int ctl, int sccreg)
+{
+ unsigned char retval;
+
+ outb_p(sccreg, ctl); /* Select register */
+ retval = inb_p(ctl);
+ return retval;
+}
+
+static void switchbuffers(struct pt_local *lp)
+{
+ if (lp->rcvbuf == lp->rxdmabuf1)
+ lp->rcvbuf = lp->rxdmabuf2;
+ else
+ lp->rcvbuf = lp->rxdmabuf1;
+}
+
+static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb)
+{
+ char kickflag;
+ unsigned long flags;
+ char *ptr;
+ struct device *dev;
+
+ /* First, let's see if this packet is actually a KISS packet */
+ ptr = skb->data;
+ if (ptr[0] != 0 && skb->len >= 2)
+ {
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#endif
+ /* Kludge to get device */
+ if ((struct pt_local*)(&pt0b.priv) == lp)
+ dev = &pt0b;
+ else
+ dev = &pt0a;
+ switch(ptr[0])
+ {
+
+ case PARAM_TXDELAY:
+ /*TxDelay is in 10mS increments */
+ lp->txdelay = ptr[1] * 10;
+ break;
+ case PARAM_PERSIST:
+ lp->persist = ptr[1];
+ break;
+ case PARAM_SLOTTIME:
+ lp->slotime = ptr[1];
+ break;
+ case PARAM_FULLDUP:
+ /* Yeah right, you wish! Fullduplex is a little while to
+ * go folks, but this is how you fire it up
+ */
+ break;
+ /* Perhaps we should have txtail here?? */
+ } /*switch */
+ return;
+ }
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes+=skb->len;
+ save_flags(flags);
+ cli();
+ kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+ restore_flags(flags);
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
+#endif
+ skb_queue_tail(&lp->sndq, skb);
+ if (kickflag)
+ {
+ /* Simulate interrupt to transmit */
+ if (lp->dmachan)
+ pt_txisr(lp);
+ else
+ {
+ save_flags(flags);
+ cli();
+ if (lp->tstate == IDLE)
+ pt_txisr(lp);
+ restore_flags(flags);
+ }
+ }
+} /* hardware_send_packet() */
+
+static void setup_rx_dma(struct pt_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned long dma_abs;
+ unsigned char dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dma_abs = (unsigned long) (lp->rcvbuf->data);
+ dmachan = lp->dmachan;
+ cmd = lp->base + CTL;
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PI: RX buffer violates DMA boundary!");
+
+ /* Get ready for RX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ disable_dma(dmachan);
+ clear_dma_ff(dmachan);
+
+ /*
+ * Set DMA mode register to single transfers, incrementing address,
+ * auto init, writes
+ */
+
+ set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+ set_dma_addr(dmachan, dma_abs);
+ set_dma_count(dmachan, lp->bufsiz);
+ enable_dma(dmachan);
+
+ /*
+ * If a packet is already coming in, this line is supposed to
+ * avoid receiving a partial packet.
+ */
+
+ wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+ /* Enable RX dma */
+ wrtscc(lp->cardbase, cmd, R1,
+ WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pt_local *lp, int length)
+{
+ unsigned long dma_abs;
+ unsigned long flags;
+ unsigned long dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dmachan = lp->dmachan;
+ dma_abs = (unsigned long) (lp->txdmabuf);
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PT: TX buffer violates DMA boundary!");
+
+ disable_dma(dmachan);
+ /* Set DMA mode register to single transfers, incrementing address,
+ * no auto init, reads
+ */
+ set_dma_mode(dmachan, DMA_MODE_WRITE);
+ clear_dma_ff(dmachan);
+ set_dma_addr(dmachan, dma_abs);
+ /* output byte count */
+ set_dma_count(dmachan, length);
+
+ restore_flags(flags);
+}
+
+/*
+ * This sets up all the registers in the SCC for the given channel
+ * based upon tsync_hwint()
+ */
+static void scc_init(struct device *dev)
+{
+ unsigned long flags;
+ struct pt_local *lp = (struct pt_local*) dev->priv;
+ register int cmd = lp->base + CTL;
+ int tc, br;
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA);
+#endif
+ save_flags(flags);
+ cli();
+
+ /* We may put something here to enable_escc */
+
+ if (cmd & CHANA)
+ {
+ wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */
+ wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialise interrupt vector */
+ }
+ else
+ wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */
+
+ /* Deselect all Rx and Tx interrupts */
+ wrtscc(lp->cardbase, cmd, R1, 0);
+
+ /* Turn off external interrupts (like CTS/CD) */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+
+ /* X1 clock, SDLC mode */
+ wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+
+ /* Preset CRC and set mode */
+ if (lp->nrzi)
+ /* Preset Tx CRC, put into NRZI mode */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ else
+ /* Preset Tx CRC, put into NRZ mode */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS);
+
+ /* Tx/Rx parameters */
+ if (lp->speed) /* Use internal clocking */
+ /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */
+ wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+ else /* Use external clocking */
+ {
+ /* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */
+ wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR);
+ wrtscc(lp->cardbase,cmd, R14, 0); /* wiz1 */
+ }
+
+ /* Null out SDLC start address */
+ wrtscc(lp->cardbase, cmd, R6, 0);
+
+ /* SDLC flag */
+ wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+ /* Setup Tx but don't enable it */
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+ /* Setup Rx */
+ wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+
+ /* Setup the BRG, turn it off first */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC);
+
+ /* set the 32x time constant for the BRG in Rx mode */
+ if (lp->speed)
+ {
+ br = lp->speed;
+ tc = ((lp->xtal / 32) / (br * 2)) - 2;
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xff); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); /* upper byte */
+ }
+
+ /* Turn transmitter off, to setup stuff */
+ pt_rts(lp, OFF);
+
+ /* External clocking */
+ if (lp->speed)
+ {
+ /* DPLL frm BRG, BRG src PCLK */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */
+
+ /* Turn off external clock port */
+ if (lp->base & CHANA)
+ outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+ else
+ outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
+ }
+ else
+ {
+ /* DPLL frm rtxc,BRG src PCLK */
+ /* Turn on external clock port */
+ if (lp->base & CHANA)
+ outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+ else
+ outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
+ }
+
+ if (!lp->dmachan)
+ wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+ wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */
+
+ /* Turn on the DTR to tell modem we're alive */
+ if (lp->base & CHANA)
+ outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) );
+ else
+ outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) );
+
+ /* Now, turn on the receiver and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 );
+
+ restore_flags(flags);
+
+} /* scc_init() */
+
+/* Resets the given channel and whole SCC if both channels off */
+static void chipset_init(struct device *dev)
+{
+
+ struct pt_local *lp = (struct pt_local*) dev->priv;
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
+ printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);
+#endif
+ /* Reset SCC if both channels are to be canned */
+ if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
+ (!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) )
+ {
+ wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES);
+ /* Reset int and dma registers */
+ outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
+ outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
+#endif
+ }
+ /* Reset individual channel */
+ if (lp->base & CHANA) {
+ wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA);
+ outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG);
+ } else {
+ wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB);
+ outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG);
+ }
+} /* chipset_init() */
+
+
+
+__initfunc(int pt_init(void))
+{
+ int *port;
+ int ioaddr = 0;
+ int card_type = 0;
+ int ports[] =
+ { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
+ 0x2b0, 0x300, 0x330, 0x3f0, 0};
+
+ printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (csmall@small.dropbear.id.au)\n");
+
+ for (port = &ports[0]; *port && !card_type; port++) {
+ ioaddr = *port;
+
+ if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
+ printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr);
+ card_type = hw_probe(ioaddr);
+ }
+ }
+ if (card_type) {
+ printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr);
+ } else {
+ printk(KERN_ERR "PT: ERROR: No card found.\n");
+ return -EIO;
+ }
+
+ /*
+ * Link a couple of device structures into the chain
+ *
+ * For the A port
+ * Allocate space for 4 buffers even though we only need 3,
+ * because one of them may cross a DMA page boundary and
+ * be rejected by get_dma_buffer().
+ */
+ register_netdev(&pt0a);
+
+ pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+ pt0a.dma = 0; /* wizzer - no dma yet */
+ pt0a.base_addr = ioaddr + CHANA;
+ pt0a.irq = 0;
+
+ /* And B port */
+ register_netdev(&pt0b);
+
+ pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+ pt0b.base_addr = ioaddr + CHANB;
+ pt0b.irq = 0;
+
+ /* Now initialise them */
+ pt_probe(&pt0a);
+ pt_probe(&pt0b);
+
+ pt0b.irq = pt0a.irq; /* IRQ is shared */
+
+ return 0;
+} /* pt_init() */
+
+/*
+ * Probe for PT card. Also initialises the timers
+ */
+__initfunc(static int hw_probe(int ioaddr))
+{
+ int time = 1000; /* Number of milliseconds to test */
+ int a = 1;
+ int b = 1;
+ unsigned long start_time, end_time;
+
+ inb_p(ioaddr + TMR1CLR);
+ inb_p(ioaddr + TMR2CLR);
+
+ /* Timer counter channel 0, 1mS period */
+ outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD);
+ outb_p(0x00, ioaddr + TMR0);
+ outb_p(0x18, ioaddr + TMR0);
+
+ /* Setup timer control word for timer 1 */
+ outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+ outb_p((time << 1) & 0xff, ioaddr + TMR1);
+ outb_p((time >> 7) & 0xff, ioaddr + TMR1);
+
+ /* wait until counter reg is loaded */
+ do {
+ /* Latch count for reading */
+ outb_p(SC1, ioaddr + TMRCMD);
+ a = inb_p(ioaddr + TMR1);
+ b = inb_p(ioaddr + TMR1);
+ } while (b == 0);
+ start_time = jiffies;
+ while(b != 0)
+ {
+ /* Latch count for reading */
+ outb_p(SC1, ioaddr + TMRCMD);
+ a = inb_p(ioaddr + TMR1);
+ b = inb_p(ioaddr + TMR1);
+ end_time = jiffies;
+ /* Don't wait forever - there may be no card here */
+ if ((end_time - start_time) > 200)
+ {
+ inb_p(ioaddr + TMR1CLR);
+ return 0;
+ }
+ }
+
+ /* Now fix the timers up for general operation */
+
+ /* Clear the timers */
+ inb_p(ioaddr + TMR1CLR);
+ inb_p(ioaddr + TMR2CLR);
+
+ outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+ inb_p(ioaddr + TMR1CLR);
+
+ outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+ /* Should this be tmr1 or tmr2? wiz3*/
+ inb_p(ioaddr + TMR1CLR);
+
+ return 1;
+} /* hw_probe() */
+
+
+static void pt_rts(struct pt_local *lp, int x)
+{
+ int tc;
+ long br;
+ int cmd = lp->base + CTL;
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
+#endif
+ if (x == ON) {
+ /* Ex ints off to avoid int */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+ wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); /* Rx off */
+ lp->rstate = IDLE;
+
+ if(lp->dmachan)
+ {
+ /* Setup for Tx DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+ } else {
+ /* No interrupts */
+ wrtscc(lp->cardbase, cmd, R1, 0);
+ }
+
+ if (!lp->clockmode)
+ {
+ if (lp->speed)
+ {
+ br = lp->speed;
+ tc = (lp->xtal / (br * 2)) - 2;
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+ }
+ }
+ /* Turn on Tx by raising RTS */
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+ /* Transmitter on now */
+ } else { /* turning off Tx */
+ lp->tstate = IDLE;
+
+ /* Turn off Tx by dropping RTS */
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+ if (!lp->clockmode)
+ {
+ if (lp->speed) /* internally clocked */
+ {
+ /* Reprogram BRG from 32x clock for Rx DPLL */
+ /* BRG off, keep PClk source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC);
+ br = lp->speed;
+ tc = ((lp->xtal / 32) / (br * 2)) - 2;
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+
+ /* SEARCH mode, BRG source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+ /* Enable the BRG */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+ }
+ }
+ /* Flush Rx fifo */
+ /* Turn Rx off */
+ wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+
+ /* Reset error latch */
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+
+ /* get status byte from R1 */
+ (void) rdscc(lp->cardbase, cmd, R1);
+
+ /* Read and dump data in queue */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ /* Now, turn on Rx and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 );
+
+ lp->rstate = ACTIVE;
+
+ if (lp->dmachan)
+ {
+ setup_rx_dma(lp);
+ } else {
+ /* Reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ /* Allow aborts to interrupt us */
+ wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB);
+
+ }
+ wrtscc(lp->cardbase, cmd, R15, BRKIE );
+ }
+} /* pt_rts() */
+
+
+static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize)
+{
+ if (((addr & 0xffff) + dev_bufsize) <= 0x10000)
+ return 1;
+ else
+ return 0;
+}
+
+static int pt_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); /* addr is an AX.25 shifted ASCII */
+ return 0; /* mac address */
+}
+
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char * get_dma_buffer(unsigned long *mem_ptr)
+{
+ char *ret;
+
+ ret = (char *) *mem_ptr;
+
+ if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) {
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ ret = (char *) *mem_ptr;
+ }
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ return (ret);
+} /* get_dma_buffer() */
+
+
+/*
+ * Sets up all the structures for the PT device
+ */
+static int pt_probe(struct device *dev)
+{
+ short ioaddr;
+ struct pt_local *lp;
+ unsigned long flags;
+ unsigned long mem_ptr;
+
+ ioaddr = dev->base_addr;
+
+ /*
+ * Initialise the device structure.
+ * Must be done before chipset_init()
+ * Make sure data structures used by the PT are aligned
+ */
+ dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+ lp = (struct pt_local*) dev->priv;
+
+ memset(dev->priv, 0, sizeof(struct pt_local));
+
+ /* Allocate some buffers which do not cross DMA boundaries */
+ mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local);
+ lp->txdmabuf = get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+ /* Initialise the Rx buffer */
+ lp->rcvbuf = lp->rxdmabuf1;
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Initialise the transmit queue head structure */
+ skb_queue_head_init(&lp->sndq);
+
+ lp->base = dev->base_addr;
+ lp->cardbase = dev->base_addr & 0x3f0;
+
+ /* These need to be initialised before scc_init() is called.
+ */
+ lp->xtal = XTAL;
+
+ if (dev->base_addr & CHANA) {
+ lp->speed = DEF_A_SPEED;
+ lp->txdelay = DEF_A_TXDELAY;
+ lp->persist = DEF_A_PERSIST;
+ lp->slotime = DEF_A_SLOTIME;
+ lp->squeldelay = DEF_A_SQUELDELAY;
+ lp->clockmode = DEF_A_CLOCKMODE;
+ lp->nrzi = DEF_A_NRZI;
+ } else {
+ lp->speed = DEF_B_SPEED;
+ lp->txdelay = DEF_B_TXDELAY;
+ lp->persist = DEF_B_PERSIST;
+ lp->slotime = DEF_B_SLOTIME;
+ lp->squeldelay = DEF_B_SQUELDELAY;
+ lp->clockmode = DEF_B_CLOCKMODE;
+ lp->nrzi = DEF_B_NRZI;
+ }
+ lp->bufsiz = DMA_BUFF_SIZE;
+ lp->tstate = IDLE;
+
+ chipset_init(dev);
+
+ if (dev->base_addr & CHANA) {
+ /* Note that a single IRQ services 2 devices (A and B channels)
+ */
+
+ /*
+ * We disable the dma for a while, we have to get ints working
+ * properly first!!
+ */
+ lp->dmachan = 0;
+
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+
+ /* Turn on PT interrupts */
+ save_flags(flags);
+ cli();
+ outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
+ restore_flags(flags);
+
+ /* Set a timer interrupt */
+ tdelay(lp, 1);
+ dev->irq = autoirq_report(20);
+
+ /* Turn off PT interrupts */
+ save_flags(flags);
+ cli();
+ outb_p( (pt_sercfg &= ~ PT_EI), lp->cardbase + INT_CFG);
+ restore_flags(flags);
+
+ if (!dev->irq) {
+ printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
+ }
+ }
+
+ printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
+
+ /* This board has jumpered interrupts. Snarf the interrupt vector
+ * now. There is no point in waiting since no other device can use
+ * the interrupt, and this marks the 'irqaction' as busy.
+ */
+ {
+ int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev);
+ if (irqval) {
+ printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ /* Grab the region */
+ request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" );
+ } /* A port */
+ dev->open = pt_open;
+ dev->stop = pt_close;
+ dev->do_ioctl = pt_ioctl;
+ dev->hard_start_xmit = pt_send_packet;
+ dev->get_stats = pt_get_stats;
+
+ /* Fill in the fields of the device structure */
+ dev_init_buffers(dev);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+#endif
+
+ dev->set_mac_address = pt_set_mac_address;
+
+ dev->type = ARPHRD_AX25; /* AF_AX25 device */
+ dev->hard_header_len = 73; /* We do digipeaters now */
+ dev->mtu = 1500; /* eth_mtu is default */
+ dev->addr_len = 7; /* sizeof an ax.25 address */
+ memcpy(dev->broadcast, ax25_bcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+
+ /* New style flags */
+ dev->flags = 0;
+
+ return 0;
+} /* pt_probe() */
+
+
+/* Open/initialise the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that 'should' only be set once at boot, so that there is
+ * a non-reboot way to recover if something goes wrong.
+ * derived from last half of tsync_attach()
+ */
+static int pt_open(struct device *dev)
+{
+ unsigned long flags;
+ struct pt_local *lp = dev->priv;
+ static first_time = 1;
+
+ if (dev->base_addr & CHANA)
+ {
+ if (first_time)
+ {
+ if (request_dma(dev->dma, "pt"))
+ {
+ free_irq(dev->irq, dev);
+ return -EAGAIN;
+ }
+ }
+
+ /* Reset hardware */
+ chipset_init(dev);
+ }
+ lp->tstate = IDLE;
+
+ if (dev->base_addr & CHANA)
+ {
+ scc_init(dev);
+ scc_init(dev->next);
+ }
+ /* Save a copy of register RR0 for comparing with later on */
+ /* We always put 0 in zero count */
+ lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT;
+
+ /* master interrupt enable */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV);
+ outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
+ restore_flags(flags);
+
+ lp->open_time = jiffies;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ first_time = 0;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+} /* pt_open() */
+
+static int pt_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct pt_local *lp = (struct pt_local *) dev->priv;
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA);
+#endif
+ hardware_send_packet(lp, skb);
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+
+
+/* The inverse routine to pt_open() */
+static int pt_close(struct device *dev)
+{
+ unsigned long flags;
+ struct pt_local *lp = dev->priv;
+ struct sk_buff *ptr = NULL;
+ int cmd;
+
+ cmd = lp->base + CTL;
+
+ save_flags(flags);
+ cli();
+
+ /* Reset SCC or channel */
+ chipset_init(dev);
+ disable_dma(lp->dmachan);
+
+ lp->open_time = 0;
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Free any buffers left in the hardware transmit queue */
+ while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+ kfree_skb(ptr, FREE_WRITE);
+
+ restore_flags(flags);
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA);
+#endif
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* pt_close() */
+
+
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ unsigned long flags;
+ struct pt_req rq;
+ struct pt_local *lp = (struct pt_local *) dev->priv;
+
+ int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req));
+ if (ret)
+ return ret;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -EINVAL;
+
+ copy_from_user(&rq, ifr->ifr_data, sizeof(struct pt_req));
+
+ switch (rq.cmd) {
+ case SIOCSPIPARAM:
+
+ if (!suser())
+ return -EPERM;
+ save_flags(flags);
+ cli();
+ lp->txdelay = rq.txdelay;
+ lp->persist = rq.persist;
+ lp->slotime = rq.slotime;
+ lp->squeldelay = rq.squeldelay;
+ lp->clockmode = rq.clockmode;
+ lp->speed = rq.speed;
+ pt_open(&pt0a);
+ restore_flags(flags);
+ ret = 0;
+ break;
+
+ case SIOCSPIDMA:
+
+ if (!suser())
+ return -EPERM;
+ ret = 0;
+ if (dev->base_addr & CHANA) { /* if A channel */
+ if (rq.dmachan < 1 || rq.dmachan > 3)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ pt_close(dev);
+ free_dma(lp->dmachan);
+ dev->dma = lp->dmachan = rq.dmachan;
+ if (request_dma(lp->dmachan,"pt"))
+ ret = -EAGAIN;
+ pt_open(dev);
+ restore_flags(flags);
+ }
+ break;
+
+ case SIOCSPIIRQ:
+ ret = -EINVAL; /* add this later */
+ break;
+
+ case SIOCGPIPARAM:
+ case SIOCGPIDMA:
+ case SIOCGPIIRQ:
+
+ rq.speed = lp->speed;
+ rq.txdelay = lp->txdelay;
+ rq.persist = lp->persist;
+ rq.slotime = lp->slotime;
+ rq.squeldelay = lp->squeldelay;
+ rq.clockmode = lp->clockmode;
+ rq.dmachan = lp->dmachan;
+ rq.irq = dev->irq;
+ copy_to_user(ifr->ifr_data, &rq, sizeof(struct pt_req));
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+
+static struct net_device_stats *pt_get_stats(struct device *dev)
+{
+ struct pt_local *lp = (struct pt_local *) dev->priv;
+ return &lp->stats;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
+
+
+static void tdelay(struct pt_local *lp, int time)
+{
+ /* For some reason, we turn off the Tx interrupts here! */
+ if (!lp->dmachan)
+ wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+
+ if (lp->base & CHANA)
+ {
+ outb_p(time & 0xff, lp->cardbase + TMR1);
+ outb_p((time >> 8)&0xff, lp->cardbase + TMR1);
+ }
+ else
+ {
+ outb_p(time & 0xff, lp->cardbase + TMR2);
+ outb_p((time >> 8)&0xff, lp->cardbase + TMR2);
+ }
+} /* tdelay */
+
+
+static void pt_txisr(struct pt_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned char c;
+
+ save_flags(flags);
+ cli();
+ cmd = lp->base + CTL;
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif
+
+ switch (lp->tstate)
+ {
+ case CRCOUT:
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+
+ case IDLE:
+ /* Transmitter idle. Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+ {
+ /* Nothing to send - return to receive mode
+ * Tx off now - flag should have gone
+ */
+ pt_rts(lp, OFF);
+
+ restore_flags(flags);
+ return;
+ }
+ if (!lp->dmachan)
+ {
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ }
+ /* If a buffer to send, drop though here */
+
+ case DEFER:
+ /* Check DCD - debounce it */
+ /* See Intel Microcommunications Handbook p2-308 */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+ {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* DEFER until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist)
+ {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ pt_rts(lp, ON); /* Tx on */
+ if (lp->dmachan)
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ case ACTIVE:
+ /* Here we are actively sending a frame */
+ if (lp->txcnt--)
+ {
+ /* XLZ - checkout Gracilis PT code to see if the while
+ * loop is better or not.
+ */
+ c = *lp->txptr++;
+ /* next char is gone */
+ wrtscc(lp->cardbase, cmd, R8, c);
+ /* stuffing a char satisfies interrupt condition */
+ } else {
+ /* No more to send */
+ kfree_skb(lp->sndbuf, FREE_WRITE);
+ lp->sndbuf = NULL;
+ if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
+ {
+ /* Did we underrun */
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ }
+ lp->tstate = UNDERRUN;
+ /* Send flags on underrun */
+ if (lp->nrzi)
+ {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ);
+ }
+ /* Reset Tx interrupt pending */
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);
+ }
+ restore_flags(flags);
+ return;
+ default:
+ printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
+ pt_rts(lp, OFF);
+ lp->tstate = IDLE;
+ break;
+ } /*switch */
+ restore_flags(flags);
+}
+
+static void pt_rxisr(struct device *dev)
+{
+ struct pt_local *lp = (struct pt_local*) dev->priv;
+ int cmd = lp->base + CTL;
+ int bytecount;
+ unsigned long flags;
+ char rse;
+ struct sk_buff *skb;
+ int sksize, pkt_len;
+ struct mbuf *cur_buf = NULL;
+ unsigned char *cfix;
+
+ save_flags(flags);
+ cli();
+
+ /* Get status byte from R1 */
+ rse = rdscc(lp->cardbase, cmd, R1);
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
+#endif
+
+ if (lp->dmachan && (rse & Rx_OVR))
+ lp->rstate = RXERROR;
+
+ if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan)
+ {
+ /* There is a char to be stored
+ * Read special condition bits before reading the data char
+ */
+ if (rse & Rx_OVR)
+ {
+ /* Rx overrun - toss buffer */
+ /* wind back the pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ lp->rstate = RXERROR;
+ lp->stats.rx_errors++;
+ lp->stats.rx_fifo_errors++;
+ } else if (lp->rcvbuf->cnt >= lp->bufsiz)
+ {
+ /* Too large packet
+ * wind back Rx buffer pointers
+ */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ lp->rstate = TOOBIG;
+ }
+ /* ok, we can store the Rx char if no errors */
+ if (lp->rstate == ACTIVE)
+ {
+ *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);
+ lp->rcvbuf->cnt++;
+ } else {
+ /* we got an error, dump the FIFO */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ /* Reset error latch */
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+ lp->rstate = ACTIVE;
+
+ /* Resync the SCC */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+ }
+ }
+
+ if (rse & END_FR)
+ {
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
+#endif
+ if (lp->dmachan)
+ {
+ clear_dma_ff(lp->dmachan);
+ bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+ } else {
+ bytecount = lp->rcvbuf->cnt;
+ }
+
+ /* END OF FRAME - Make sure Rx was active */
+ if (lp->rcvbuf->cnt > 0 || lp->dmachan)
+ {
+ if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10))
+ {
+ if ((bytecount >= 10) && (rse & CRC_ERR))
+ {
+ lp->stats.rx_crc_errors++;
+ }
+ if (lp->dmachan)
+ {
+ if (lp->rstate == RXERROR)
+ {
+ lp->stats.rx_errors++;
+ lp->stats.rx_over_errors++;
+ }
+ lp->rstate = ACTIVE;
+ setup_rx_dma(lp);
+ } else {
+ /* wind back Rx buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Re-sync the SCC */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+ }
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
+#endif
+ } else {
+ /* We have a valid frame */
+ if (lp->dmachan)
+ {
+ pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1;
+ /* Get buffer for next frame */
+ cur_buf = lp->rcvbuf;
+ switchbuffers(lp);
+ setup_rx_dma(lp);
+ } else {
+ pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 CRC bytes */
+ pkt_len += 1; /* make room for KISS control byte */
+ }
+
+ /* Malloc up new buffer */
+ sksize = pkt_len;
+ skb = dev_alloc_skb(sksize);
+ if (skb == NULL)
+ {
+ printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ restore_flags(flags);
+ return;
+ }
+ skb->dev = dev;
+
+ /* KISS kludge = prefix with a 0 byte */
+ cfix=skb_put(skb,pkt_len);
+ *cfix++=0;
+ /* skb->data points to the start of sk_buff area */
+ if (lp->dmachan)
+ memcpy(cfix, (char*)cur_buf->data, pkt_len - 1);
+ else
+ memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
+ skb->protocol = ntohs(ETH_P_AX25);
+ skb->mac.raw=skb->data;
+ lp->stats.rx_bytes+=skb->len;
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ if (!lp->dmachan)
+ {
+ /* packet queued - wind back buffer for next frame */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ }
+ } /* good frame */
+ } /* check active Rx */
+ /* Clear error status */
+ lp->rstate = ACTIVE;
+ /* Reset error latch */
+ } /* end EOF check */
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+ restore_flags(flags);
+} /* pt_rxisr() */
+
+/*
+ * This handles the two timer interrupts.
+ * This is a real bugger, cause you have to rip it out of the pi's
+ * external status code. They use the CTS line or something.
+ */
+static void pt_tmrisr(struct pt_local *lp)
+{
+ unsigned long flags;
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif
+
+ save_flags(flags);
+ cli();
+
+
+ switch (lp->tstate)
+ {
+ /* Most of this stuff is in pt_exisr() */
+ case FLAGOUT:
+ case ST_TXDELAY:
+ case DEFER:
+/* case ACTIVE:
+ case UNDERRUN:*/
+ pt_exisr(lp);
+ break;
+
+ default:
+ if (lp->base & CHANA)
+ printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
+ else
+ printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
+ break;
+ } /* end switch */
+ restore_flags(flags);
+} /* pt_tmrisr() */
+
+
+/*
+ * This routine is called by the kernel when there is an interrupt for the
+ * PT.
+ */
+static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* It's a tad dodgy here, but we assume pt0a until proven otherwise */
+ struct device *dev = &pt0a;
+ struct pt_local *lp = dev->priv;
+ unsigned char intreg;
+ unsigned char st;
+ register int cbase = dev->base_addr & 0x3f0;
+ unsigned long flags;
+
+ /* Read the PT's interrupt register, this is not the SCC one! */
+ intreg = inb_p(cbase + INT_REG);
+ while(( intreg & 0x07) != 0x07) {
+ /* Read interrupt register pending from Channel A */
+ while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0)
+ {
+ /* Read interrupt vector from R2, channel B */
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);
+#endif
+/* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);
+#endif
+ if (st & CHARxIP) {
+ /* Channel A Rx */
+ lp = (struct pt_local*)pt0a.priv;
+ pt_rxisr(&pt0a);
+ } else if (st & CHATxIP) {
+ /* Channel A Tx */
+ lp = (struct pt_local*)pt0a.priv;
+ pt_txisr(lp);
+ } else if (st & CHAEXT) {
+ /* Channel A External Status */
+ lp = (struct pt_local*)pt0a.priv;
+ pt_exisr(lp);
+ } else if (st & CHBRxIP) {
+ /* Channel B Rx */
+ lp= (struct pt_local*)pt0b.priv;
+ pt_rxisr(&pt0b);
+ } else if (st & CHBTxIP) {
+ /* Channel B Tx */
+ lp = (struct pt_local*)pt0b.priv;
+ pt_txisr(lp);
+ } else if (st & CHBEXT) {
+ /* Channel B External Status */
+ lp = (struct pt_local*)pt0b.priv;
+ pt_exisr(lp);
+ }
+ /* Reset highest interrupt under service */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+ restore_flags(flags);
+ } /* end of SCC ints */
+
+ if (!(intreg & PT_TMR1_MSK))
+ {
+ /* Clear timer 1 */
+ inb_p(cbase + TMR1CLR);
+
+ pt_tmrisr( (struct pt_local*)pt0a.priv);
+ }
+
+ if (!(intreg & PT_TMR2_MSK))
+ {
+ /* Clear timer 2 */
+ inb_p(cbase + TMR2CLR);
+
+ pt_tmrisr( (struct pt_local*)pt0b.priv);
+ }
+
+ /* Get the next PT interrupt vector */
+ intreg = inb_p(cbase + INT_REG);
+ } /* while (intreg) */
+} /* pt_interrupt() */
+
+
+static void pt_exisr(struct pt_local *lp)
+{
+ unsigned long flags;
+ int cmd = lp->base + CTL;
+ unsigned char st;
+ char c;
+ int length;
+
+ save_flags(flags);
+ cli();
+
+ /* Get external status */
+ st = rdscc(lp->cardbase, cmd, R0);
+
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
+#endif
+ /* Reset external status latch */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+ if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan)
+ {
+ setup_rx_dma(lp);
+ lp->rstate = ACTIVE;
+ }
+
+ switch (lp->tstate)
+ {
+ case ACTIVE: /* Unexpected underrun */
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
+#endif
+ kfree_skb(lp->sndbuf, FREE_WRITE);
+ lp->sndbuf = NULL;
+ if (!lp->dmachan)
+ {
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ }
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ case UNDERRUN:
+ lp->tstate = CRCOUT;
+ restore_flags(flags);
+ return;
+ case FLAGOUT:
+ /* squeldelay has timed out */
+ /* Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+ {
+ /* Nothing to send - return to Rx mode */
+ pt_rts(lp, OFF);
+ lp->tstate = IDLE;
+ restore_flags(flags);
+ return;
+ }
+ if (!lp->dmachan)
+ {
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ }
+ /* Fall through if we have a packet */
+
+ case ST_TXDELAY:
+ if (lp->dmachan)
+ {
+ /* Disable DMA chan */
+ disable_dma(lp->dmachan);
+
+ /* Set up for TX dma */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+
+ length = lp->sndbuf->len - 1;
+ memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+
+ /* Setup DMA controller for Tx */
+ setup_tx_dma(lp, length);
+
+ enable_dma(lp->dmachan);
+
+ /* Reset CRC, Txint pending */
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+
+ /* Allow underrun only */
+ wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+ /* Enable TX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+
+ /* Send CRC on underrun */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+ lp->tstate = ACTIVE;
+ break;
+ }
+ /* Get first char to send */
+ lp->txcnt--;
+ c = *lp->txptr++;
+ /* Reset CRC for next frame */
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);
+
+ /* send abort on underrun */
+ if (lp->nrzi)
+ {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER);
+ }
+ /* send first char */
+ wrtscc(lp->cardbase, cmd, R8, c);
+
+ /* Reset end of message latch */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+ /* stuff an extra one in */
+/* while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt)
+ {
+ lp->txcnt--;
+ c = *lp->txptr++;
+ wrtscc(lp->cardbase, cmd, R8, c);
+ }*/
+
+ /* select Tx interrupts to enable */
+ /* Allow underrun int only */
+ wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+ /* Reset external interrupts */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+ /* Tx and Rx ints enabled */
+ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+
+ lp->tstate = ACTIVE;
+ restore_flags(flags);
+ return;
+
+ /* slotime has timed out */
+ case DEFER:
+ /* Check DCD - debounce it
+ * see Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+ {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* DEFER until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist)
+ {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ if (lp->dmachan)
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ pt_rts(lp, ON); /* Tx on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ /* Only for int driven parts */
+ if (lp->dmachan)
+ {
+ restore_flags(flags);
+ return;
+ }
+
+ } /* end switch */
+ /*
+ * Rx mode only
+ * This triggers when hunt mode is entered, & since an ABORT
+ * automatically enters hunt mode, we use that to clean up
+ * any waiting garbage
+ */
+ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
+ {
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: exisr(): abort detected.\n");
+#endif
+ /* read and dump all of SCC Rx FIFO */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Re-sync the SCC */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+ }
+
+ /* Check for DCD transitions */
+ if ( (st & DCD) != (lp->saved_RR0 & DCD))
+ {
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
+#endif
+ if (st & DCD)
+ {
+ /* Check that we don't already have some data */
+ if (lp->rcvbuf->cnt > 0)
+ {
+#ifdef PT_DEBUG
+ printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
+#endif
+ /* wind back buffers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ }
+ } else { /* DCD off */
+
+ /* read and dump al SCC FIFO */
+ (void)rdscc(lp->cardbase, cmd, R8);
+ (void)rdscc(lp->cardbase, cmd, R8);
+ (void)rdscc(lp->cardbase, cmd, R8);
+
+ /* wind back buffers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Re-sync the SCC */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+ }
+
+ }
+ /* Update the saved version of register RR) */
+ lp->saved_RR0 = st &~ ZCOUNT;
+ restore_flags(flags);
+
+} /* pt_exisr() */
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Craig Small VK2XLZ <vk2xlz@vk2xlz.ampr.org>");
+MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card");
+
+int init_module(void)
+{
+ return pt_init();
+}
+
+void cleanup_module(void)
+{
+ free_irq(pt0a.irq, &pt0a); /* IRQs and IO Ports are shared */
+ release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
+
+ kfree(pt0a.priv);
+ pt0a.priv = NULL;
+ unregister_netdev(&pt0a);
+
+ kfree(pt0b.priv);
+ pt0b.priv = NULL;
+ unregister_netdev(&pt0b);
+}
+#endif
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
new file mode 100644
index 000000000..1665d2b2b
--- /dev/null
+++ b/drivers/net/hamradio/scc.c
@@ -0,0 +1,2257 @@
+#define RCS_ID "$Id: scc.c,v 1.71 1997/11/29 19:59:20 jreuter Exp jreuter $"
+
+#define VERSION "3.0"
+#define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
+
+/*
+ * Please use z8530drv-utils-3.0 with this version.
+ * ------------------
+ */
+
+/*
+ ********************************************************************
+ * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 *
+ ********************************************************************
+
+
+ ********************************************************************
+
+ Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
+
+ portions (c) 1993 Guido ten Dolle PE1NNZ
+
+ ********************************************************************
+
+ The driver and the programs in the archive are UNDER CONSTRUCTION.
+ The code is likely to fail, and so your kernel could --- even
+ a whole network.
+
+ This driver is intended for Amateur Radio use. If you are running it
+ for commercial purposes, please drop me a note. I am nosy...
+
+ ...BUT:
+
+ ! You m u s t recognize the appropriate legislations of your country !
+ ! before you connect a radio to the SCC board and start to transmit or !
+ ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! !
+
+ For non-Amateur-Radio use please note that you might need a special
+ allowance/licence from the designer of the SCC Board and/or the
+ MODEM.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the (modified) GNU General Public License
+ delivered with the Linux kernel source.
+
+ 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 find a copy of the GNU General Public License in
+ /usr/src/linux/COPYING;
+
+ ********************************************************************
+
+
+ Incomplete history of z8530drv:
+ -------------------------------
+
+ 940913 - started to write the driver, rescued most of my own
+ code (and Hans Alblas' memory buffer pool concept) from
+ an earlier project "sccdrv" which was initiated by
+ Guido ten Dolle. Not much of the old driver survived,
+ though. The first version I put my hands on was sccdrv1.3
+ from August 1993. The memory buffer pool concept
+ appeared in an unauthorized sccdrv version (1.5) from
+ August 1994.
+
+ 950131 - changed copyright notice to GPL without limitations.
+
+ .
+ . <SNIP>
+ .
+
+ 961005 - New semester, new driver...
+
+ * KISS TNC emulator removed (TTY driver)
+ * Source moved to drivers/net/
+ * Includes Z8530 defines from drivers/net/z8530.h
+ * Uses sk_buffer memory management
+ * Reduced overhead of /proc/net/z8530drv output
+ * Streamlined quite a lot things
+ * Invents brand new bugs... ;-)
+
+ The move to version number 3.0 reflects theses changes.
+ You can use 'kissbridge' if you need a KISS TNC emulator.
+
+ 961213 - Fixed for Linux networking changes. (G4KLX)
+ 970108 - Fixed the remaining problems.
+ 970402 - Hopefully fixed the problems with the new *_timer()
+ routines, added calibration code.
+ 971012 - made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
+
+ Thanks to all who contributed to this driver with ideas and bug
+ reports!
+
+ NB -- if you find errors, change something, please let me know
+ first before you distribute it... And please don't touch
+ the version number. Just replace my callsign in
+ "v3.0.dl1bke" with your own. Just to avoid confusion...
+
+ If you want to add your modification to the linux distribution
+ please (!) contact me first.
+
+ New versions of the driver will be announced on the linux-hams
+ mailing list on vger.rutgers.edu. To subscribe send an e-mail
+ to majordomo@vger.rutgers.edu with the following line in
+ the body of the mail:
+
+ subscribe linux-hams
+
+ The content of the "Subject" field will be ignored.
+
+ vy 73,
+ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
+ AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU
+ Internet: jreuter@poboxes.com
+ www : http://www.rat.de/jr
+*/
+
+/* ----------------------------------------------------------------------- */
+
+#undef SCC_LDELAY 1 /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+#define MAXSCC 4 /* number of max. supported chips */
+#define BUFSIZE 384 /* must not exceed 4096 */
+#define MAXQUEUE 8 /* number of buffers we queue ourself */
+#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */
+ /* enable_irq()/disable_irq() */
+#undef SCC_DEBUG
+
+#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */
+
+/* ----------------------------------------------------------------------- */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/init.h>
+
+#include <linux/scc.h>
+#include "z8530.h"
+
+#include <net/ax25.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+
+int scc_init(void);
+
+static void t_dwait(unsigned long);
+static void t_txdelay(unsigned long);
+static void t_tail(unsigned long);
+static void t_busy(unsigned long);
+static void t_maxkeyup(unsigned long);
+static void t_idle(unsigned long);
+static void scc_tx_done(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
+static void scc_start_maxkeyup(struct scc_channel *);
+static void scc_start_defer(struct scc_channel *);
+
+static void z8530_init(void);
+
+static void init_channel(struct scc_channel *scc);
+static void scc_key_trx (struct scc_channel *scc, char tx);
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static void scc_init_timer(struct scc_channel *scc);
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
+static int scc_net_init(struct device *dev);
+static int scc_net_open(struct device *dev);
+static int scc_net_close(struct device *dev);
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
+static int scc_net_tx(struct sk_buff *skb, struct device *dev);
+static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int scc_net_set_mac_address(struct device *dev, void *addr);
+static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
+static struct net_device_stats * scc_net_get_stats(struct device *dev);
+
+static unsigned char *SCC_DriverName = "scc";
+
+static struct irqflags { unsigned char used : 1; } Ivec[16];
+
+static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */
+
+static struct scc_ctrl {
+ io_port chan_A;
+ io_port chan_B;
+ int irq;
+} SCC_ctrl[MAXSCC+1];
+
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
+
+MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("scc");
+
+/* ******************************************************************** */
+/* * Port Access Functions * */
+/* ******************************************************************** */
+
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+extern __inline__ unsigned char InReg(io_port port, unsigned char reg)
+{
+ unsigned long flags;
+ unsigned char r;
+
+ save_flags(flags);
+ cli();
+#ifdef SCC_LDELAY
+ Outb(port, reg);
+ udelay(SCC_LDELAY);
+ r=Inb(port);
+ udelay(SCC_LDELAY);
+#else
+ Outb(port, reg);
+ r=Inb(port);
+#endif
+ restore_flags(flags);
+ return r;
+}
+
+extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+#ifdef SCC_LDELAY
+ Outb(port, reg); udelay(SCC_LDELAY);
+ Outb(port, val); udelay(SCC_LDELAY);
+#else
+ Outb(port, reg);
+ Outb(port, val);
+#endif
+ restore_flags(flags);
+}
+
+extern __inline__ void wr(struct scc_channel *scc, unsigned char reg,
+ unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
+}
+
+extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
+}
+
+extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
+}
+
+#ifdef DISABLE_ALL_INTS
+extern __inline__ void scc_cli(int irq)
+{ cli(); }
+extern __inline__ void scc_sti(int irq)
+{ sti(); }
+#else
+static __inline__ void scc_cli(int irq)
+{ disable_irq(irq); }
+static __inline__ void scc_sti(int irq)
+{ enable_irq(irq); }
+#endif
+
+/* ******************************************************************** */
+/* * Some useful macros * */
+/* ******************************************************************** */
+
+
+extern __inline__ void scc_lock_dev(struct scc_channel *scc)
+{
+ scc->dev->tbusy = 1;
+}
+
+extern __inline__ void scc_unlock_dev(struct scc_channel *scc)
+{
+ scc->dev->tbusy = 0;
+}
+
+extern __inline__ void scc_discard_buffers(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (scc->tx_buff != NULL)
+ {
+ dev_kfree_skb(scc->tx_buff, FREE_WRITE);
+ scc->tx_buff = NULL;
+ }
+
+ while (skb_queue_len(&scc->tx_queue))
+ dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE);
+
+ restore_flags(flags);
+}
+
+
+
+/* ******************************************************************** */
+/* * Interrupt Service Routines * */
+/* ******************************************************************** */
+
+
+/* ----> subroutines for the interrupt handlers <---- */
+
+extern __inline__ void scc_notify(struct scc_channel *scc, int event)
+{
+ struct sk_buff *skb;
+ char *bp;
+
+ if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
+ return;
+
+ skb = dev_alloc_skb(2);
+ if (skb != NULL)
+ {
+ bp = skb_put(skb, 2);
+ *bp++ = PARAM_HWEVENT;
+ *bp++ = event;
+ scc_net_rx(scc, skb);
+ } else
+ scc->stat.nospace++;
+}
+
+extern __inline__ void flush_rx_FIFO(struct scc_channel *scc)
+{
+ int k;
+
+ for (k=0; k<3; k++)
+ Inb(scc->data);
+
+ if(scc->rx_buff != NULL) /* did we receive something? */
+ {
+ scc->stat.rxerrs++; /* then count it as an error */
+ kfree_skb(scc->rx_buff, FREE_READ);
+ scc->rx_buff = NULL;
+ }
+}
+
+
+/* ----> four different interrupt handlers for Tx, Rx, changing of */
+/* DCD/CTS and Rx/Tx errors */
+
+/* Transmitter interrupt handler */
+extern __inline__ void scc_txint(struct scc_channel *scc)
+{
+ struct sk_buff *skb;
+
+ scc->stat.txints++;
+ skb = scc->tx_buff;
+
+ /* send first octet */
+
+ if (skb == NULL)
+ {
+ skb = skb_dequeue(&scc->tx_queue);
+ scc->tx_buff = skb;
+ scc_unlock_dev(scc);
+
+ if (skb == NULL)
+ {
+ scc_tx_done(scc);
+ Outb(scc->ctrl, RES_Tx_P);
+ return;
+ }
+
+ if (skb->len == 0) /* Paranoia... */
+ {
+ dev_kfree_skb(skb, FREE_WRITE);
+ scc->tx_buff = NULL;
+ scc_tx_done(scc);
+ Outb(scc->ctrl, RES_Tx_P);
+ return;
+ }
+
+ scc->stat.tx_state = TXS_ACTIVE;
+
+ OutReg(scc->ctrl, R0, RES_Tx_CRC);
+ /* reset CRC generator */
+ or(scc,R10,ABUNDER); /* re-install underrun protection */
+ Outb(scc->data,*skb->data); /* send byte */
+ skb_pull(skb, 1);
+
+ if (!scc->enhanced) /* reset EOM latch */
+ Outb(scc->ctrl,RES_EOM_L);
+ return;
+ }
+
+ /* End Of Frame... */
+
+ if (skb->len == 0)
+ {
+ Outb(scc->ctrl, RES_Tx_P); /* reset pending int */
+ cl(scc, R10, ABUNDER); /* send CRC */
+ dev_kfree_skb(skb, FREE_WRITE);
+ scc->tx_buff = NULL;
+ scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
+ return;
+ }
+
+ /* send octet */
+
+ Outb(scc->data,*skb->data);
+ skb_pull(skb, 1);
+}
+
+
+/* External/Status interrupt handler */
+extern __inline__ void scc_exint(struct scc_channel *scc)
+{
+ unsigned char status,changes,chg_and_stat;
+
+ scc->stat.exints++;
+
+ status = InReg(scc->ctrl,R0);
+ changes = status ^ scc->status;
+ chg_and_stat = changes & status;
+
+ /* ABORT: generated whenever DCD drops while receiving */
+
+ if (chg_and_stat & BRK_ABRT) /* Received an ABORT */
+ flush_rx_FIFO(scc);
+
+
+ /* DCD: on = start to receive packet, off = ABORT condition */
+ /* (a successfully received packet generates a special condition int) */
+
+ if(changes & DCD) /* DCD input changed state */
+ {
+ if(status & DCD) /* DCD is now ON */
+ {
+ if (scc->modem.clocksrc != CLK_EXTERNAL)
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ } else { /* DCD is now OFF */
+ cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
+ flush_rx_FIFO(scc);
+ }
+
+ if (!scc->kiss.softdcd)
+ scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
+ }
+
+ /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+
+ if (changes & SYNC_HUNT)
+ {
+ if (scc->kiss.softdcd)
+ scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
+ else
+ cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */
+ }
+
+#ifdef notdef
+ /* CTS: use external TxDelay (what's that good for?!)
+ * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+ * own purposes) we _should_ use the "autoenable" feature
+ * of the Z8530 and not this interrupt...
+ */
+
+ if (chg_and_stat & CTS) /* CTS is now ON */
+ {
+ if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
+ scc_start_tx_timer(scc, t_txdelay, 0);
+ }
+#endif
+
+ if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
+ {
+ scc->stat.tx_under++; /* oops, an underrun! count 'em */
+ Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */
+
+ if (scc->tx_buff != NULL)
+ {
+ dev_kfree_skb(scc->tx_buff, FREE_WRITE);
+ scc->tx_buff = NULL;
+ }
+
+ or(scc,R10,ABUNDER);
+ scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */
+ }
+
+ scc->status = status;
+ Outb(scc->ctrl,RES_EXT_INT);
+}
+
+
+/* Receiver interrupt handler */
+extern __inline__ void scc_rxint(struct scc_channel *scc)
+{
+ struct sk_buff *skb;
+
+ scc->stat.rxints++;
+
+ if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ Inb(scc->data); /* discard char */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+ return;
+ }
+
+ skb = scc->rx_buff;
+
+ if (skb == NULL)
+ {
+ skb = dev_alloc_skb(scc->stat.bufsize);
+ if (skb == NULL)
+ {
+ scc->dev_stat.rx_dropped++;
+ scc->stat.nospace++;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
+ }
+
+ scc->rx_buff = skb;
+ *(skb_put(skb, 1)) = 0; /* KISS data */
+ }
+
+ if (skb->len >= scc->stat.bufsize)
+ {
+#ifdef notdef
+ printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
+#endif
+ kfree_skb(skb, FREE_READ);
+ scc->rx_buff = NULL;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
+ }
+
+ *(skb_put(skb, 1)) = Inb(scc->data);
+}
+
+
+/* Receive Special Condition interrupt handler */
+extern __inline__ void scc_spint(struct scc_channel *scc)
+{
+ unsigned char status;
+ struct sk_buff *skb;
+
+ scc->stat.spints++;
+
+ status = InReg(scc->ctrl,R1); /* read receiver status */
+
+ Inb(scc->data); /* throw away Rx byte */
+ skb = scc->rx_buff;
+
+ if(status & Rx_OVR) /* receiver overrun */
+ {
+ scc->stat.rx_over++; /* count them */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+
+ if (skb != NULL)
+ kfree_skb(skb, FREE_READ);
+ scc->rx_buff = NULL;
+ }
+
+ if(status & END_FR && skb != NULL) /* end of frame */
+ {
+ /* CRC okay, frame ends on 8 bit boundary and received something ? */
+
+ if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+ {
+ /* ignore last received byte (first of the CRC bytes) */
+ skb_trim(skb, skb->len-1);
+ scc_net_rx(scc, skb);
+ scc->rx_buff = NULL;
+ scc->stat.rxframes++;
+ } else { /* a bad frame */
+ kfree_skb(skb, FREE_READ);
+ scc->rx_buff = NULL;
+ scc->stat.rxerrs++;
+ }
+ }
+
+ Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+ switch (vector & VECTOR_MASK)
+ {
+ case TXINT: scc_txint(scc); break;
+ case EXINT: scc_exint(scc); break;
+ case RXINT: scc_rxint(scc); break;
+ case SPINT: scc_spint(scc); break;
+ }
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+ use it to get the number of the chip that generated the int.
+ If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char vector;
+ struct scc_channel *scc;
+ struct scc_ctrl *ctrl;
+ int k;
+
+ scc_cli(irq);
+
+ if (Vector_Latch)
+ {
+ for(k=0; k < SCC_IRQTIMEOUT; k++)
+ {
+ Outb(Vector_Latch, 0); /* Generate INTACK */
+
+ /* Read the vector */
+ if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
+ if (vector & 0x01) break;
+
+ scc=&SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+
+ OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */
+ }
+ scc_sti(irq);
+
+ if (k == SCC_IRQTIMEOUT)
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+ return;
+ }
+
+ /* Find the SCC generating the interrupt by polling all attached SCCs
+ * reading RR3A (the interrupt pending register)
+ */
+
+ ctrl = SCC_ctrl;
+ while (ctrl->chan_A)
+ {
+ if (ctrl->irq != irq)
+ {
+ ctrl++;
+ continue;
+ }
+
+ scc = NULL;
+ for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+ {
+ vector=InReg(ctrl->chan_B,R2); /* Read the vector */
+ if (vector & 0x01) break;
+
+ scc = &SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+ }
+
+ if (k == SCC_IRQTIMEOUT)
+ {
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+ break;
+ }
+
+ /* This looks wierd and it is. At least the BayCom USCC doesn't
+ * use the Interrupt Daisy Chain, thus we'll have to start
+ * all over again to be sure not to miss an interrupt from
+ * (any of) the other chip(s)...
+ * Honestly, the situation *is* braindamaged...
+ */
+
+ if (scc != NULL)
+ {
+ OutReg(scc->ctrl,R0,RES_H_IUS);
+ ctrl = SCC_ctrl;
+ } else
+ ctrl++;
+ }
+
+ scc_sti(irq);
+}
+
+
+
+/* ******************************************************************** */
+/* * Init Channel */
+/* ******************************************************************** */
+
+
+/* ----> set SCC channel speed <---- */
+
+extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc)
+{
+ cl(scc,R14,BRENABL); /* disable baudrate generator */
+ wr(scc,R12,tc & 255); /* brg rate LOW */
+ wr(scc,R13,tc >> 8); /* brg rate HIGH */
+ or(scc,R14,BRENABL); /* enable baudrate generator */
+}
+
+extern __inline__ void set_speed(struct scc_channel *scc)
+{
+ disable_irq(scc->irq);
+
+ if (scc->modem.speed > 0) /* paranoia... */
+ set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+ enable_irq(scc->irq);
+}
+
+
+/* ----> initialize a SCC channel <---- */
+
+extern __inline__ void init_brg(struct scc_channel *scc)
+{
+ wr(scc, R14, BRSRC); /* BRG source = PCLK */
+ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
+ OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
+}
+
+/*
+ * Initialization according to the Z8530 manual (SGS-Thomson's version):
+ *
+ * 1. Modes and constants
+ *
+ * WR9 11000000 chip reset
+ * WR4 XXXXXXXX Tx/Rx control, async or sync mode
+ * WR1 0XX00X00 select W/REQ (optional)
+ * WR2 XXXXXXXX program interrupt vector
+ * WR3 XXXXXXX0 select Rx control
+ * WR5 XXXX0XXX select Tx control
+ * WR6 XXXXXXXX sync character
+ * WR7 XXXXXXXX sync character
+ * WR9 000X0XXX select interrupt control
+ * WR10 XXXXXXXX miscellaneous control (optional)
+ * WR11 XXXXXXXX clock control
+ * WR12 XXXXXXXX time constant lower byte (optional)
+ * WR13 XXXXXXXX time constant upper byte (optional)
+ * WR14 XXXXXXX0 miscellaneous control
+ * WR14 XXXSSSSS commands (optional)
+ *
+ * 2. Enables
+ *
+ * WR14 000SSSS1 baud rate enable
+ * WR3 SSSSSSS1 Rx enable
+ * WR5 SSSS1SSS Tx enable
+ * WR0 10000000 reset Tx CRG (optional)
+ * WR1 XSS00S00 DMA enable (optional)
+ *
+ * 3. Interrupt status
+ *
+ * WR15 XXXXXXXX enable external/status
+ * WR0 00010000 reset external status
+ * WR0 00010000 reset external status twice
+ * WR1 SSSXXSXX enable Rx, Tx and Ext/status
+ * WR9 000SXSSS enable master interrupt enable
+ *
+ * 1 = set to one, 0 = reset to zero
+ * X = user defined, S = same as previous init
+ *
+ *
+ * Note that the implementation differs in some points from above scheme.
+ *
+ */
+
+static void init_channel(struct scc_channel *scc)
+{
+ del_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
+
+ disable_irq(scc->irq);
+
+ wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
+ wr(scc,R1,0); /* no W/REQ operation */
+ wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
+ wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */
+ wr(scc,R6,0); /* SDLC address zero (not used) */
+ wr(scc,R7,FLAG); /* SDLC flag value */
+ wr(scc,R9,VIS); /* vector includes status */
+ wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+ wr(scc,R14, 0);
+
+
+/* set clock sources:
+
+ CLK_DPLL: normal halfduplex operation
+
+ RxClk: use DPLL
+ TxClk: use DPLL
+ TRxC mode DPLL output
+
+ CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
+
+ BayCom: others:
+
+ TxClk = pin RTxC TxClk = pin TRxC
+ RxClk = pin TRxC RxClk = pin RTxC
+
+
+ CLK_DIVIDER:
+ RxClk = use DPLL
+ TxClk = pin RTxC
+
+ BayCom: others:
+ pin TRxC = DPLL pin TRxC = BRG
+ (RxClk * 1) (RxClk * 32)
+*/
+
+
+ switch(scc->modem.clocksrc)
+ {
+ case CLK_DPLL:
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+ init_brg(scc);
+ break;
+
+ case CLK_DIVIDER:
+ wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+ init_brg(scc);
+ break;
+
+ case CLK_EXTERNAL:
+ wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+ OutReg(scc->ctrl, R14, DISDPLL);
+ break;
+
+ }
+
+ set_speed(scc); /* set baudrate */
+
+ if(scc->enhanced)
+ {
+ or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
+ wr(scc,R7,AUTOEOM);
+ }
+
+ if((InReg(scc->ctrl,R0)) & DCD) /* DCD is now ON */
+ {
+ if (scc->modem.clocksrc != CLK_EXTERNAL)
+ or(scc,R14, SEARCH);
+
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ }
+
+ /* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+ wr(scc,R15, BRKIE|TxUIE|DCDIE);
+ if (scc->kiss.softdcd)
+ or(scc,R15, SYNCIE);
+
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
+
+ or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+
+ scc->status = InReg(scc->ctrl,R0); /* read initial status */
+
+ or(scc,R9,MIE); /* master interrupt enable */
+
+ scc_init_timer(scc);
+
+ enable_irq(scc->irq);
+}
+
+
+
+
+/* ******************************************************************** */
+/* * SCC timer functions * */
+/* ******************************************************************** */
+
+
+/* ----> scc_key_trx sets the time constant for the baudrate
+ generator and keys the transmitter <---- */
+
+static void scc_key_trx(struct scc_channel *scc, char tx)
+{
+ unsigned int time_const;
+
+ if (scc->brand & PRIMUS)
+ Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
+
+ if (scc->modem.speed < 300)
+ scc->modem.speed = 1200;
+
+ time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
+
+ disable_irq(scc->irq);
+
+ if (tx)
+ {
+ or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */
+ or(scc, R15, TxUIE);
+ }
+
+ if (scc->modem.clocksrc == CLK_DPLL)
+ { /* force simplex operation */
+ if (tx)
+ {
+#ifdef CONFIG_SCC_TRXECHO
+ cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */
+ cl(scc, R15, DCDIE); /* No DCD changes, please */
+#endif
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
+ wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
+
+ or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
+ } else {
+ cl(scc,R5,RTS|TxENAB);
+
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+#ifdef CONFIG_SCC_TRXECHO
+ or(scc,R3,RxENABLE|ENT_HM);
+ or(scc,R15, DCDIE);
+#endif
+ }
+ } else {
+ if (tx)
+ {
+#ifdef CONFIG_SCC_TRXECHO
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ cl(scc, R3, RxENABLE);
+ cl(scc, R15, DCDIE);
+ }
+#endif
+
+
+ or(scc,R5,RTS|TxENAB); /* enable tx */
+ } else {
+ cl(scc,R5,RTS|TxENAB); /* disable tx */
+
+#ifdef CONFIG_SCC_TRXECHO
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ or(scc, R3, RxENABLE|ENT_HM);
+ or(scc, R15, DCDIE);
+ }
+#endif
+ }
+ }
+
+ enable_irq(scc->irq);
+}
+
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_t);
+
+ if (when == 0)
+ {
+ handler((unsigned long) scc);
+ } else
+ if (when != TIMER_OFF)
+ {
+ scc->tx_t.data = (unsigned long) scc;
+ scc->tx_t.function = handler;
+ scc->tx_t.expires = jiffies + (when*HZ)/100;
+ add_timer(&scc->tx_t);
+ }
+
+ restore_flags(flags);
+}
+
+static void scc_start_defer(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+
+ if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+ {
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = t_busy;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
+ add_timer(&scc->tx_wdog);
+ }
+ restore_flags(flags);
+}
+
+static void scc_start_maxkeyup(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+
+ if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+ {
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = t_maxkeyup;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+ add_timer(&scc->tx_wdog);
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void scc_tx_done(struct scc_channel *scc)
+{
+ /*
+ * trx remains keyed in fulldup mode 2 until t_idle expires.
+ */
+
+ switch (scc->kiss.fulldup)
+ {
+ case KISS_DUPLEX_LINK:
+ scc->stat.tx_state = TXS_IDLE2;
+ if (scc->kiss.idletime != TIMER_OFF)
+ scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
+ break;
+ case KISS_DUPLEX_OPTIMA:
+ scc_notify(scc, HWEV_ALL_SENT);
+ break;
+ default:
+ scc->stat.tx_state = TXS_BUSY;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+ }
+
+ scc_unlock_dev(scc);
+}
+
+
+static unsigned char Rand = 17;
+
+extern __inline__ int is_grouped(struct scc_channel *scc)
+{
+ int k;
+ struct scc_channel *scc2;
+ unsigned char grp1, grp2;
+
+ grp1 = scc->kiss.group;
+
+ for (k = 0; k < (Nchips * 2); k++)
+ {
+ scc2 = &SCC_Info[k];
+ grp2 = scc2->kiss.group;
+
+ if (scc2 == scc || !(scc2->dev && grp2))
+ continue;
+
+ if ((grp1 & 0x3f) == (grp2 & 0x3f))
+ {
+ if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
+ return 1;
+
+ if ( (grp1 & RXGROUP) && (scc2->status & DCD) )
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* DWAIT and SLOTTIME expired
+ *
+ * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer
+ * else key trx and start txdelay
+ * fulldup == 1: key trx and start txdelay
+ * fulldup == 2: mintime expired, reset status or key trx and start txdelay
+ */
+
+static void t_dwait(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+
+ if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */
+ {
+ if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */
+ {
+ scc->stat.tx_state = TXS_IDLE;
+ scc_unlock_dev(scc); /* t_maxkeyup locked it. */
+ return;
+ }
+
+ scc->stat.tx_state = TXS_BUSY;
+ }
+
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ Rand = Rand * 17 + 31;
+
+ if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+ {
+ scc_start_defer(scc);
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
+ return ;
+ }
+ }
+
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+ } else {
+ scc_start_tx_timer(scc, t_txdelay, 0);
+ }
+}
+
+
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
+
+static void t_txdelay(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+
+ scc_start_maxkeyup(scc);
+
+ if (scc->tx_buff == NULL)
+ {
+ disable_irq(scc->irq);
+ scc_txint(scc);
+ enable_irq(scc->irq);
+ }
+}
+
+
+/* TAILTIME expired
+ *
+ * switch off transmitter. If we were stopped by Maxkeyup restart
+ * transmission after 'mintime' seconds
+ */
+
+static void t_tail(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+ scc_key_trx(scc, TX_OFF);
+
+ restore_flags(flags);
+
+ if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */
+ {
+ scc->stat.tx_state = TXS_WAIT;
+
+ if (scc->kiss.mintime != TIMER_OFF) /* try it again */
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ else
+ scc_start_tx_timer(scc, t_dwait, 0);
+ return;
+ }
+
+ scc->stat.tx_state = TXS_IDLE;
+ scc_unlock_dev(scc);
+}
+
+
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void t_busy(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+
+ del_timer(&scc->tx_t);
+ scc_lock_dev(scc);
+
+ scc_discard_buffers(scc);
+
+ scc->stat.txerrs++;
+ scc->stat.tx_state = TXS_IDLE;
+
+ scc_unlock_dev(scc);
+}
+
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
+
+static void t_maxkeyup(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * let things settle down before we start to
+ * accept new data.
+ */
+
+ scc_lock_dev(scc);
+ scc_discard_buffers(scc);
+
+ del_timer(&scc->tx_t);
+
+ cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */
+ cl(scc, R15, TxUIE); /* count it. */
+ OutReg(scc->ctrl, R0, RES_Tx_P);
+
+ restore_flags(flags);
+
+ scc->stat.txerrs++;
+ scc->stat.tx_state = TXS_TIMEOUT;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+}
+
+/* IDLE timeout
+ *
+ * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
+ * of inactivity. We will not restart transmission before 'mintime'
+ * expires.
+ */
+
+static void t_idle(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+
+ del_timer(&scc->tx_wdog);
+
+ scc_key_trx(scc, TX_OFF);
+
+ if (scc->kiss.mintime != TIMER_OFF)
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ scc->stat.tx_state = TXS_WAIT;
+}
+
+static void scc_init_timer(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ scc->stat.tx_state = TXS_IDLE;
+
+ restore_flags(flags);
+}
+
+
+/* ******************************************************************** */
+/* * Set/get L1 parameters * */
+/* ******************************************************************** */
+
+
+/*
+ * this will set the "hardware" parameters through KISS commands or ioctl()
+ */
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
+{
+ int dcd;
+
+ switch (cmd)
+ {
+ case PARAM_TXDELAY: scc->kiss.txdelay=arg; break;
+ case PARAM_PERSIST: scc->kiss.persist=arg; break;
+ case PARAM_SLOTTIME: scc->kiss.slottime=arg; break;
+ case PARAM_TXTAIL: scc->kiss.tailtime=arg; break;
+ case PARAM_FULLDUP: scc->kiss.fulldup=arg; break;
+ case PARAM_DTR: break; /* does someone need this? */
+ case PARAM_GROUP: scc->kiss.group=arg; break;
+ case PARAM_IDLE: scc->kiss.idletime=arg; break;
+ case PARAM_MIN: scc->kiss.mintime=arg; break;
+ case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break;
+ case PARAM_WAIT: scc->kiss.waittime=arg; break;
+ case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break;
+ case PARAM_TX: scc->kiss.tx_inhibit=arg; break;
+
+ case PARAM_SOFTDCD:
+ scc->kiss.softdcd=arg;
+ if (arg)
+ or(scc, R15, SYNCIE);
+ else
+ cl(scc, R15, SYNCIE);
+ break;
+
+ case PARAM_SPEED:
+ if (arg < 256)
+ scc->modem.speed=arg*100;
+ else
+ scc->modem.speed=arg;
+
+ if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
+ set_speed(scc);
+ break;
+
+ case PARAM_RTS:
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ if (arg != TX_OFF)
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+ } else {
+ if (arg == TX_OFF)
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+ }
+ }
+ break;
+
+ case PARAM_HWEVENT:
+ dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
+ scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+ break;
+
+ default: return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
+{
+ switch (cmd)
+ {
+ case PARAM_TXDELAY: return CAST(scc->kiss.txdelay);
+ case PARAM_PERSIST: return CAST(scc->kiss.persist);
+ case PARAM_SLOTTIME: return CAST(scc->kiss.slottime);
+ case PARAM_TXTAIL: return CAST(scc->kiss.tailtime);
+ case PARAM_FULLDUP: return CAST(scc->kiss.fulldup);
+ case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd);
+ case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0);
+ case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0);
+ case PARAM_SPEED: return CAST(scc->modem.speed);
+ case PARAM_GROUP: return CAST(scc->kiss.group);
+ case PARAM_IDLE: return CAST(scc->kiss.idletime);
+ case PARAM_MIN: return CAST(scc->kiss.mintime);
+ case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup);
+ case PARAM_WAIT: return CAST(scc->kiss.waittime);
+ case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer);
+ case PARAM_TX: return CAST(scc->kiss.tx_inhibit);
+ default: return NO_SUCH_PARAM;
+ }
+
+}
+
+#undef CAST
+#undef SVAL
+
+/* ******************************************************************* */
+/* * Send calibration pattern * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+ scc_key_trx(scc, TX_OFF);
+ wr(scc, R6, 0);
+ wr(scc, R7, FLAG);
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT);
+
+ scc_unlock_dev(scc);
+
+ restore_flags(flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ scc_lock_dev(scc);
+ scc_discard_buffers(scc);
+
+ del_timer(&scc->tx_wdog);
+
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = scc_stop_calibrate;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+ add_timer(&scc->tx_wdog);
+
+ wr(scc, R6, 0);
+ wr(scc, R7, pattern);
+
+ /*
+ * Don't know if this works.
+ * Damn, where is my Z8530 programming manual...?
+ */
+
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT);
+
+ scc_key_trx(scc, TX_ON);
+
+ restore_flags(flags);
+}
+
+/* ******************************************************************* */
+/* * Init channel structures, special HW, etc... * */
+/* ******************************************************************* */
+
+/*
+ * Reset the Z8530s and setup special hardware
+ */
+
+static void z8530_init(void)
+{
+ struct scc_channel *scc;
+ int chip, k;
+ unsigned long flags;
+ char *flag;
+
+
+ printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
+
+ flag=" ";
+ for (k = 0; k < 16; k++)
+ if (Ivec[k].used)
+ {
+ printk("%s%d", flag, k);
+ flag=",";
+ }
+ printk("\n");
+
+
+ /* reset and pre-init all chips in the system */
+ for (chip = 0; chip < Nchips; chip++)
+ {
+ scc=&SCC_Info[2*chip];
+ if (!scc->ctrl) continue;
+
+ /* Special SCC cards */
+
+ if(scc->brand & EAGLE) /* this is an EAGLE card */
+ Outb(scc->special,0x08); /* enable interrupt on the board */
+
+ if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */
+ Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */
+
+
+ /* Reset and pre-init Z8530 */
+
+ save_flags(flags);
+ cli();
+
+ Outb(scc->ctrl, 0);
+ OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */
+ udelay(100); /* give it 'a bit' more time than required */
+ wr(scc, R2, chip*16); /* interrupt vector */
+ wr(scc, R9, VIS); /* vector includes status */
+
+ restore_flags(flags);
+ }
+
+
+ Driver_Initialized = 1;
+}
+
+/*
+ * Allocate device structure, err, instance, and register driver
+ */
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name)
+{
+ unsigned char *buf;
+ struct device *dev;
+
+ if (dev_get(name) != NULL)
+ {
+ printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+ return -EEXIST;
+ }
+
+ if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ dev = scc->dev;
+ memset(dev, 0, sizeof(struct device));
+
+ buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+ strcpy(buf, name);
+
+ dev->priv = (void *) scc;
+ dev->name = buf;
+ dev->init = scc_net_init;
+
+ if (register_netdev(dev) != 0)
+ {
+ kfree(dev);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+
+/* ******************************************************************** */
+/* * Network driver methods * */
+/* ******************************************************************** */
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* ----> Initialize device <----- */
+
+static int scc_net_init(struct device *dev)
+{
+ dev_init_buffers(dev);
+
+ dev->tx_queue_len = 16; /* should be enough... */
+
+ dev->open = scc_net_open;
+ dev->stop = scc_net_close;
+
+ dev->hard_start_xmit = scc_net_tx;
+ dev->hard_header = scc_net_header;
+ dev->rebuild_header = ax25_rebuild_header;
+ dev->set_mac_address = scc_net_set_mac_address;
+ dev->get_stats = scc_net_get_stats;
+ dev->do_ioctl = scc_net_ioctl;
+
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+
+ dev->flags = 0;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ return 0;
+}
+
+/* ----> open network device <---- */
+
+static int scc_net_open(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -ENODEV;
+
+ if (!scc->init)
+ return -EINVAL;
+
+ MOD_INC_USE_COUNT;
+
+ scc->tx_buff = NULL;
+ skb_queue_head_init(&scc->tx_queue);
+
+ init_channel(scc);
+
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ return 0;
+}
+
+/* ----> close network device <---- */
+
+static int scc_net_close(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -ENODEV;
+
+ MOD_DEC_USE_COUNT;
+
+ save_flags(flags);
+ cli();
+
+ Outb(scc->ctrl,0); /* Make sure pointer is written */
+ wr(scc,R1,0); /* disable interrupts */
+ wr(scc,R3,0);
+
+ del_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
+
+ restore_flags(flags);
+
+ scc_discard_buffers(scc);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
+{
+ if (skb->len == 0)
+ {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ scc->dev_stat.rx_packets++;
+
+ skb->dev = scc->dev;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+
+ netif_rx(skb);
+ return;
+}
+
+/* ----> transmit frame <---- */
+
+static int scc_net_tx(struct sk_buff *skb, struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+ char kisscmd;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy)
+ {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ if (skb->len > scc->stat.bufsize || skb->len < 2)
+ {
+ scc->dev_stat.tx_dropped++; /* bogus frame */
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ scc->dev_stat.tx_packets++;
+ scc->stat.txframes++;
+
+ kisscmd = *skb->data & 0x1f;
+ skb_pull(skb, 1);
+
+ if (kisscmd)
+ {
+ scc_set_param(scc, kisscmd, *skb->data);
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
+ {
+ struct sk_buff *skb_del;
+ skb_del = __skb_dequeue(&scc->tx_queue);
+ dev_kfree_skb(skb_del, FREE_WRITE);
+ }
+ __skb_queue_tail(&scc->tx_queue, skb);
+
+ dev->trans_start = jiffies;
+
+ /*
+ * Start transmission if the trx state is idle or
+ * t_idle hasn't expired yet. Use dwait/persistance/slottime
+ * algorithm for normal halfduplex operation.
+ */
+
+ if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
+ else
+ scc_start_tx_timer(scc, t_dwait, 0);
+ }
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+/* ----> ioctl functions <---- */
+
+/*
+ * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg
+ * SIOCSCCINI - initialize driver arg: ---
+ * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg
+ * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg
+ * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg
+ * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg
+ */
+
+static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct scc_kiss_cmd kiss_cmd;
+ struct scc_mem_config memcfg;
+ struct scc_hw_config hwcfg;
+ struct scc_calibrate cal;
+ int chan;
+ unsigned char device_name[10];
+ void *arg;
+ struct scc_channel *scc;
+
+ scc = (struct scc_channel *) dev->priv;
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -EINVAL;
+
+ arg = (void *) ifr->ifr_data;
+
+ if (!Driver_Initialized)
+ {
+ if (cmd == SIOCSCCCFG)
+ {
+ int found = 1;
+
+ if (!suser()) return -EPERM;
+ if (!arg) return -EFAULT;
+
+ if (Nchips >= MAXSCC)
+ return -EINVAL;
+
+ if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
+ return -EFAULT;
+
+ if (hwcfg.irq == 2) hwcfg.irq = 9;
+
+ if (!Ivec[hwcfg.irq].used && hwcfg.irq)
+ {
+ if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
+ printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
+ else
+ Ivec[hwcfg.irq].used = 1;
+ }
+
+ if (hwcfg.vector_latch)
+ Vector_Latch = hwcfg.vector_latch;
+
+ if (hwcfg.clock == 0)
+ hwcfg.clock = DEFAULT_CLOCK;
+
+#ifndef DONT_CHECK
+ disable_irq(hwcfg.irq);
+
+ check_region(scc->ctrl, 1);
+ Outb(hwcfg.ctrl_a, 0);
+ udelay(5);
+ OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */
+ udelay(5);
+
+ if (InReg(hwcfg.ctrl_a,R13) != 0x55)
+ found = 0;
+
+ enable_irq(hwcfg.irq);
+#endif
+
+ if (found)
+ {
+ SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a;
+ SCC_Info[2*Nchips ].data = hwcfg.data_a;
+ SCC_Info[2*Nchips ].irq = hwcfg.irq;
+ SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
+ SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+ SCC_Info[2*Nchips+1].irq = hwcfg.irq;
+
+ SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+ SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+ SCC_ctrl[Nchips].irq = hwcfg.irq;
+ }
+
+
+ for (chan = 0; chan < 2; chan++)
+ {
+ sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+
+ SCC_Info[2*Nchips+chan].special = hwcfg.special;
+ SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
+ SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
+ SCC_Info[2*Nchips+chan].option = hwcfg.option;
+ SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
+
+#ifdef DONT_CHECK
+ printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n",
+ device_name,
+ SCC_Info[2*Nchips+chan].data,
+ SCC_Info[2*Nchips+chan].ctrl);
+
+#else
+ printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n",
+ device_name,
+ chan? hwcfg.data_b : hwcfg.data_a,
+ chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
+ found? "found" : "missing");
+#endif
+
+ if (found)
+ {
+ request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
+ request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+ if (Nchips+chan != 0)
+ scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
+ }
+ }
+
+ if (found) Nchips++;
+
+ return 0;
+ }
+
+ if (cmd == SIOCSCCINI)
+ {
+ if (!suser())
+ return -EPERM;
+
+ if (Nchips == 0)
+ return -EINVAL;
+
+ z8530_init();
+ return 0;
+ }
+
+ return -EINVAL; /* confuse the user */
+ }
+
+ if (!scc->init)
+ {
+ if (cmd == SIOCSCCCHANINI)
+ {
+ if (!suser()) return -EPERM;
+ if (!arg) return -EINVAL;
+
+ scc->stat.bufsize = BUFSIZE;
+
+ if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
+ return -EINVAL;
+
+ /* default KISS Params */
+
+ if (scc->modem.speed < 4800)
+ {
+ scc->kiss.txdelay = 36; /* 360 ms */
+ scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 16; /* 160 ms */
+ scc->kiss.tailtime = 4; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50; /* 500 ms */
+ scc->kiss.maxkeyup = 10; /* 10 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.softdcd = 0; /* hardware dcd */
+ } else {
+ scc->kiss.txdelay = 10; /* 100 ms */
+ scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 8; /* 160 ms */
+ scc->kiss.tailtime = 1; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50; /* 500 ms */
+ scc->kiss.maxkeyup = 7; /* 7 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.softdcd = 0; /* hardware dcd */
+ }
+
+ scc->tx_buff = NULL;
+ skb_queue_head_init(&scc->tx_queue);
+ scc->init = 1;
+
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ switch(cmd)
+ {
+ case SIOCSCCRESERVED:
+ return -ENOIOCTLCMD;
+
+ case SIOCSCCSMEM:
+ if (!suser()) return -EPERM;
+ if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
+ return -EINVAL;
+ scc->stat.bufsize = memcfg.bufsize;
+ return 0;
+
+ case SIOCSCCGSTAT:
+ if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
+ return -EINVAL;
+ return 0;
+
+ case SIOCSCCGKISS:
+ if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+ return -EINVAL;
+ kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
+ if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
+ return -EINVAL;
+ return 0;
+
+ case SIOCSCCSKISS:
+ if (!suser()) return -EPERM;
+ if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+ return -EINVAL;
+ return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
+
+ case SIOCSCCCAL:
+ if (!suser()) return -EPERM;
+ if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
+ return -EINVAL;
+
+ scc_start_calibrate(scc, cal.time, cal.pattern);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+
+ }
+
+ return -EINVAL;
+}
+
+/* ----> set interface callsign <---- */
+
+static int scc_net_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+/* ----> "hard" header <---- */
+
+static int scc_net_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* ----> get statistics <---- */
+
+static struct net_device_stats *scc_net_get_stats(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return NULL;
+
+ scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+ scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+ scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+ scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+ return &scc->dev_stat;
+}
+
+/* ******************************************************************** */
+/* * dump statistics to /proc/net/z8530drv * */
+/* ******************************************************************** */
+
+
+static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct scc_channel *scc;
+ struct scc_kiss *kiss;
+ struct scc_stat *stat;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ int k;
+
+ len += sprintf(buffer, "z8530drv-"VERSION"\n");
+
+ if (!Driver_Initialized)
+ {
+ len += sprintf(buffer+len, "not initialized\n");
+ goto done;
+ }
+
+ if (!Nchips)
+ {
+ len += sprintf(buffer+len, "chips missing\n");
+ goto done;
+ }
+
+ for (k = 0; k < Nchips*2; k++)
+ {
+ scc = &SCC_Info[k];
+ stat = &scc->stat;
+ kiss = &scc->kiss;
+
+ if (!scc->init)
+ continue;
+
+ /* dev data ctrl irq clock brand enh vector special option
+ * baud nrz clocksrc softdcd bufsize
+ * rxints txints exints spints
+ * rcvd rxerrs over / xmit txerrs under / nospace bufsize
+ * txd pers slot tail ful wait min maxk idl defr txof grp
+ * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
+ * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
+ */
+
+ len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
+ scc->dev->name,
+ scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
+ scc->enhanced, Vector_Latch, scc->special,
+ scc->option);
+ len += sprintf(buffer+len, "\t%lu %d %d %d %d\n",
+ scc->modem.speed, scc->modem.nrz,
+ scc->modem.clocksrc, kiss->softdcd,
+ stat->bufsize);
+ len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n",
+ stat->rxints, stat->txints, stat->exints, stat->spints);
+ len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
+ stat->rxframes, stat->rxerrs, stat->rx_over,
+ stat->txframes, stat->txerrs, stat->tx_under,
+ stat->nospace, stat->tx_state);
+
+#define K(x) kiss->x
+ len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n",
+ K(txdelay), K(persist), K(slottime), K(tailtime),
+ K(fulldup), K(waittime), K(mintime), K(maxkeyup),
+ K(idletime), K(maxdefer), K(tx_inhibit), K(group));
+#undef K
+#ifdef SCC_DEBUG
+ {
+ int reg;
+
+ len += sprintf(buffer+len, "\tW ");
+ for (reg = 0; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+ len += sprintf(buffer+len, "\n");
+
+ len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+ for (reg = 3; reg < 8; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "XX ");
+ for (reg = 9; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "\n");
+ }
+#endif
+ len += sprintf(buffer+len, "\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+done:
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+#ifdef CONFIG_PROC_FS
+
+struct proc_dir_entry scc_proc_dir_entry =
+{
+ PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0,
+ &proc_net_inode_operations, scc_net_get_info
+};
+
+#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry);
+#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
+#else
+#define scc_net_procfs_init()
+#define scc_net_procfs_remove()
+#endif
+
+
+/* ******************************************************************** */
+/* * Init SCC driver * */
+/* ******************************************************************** */
+
+__initfunc(int scc_init (void))
+{
+ int chip, chan, k, result;
+ char devname[10];
+
+ printk(KERN_INFO BANNER);
+
+ memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
+
+ /* pre-init channel information */
+
+ for (chip = 0; chip < MAXSCC; chip++)
+ {
+ memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel));
+ memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
+
+ for (chan = 0; chan < 2; chan++)
+ SCC_Info[2*chip+chan].magic = SCC_MAGIC;
+ }
+
+ for (k = 0; k < 16; k++) Ivec[k].used = 0;
+
+ sprintf(devname,"%s0", SCC_DriverName);
+
+ result = scc_net_setup(SCC_Info, devname);
+ if (result)
+ {
+ printk(KERN_ERR "z8530drv: cannot initialize module\n");
+ return result;
+ }
+
+ scc_net_procfs_init();
+
+ return 0;
+}
+
+/* ******************************************************************** */
+/* * Module support * */
+/* ******************************************************************** */
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ int result = 0;
+
+ result = scc_init();
+
+ if (result == 0)
+ printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n");
+
+ return result;
+}
+
+void cleanup_module(void)
+{
+ long flags;
+ io_port ctrl;
+ int k;
+ struct scc_channel *scc;
+
+ save_flags(flags);
+ cli();
+
+ if (Nchips == 0)
+ unregister_netdev(SCC_Info[0].dev);
+
+ for (k = 0; k < Nchips; k++)
+ if ( (ctrl = SCC_ctrl[k].chan_A) )
+ {
+ Outb(ctrl, 0);
+ OutReg(ctrl,R9,FHWRES); /* force hardware reset */
+ udelay(50);
+ }
+
+ for (k = 0; k < Nchips*2; k++)
+ {
+ scc = &SCC_Info[k];
+ if (scc)
+ {
+ release_region(scc->ctrl, 1);
+ release_region(scc->data, 1);
+ if (scc->dev)
+ {
+ unregister_netdev(scc->dev);
+ kfree(scc->dev);
+ }
+ }
+ }
+
+ for (k=0; k < 16 ; k++)
+ if (Ivec[k].used) free_irq(k, NULL);
+
+ restore_flags(flags);
+
+ scc_net_procfs_remove();
+}
+#endif
diff --git a/drivers/net/hamradio/soundmodem/.cvsignore b/drivers/net/hamradio/soundmodem/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/net/hamradio/soundmodem/Makefile b/drivers/net/hamradio/soundmodem/Makefile
new file mode 100644
index 000000000..1aabdd9e8
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/Makefile
@@ -0,0 +1,60 @@
+#
+# Makefile for the soundmodem 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 := soundmodem.o
+
+O_OBJS := sm.o
+ifeq ($(CONFIG_SOUNDMODEM_SBC),y)
+O_OBJS += sm_sbc.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_WSS),y)
+O_OBJS += sm_wss.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y)
+O_OBJS += sm_afsk1200.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y)
+O_OBJS += sm_afsk2400_7.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y)
+O_OBJS += sm_afsk2400_8.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y)
+O_OBJS += sm_afsk2666.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y)
+O_OBJS += sm_hapn4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y)
+O_OBJS += sm_psk4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y)
+O_OBJS += sm_fsk9600.o
+endif
+
+M_OBJS := $(O_TARGET)
+
+all: all_targets
+.PHONY: all
+
+gentbl: gentbl.c
+ $(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h
+TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h
+TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h
+
+$(TBLHDR): gentbl
+ ./gentbl
+
+fastdep: $(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/net/hamradio/soundmodem/gentbl.c b/drivers/net/hamradio/soundmodem/gentbl.c
new file mode 100644
index 000000000..d1dce6687
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/gentbl.c
@@ -0,0 +1,676 @@
+/*****************************************************************************/
+
+/*
+ * gentbl.c -- soundcard radio modem driver table generator.
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_offscostab(FILE *f, unsigned int nbits)
+{
+ int i;
+
+ fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
+ "#define OFFSCOSTABBITS %u\n"
+ "#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n",
+ nbits);
+ fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
+ for (i = 0; i < (1<<nbits); i++) {
+ fprintf(f, "%4u", (int)
+ (128+127.0*cos(i*2.0*M_PI/(1<<nbits))));
+ if (i < (1<<nbits)-1)
+ fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
+ }
+ fprintf(f, "\n};\n\n"
+ "#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
+ 16-nbits, (1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_costab(FILE *f, unsigned int nbits)
+{
+ int i;
+
+ fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
+ "static const short costab[%d] = {", (1<<nbits));
+ for (i = 0; i < (1<<nbits); i++) {
+ if (!(i & 7))
+ fprintf(f, "\n\t");
+ fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits))));
+ if (i != ((1<<nbits)-1))
+ fprintf(f, ", ");
+ }
+ fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
+ "#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits,
+ (1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK12_SAMPLE_RATE 9600
+#define AFSK12_TX_FREQ_LO 1200
+#define AFSK12_TX_FREQ_HI 2200
+#define AFSK12_CORRLEN 8
+
+static void gentbl_afsk1200(FILE *f)
+{
+ int i, v, sum;
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
+
+ fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
+ "#define AFSK12_SAMPLE_RATE %u\n"
+ "#define AFSK12_TX_FREQ_LO %u\n"
+ "#define AFSK12_TX_FREQ_HI %u\n"
+ "#define AFSK12_CORRLEN %u\n\n",
+ AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO,
+ AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
+ fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
+ for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+ sum += (v = 127.0*cos(ARGLO(i)));
+ fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n"
+ "static const int afsk12_tx_lo_q[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+ sum += (v = 127.0*sin(ARGLO(i)));
+ fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n"
+ "static const int afsk12_tx_hi_i[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+ sum += (v = 127.0*cos(ARGHI(i)));
+ fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n"
+ "static const int afsk12_tx_hi_q[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+ sum += (v = 127.0*sin(ARGHI(i)));
+ fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+}
+
+/* -------------------------------------------------------------------- */
+
+static const float fsk96_tx_coeff_4[32] = {
+ -0.001152, 0.000554, 0.002698, 0.002753,
+ -0.002033, -0.008861, -0.008002, 0.006607,
+ 0.023727, 0.018905, -0.018056, -0.057957,
+ -0.044368, 0.055683, 0.207667, 0.322048,
+ 0.322048, 0.207667, 0.055683, -0.044368,
+ -0.057957, -0.018056, 0.018905, 0.023727,
+ 0.006607, -0.008002, -0.008861, -0.002033,
+ 0.002753, 0.002698, 0.000554, -0.001152
+};
+
+static const float fsk96_tx_coeff_5[40] = {
+ -0.001009, -0.000048, 0.001376, 0.002547,
+ 0.002061, -0.001103, -0.005795, -0.008170,
+ -0.004017, 0.006924, 0.018225, 0.019238,
+ 0.002925, -0.025777, -0.048064, -0.039683,
+ 0.013760, 0.104144, 0.200355, 0.262346,
+ 0.262346, 0.200355, 0.104144, 0.013760,
+ -0.039683, -0.048064, -0.025777, 0.002925,
+ 0.019238, 0.018225, 0.006924, -0.004017,
+ -0.008170, -0.005795, -0.001103, 0.002061,
+ 0.002547, 0.001376, -0.000048, -0.001009
+};
+
+#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
+
+static inline float hamming(float x)
+{
+ return 0.54-0.46*cos(2*M_PI*x);
+}
+
+static inline float sinc(float x)
+{
+ if (x == 0)
+ return 1;
+ x *= M_PI;
+ return sin(x)/x;
+}
+
+static void gentbl_fsk9600(FILE *f)
+{
+ int i, j, k, l, m;
+ float s;
+ float c[44];
+ float min, max;
+
+ fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+#if 0
+ memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
+#else
+ for (i = 0; i < 29; i++)
+ c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
+#endif
+ fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 256; j++) {
+ for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+ if (j & k) {
+ for (m = 0; m < 4; m++, l++)
+ s += c[l];
+ } else {
+ for (m = 0; m < 4; m++, l++)
+ s -= c[l];
+ }
+ }
+ s *= 0.75;
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 3 || j < 255)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+#if 0
+ memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
+#else
+ for (i = 0; i < 36; i++)
+ c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
+#endif
+ fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
+ for (i = 0; i < 5; i++) {
+ for (j = 0; j < 256; j++) {
+ for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+ if (j & k) {
+ for (m = 0; m < 5; m++, l++)
+ s += c[l];
+ } else {
+ for (m = 0; m < 5; m++, l++)
+ s -= c[l];
+ }
+ }
+ s *= 0.75;
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 4 || j < 255)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK26_SAMPLERATE 16000
+
+#define AFSK26_NUMCAR 2
+#define AFSK26_FIRSTCAR 2000
+#define AFSK26_MSK_LEN 6
+#define AFSK26_RXOVER 2
+
+#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
+
+#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
+
+#define AFSK26_AMPL(x) (((x)?1.0:0.7))
+
+#undef AFSK26_AMPL
+#define AFSK26_AMPL(x) 1
+
+static void gentbl_afsk2666(FILE *f)
+{
+ int i, j, k, l, o, v, sumi, sumq;
+ float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
+ int cfreq[AFSK26_NUMCAR];
+
+ fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
+ "#define AFSK26_DEMCORRLEN %d\n"
+ "#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
+ AFSK26_SAMPLERATE);
+ fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
+ AFSK26_NUMCAR);
+ for (i = 0; i < AFSK26_NUMCAR; i++) {
+ cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
+ 0x10000*i/AFSK26_MSK_LEN/2;
+ fprintf(f, "0x%x", cfreq[i]);
+ if (i < AFSK26_NUMCAR-1)
+ fprintf(f, ", ");
+ }
+ fprintf(f, " };\n\n");
+ for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
+ window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
+ AFSK26_RXOVER)) * 127.0;
+ fprintf(f, "\nstatic const struct {\n\t"
+ "int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n",
+ AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR);
+ for (o = AFSK26_RXOVER-1; o >= 0; o--) {
+ fprintf(f, "\t{\n");
+ for (i = 0; i < AFSK26_NUMCAR; i++) {
+ j = cfreq[i];
+ fprintf(f, "\t\t{{ ");
+ for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0;
+ l--, k = (k+j)&0xffffu) {
+ sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+ cos(M_PI*k/32768.0));
+ fprintf(f, "%6d%s", v, l ? ", " : " }, { ");
+ }
+ for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0;
+ l--, k = (k+j)&0xffffu) {
+ sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+ sin(M_PI*k/32768.0));
+ fprintf(f, "%6d%s", v, l ? ", " : " }}");
+ }
+ if (i < 1)
+ fprintf(f, ",");
+ fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n"
+ "#define AFSK26_DEM_SUM_Q_%d_%d %d\n",
+ AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq);
+ }
+ fprintf(f, "\t}%s\n", o ? "," : "");
+ }
+ fprintf(f, "};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+#define ATAN_TABLEN 1024
+
+static void gentbl_atantab(FILE *f)
+{
+ int i;
+ short x;
+
+ fprintf(f, "\n/*\n"
+ " * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
+ " */\n""#define ATAN_TABLEN %d\n\n"
+ "static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
+ ATAN_TABLEN);
+ for (i = 0; i <= ATAN_TABLEN; i++) {
+ if (!(i & 7))
+ fprintf(f, "\n\t");
+ x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
+ fprintf(f, "%6d, ", x);
+ }
+ fprintf(f, "%6d\n};\n\n", x);
+
+}
+
+/* -------------------------------------------------------------------- */
+
+#define PSK48_TXF_OVERSAMPLING 5
+#define PSK48_TXF_NUMSAMPLES 16
+#define PSK48_RXF_LEN 64
+
+static const float psk48_tx_coeff[80] = {
+ -0.000379, -0.000640, -0.000000, 0.000772,
+ 0.000543, -0.000629, -0.001187, -0.000000,
+ 0.001634, 0.001183, -0.001382, -0.002603,
+ -0.000000, 0.003481, 0.002472, -0.002828,
+ -0.005215, -0.000000, 0.006705, 0.004678,
+ -0.005269, -0.009584, -0.000000, 0.012065,
+ 0.008360, -0.009375, -0.017028, -0.000000,
+ 0.021603, 0.015123, -0.017229, -0.032012,
+ -0.000000, 0.043774, 0.032544, -0.040365,
+ -0.084963, -0.000000, 0.201161, 0.374060,
+ 0.374060, 0.201161, -0.000000, -0.084963,
+ -0.040365, 0.032544, 0.043774, -0.000000,
+ -0.032012, -0.017229, 0.015123, 0.021603,
+ -0.000000, -0.017028, -0.009375, 0.008360,
+ 0.012065, -0.000000, -0.009584, -0.005269,
+ 0.004678, 0.006705, -0.000000, -0.005215,
+ -0.002828, 0.002472, 0.003481, -0.000000,
+ -0.002603, -0.001382, 0.001183, 0.001634,
+ -0.000000, -0.001187, -0.000629, 0.000543,
+ 0.000772, -0.000000, -0.000640, -0.000379
+};
+
+static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
+ -0.000219, 0.000360, 0.000873, 0.001080,
+ 0.000747, -0.000192, -0.001466, -0.002436,
+ -0.002328, -0.000699, 0.002101, 0.004809,
+ 0.005696, 0.003492, -0.001633, -0.007660,
+ -0.011316, -0.009627, -0.001780, 0.009712,
+ 0.019426, 0.021199, 0.011342, -0.008583,
+ -0.030955, -0.044093, -0.036634, -0.002651,
+ 0.054742, 0.123101, 0.184198, 0.220219,
+ 0.220219, 0.184198, 0.123101, 0.054742,
+ -0.002651, -0.036634, -0.044093, -0.030955,
+ -0.008583, 0.011342, 0.021199, 0.019426,
+ 0.009712, -0.001780, -0.009627, -0.011316,
+ -0.007660, -0.001633, 0.003492, 0.005696,
+ 0.004809, 0.002101, -0.000699, -0.002328,
+ -0.002436, -0.001466, -0.000192, 0.000747,
+ 0.001080, 0.000873, 0.000360, -0.000219
+};
+
+static void gentbl_psk4800(FILE *f)
+{
+ int i, j, k;
+ short x;
+
+ fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
+ "#define PSK48_TXF_OVERSAMPLING %d\n"
+ "#define PSK48_TXF_NUMSAMPLES %d\n\n"
+ "#define PSK48_SAMPLERATE 8000\n"
+ "#define PSK48_CAR_FREQ 2000\n"
+ "#define PSK48_PSK_LEN 5\n"
+ "#define PSK48_RXF_LEN %u\n"
+ "#define PSK48_PHASEINC (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
+ "#define PSK48_SPHASEINC (0x10000/(2*PSK48_PSK_LEN))\n\n"
+ "static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
+ "PSK48_TXF_NUMSAMPLES*8*2] = {",
+ PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
+ for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
+ for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
+ fprintf(f, "\n\t");
+ for (k = 0; k < 8; k++) {
+ x = 32767.0 * cos(k*M_PI/4.0) *
+ psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+ fprintf(f, "%6d, ", x);
+ }
+ fprintf(f, "\n\t");
+ for (k = 0; k < 8; k++) {
+ x = 32767.0 * sin(k*M_PI/4.0) *
+ psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+ fprintf(f, "%6d", x);
+ if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 ||
+ i != PSK48_TXF_OVERSAMPLING-1)
+ fprintf(f, ", ");
+ }
+ }
+ }
+ fprintf(f, "\n};\n\n");
+
+ fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
+ for (i = 0; i < PSK48_RXF_LEN; i++) {
+ fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
+ if (i < PSK48_RXF_LEN-1)
+ fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
+ }
+ fprintf(f, "\n};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_hapn4800(FILE *f)
+{
+ int i, j, k, l;
+ float s;
+ float c[40];
+ float min, max;
+
+ fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
+ /*
+ * firstly generate tables for the FM transmitter modulator
+ */
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+ for (i = 0; i < 24; i++)
+ c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
+ for (i = 0; i < 24; i++)
+ c[i] -= c[i+8];
+ fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 16; j++) {
+ for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+ if (j & k)
+ s += c[l];
+ else
+ s -= c[l];
+ }
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 7 || j < 15)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+ for (i = 0; i < 30; i++)
+ c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
+ for (i = 0; i < 30; i++)
+ c[i] -= c[i+10];
+ fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
+ for (i = 0; i < 10; i++) {
+ for (j = 0; j < 16; j++) {
+ for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+ if (j & k)
+ s += c[l];
+ else
+ s -= c[l];
+ }
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 9 || j < 15)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+ /*
+ * secondly generate tables for the PM transmitter modulator
+ */
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+ for (i = 0; i < 25; i++)
+ c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
+ for (i = 0; i < 25; i++)
+ for (j = 1; j < 8; j++)
+ c[i] += c[i+j];
+ fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 16; j++) {
+ for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+ if (j & k)
+ s += c[l];
+ else
+ s -= c[l];
+ }
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 7 || j < 15)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+ min = max = 0;
+ memset(c, 0, sizeof(c));
+ for (i = 0; i < 31; i++)
+ c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
+ for (i = 0; i < 31; i++)
+ for (j = 1; j < 10; j++)
+ c[i] += c[i+j];
+ fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
+ for (i = 0; i < 10; i++) {
+ for (j = 0; j < 16; j++) {
+ for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+ if (j & k)
+ s += c[l];
+ else
+ s -= c[l];
+ }
+ if (s > max)
+ max = s;
+ if (s < min)
+ min = s;
+ fprintf(f, "%4d", (int)(128+127*s));
+ if (i < 9 || j < 15)
+ fprintf(f, ",%s", (j & 7) == 7
+ ? "\n\t" : "");
+ }
+ }
+ fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
+ fprintf(f, "\n};\n\n");
+
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK24_SAMPLERATE 16000
+#define AFSK24_CORRLEN 14
+
+static void gentbl_afsk2400(FILE *f, float tcm3105clk)
+{
+ int i, sum, v;
+
+ fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
+ "#define AFSK24_TX_FREQ_LO %d\n"
+ "#define AFSK24_TX_FREQ_HI %d\n"
+ "#define AFSK24_BITPLL_INC %d\n"
+ "#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk,
+ (int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0),
+ 0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE);
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
+#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
+
+ fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
+ for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+ sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
+ fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n"
+ "static const int afsk24_tx_lo_q[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+ sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i));
+ fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n"
+ "static const int afsk24_tx_hi_i[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+ sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i));
+ fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n"
+ "static const int afsk24_tx_hi_q[] = {\n\t", sum);
+ for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+ sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
+ fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+ }
+ fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+#undef WINDOW
+}
+
+/* -------------------------------------------------------------------- */
+
+static char *progname;
+
+static void gentbl_banner(FILE *f)
+{
+ fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
+ "DO NOT EDIT!\n */\n\n", progname);
+}
+
+/* -------------------------------------------------------------------- */
+
+void main(int argc, char *argv[])
+{
+ FILE *f;
+
+ progname = argv[0];
+ if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_offscostab(f, 6);
+ gentbl_costab(f, 6);
+ gentbl_afsk1200(f);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_offscostab(f, 6);
+ gentbl_costab(f, 6);
+ gentbl_afsk2666(f);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_psk4800.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_psk4800(f);
+ gentbl_costab(f, 8);
+ gentbl_atantab(f);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_hapn4800(f);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_fsk9600(f);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_offscostab(f, 6);
+ gentbl_costab(f, 6);
+ gentbl_afsk2400(f, 8000000);
+ fclose(f);
+ if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
+ exit(1);
+ gentbl_banner(f);
+ gentbl_offscostab(f, 6);
+ gentbl_costab(f, 6);
+ gentbl_afsk2400(f, 7372800);
+ fclose(f);
+ exit(0);
+}
+
+
+/* -------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c
new file mode 100644
index 000000000..171aecfc7
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm.c
@@ -0,0 +1,898 @@
+/*****************************************************************************/
+
+/*
+ * sm.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)
+ *
+ * mode mode string; eg. "wss:afsk1200"
+ * 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)
+ * 0.4 21.01.97 Separately compileable soundcard/modem modules
+ * 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round)
+ * 0.6 16.04.97 init code/data tagged
+ * 0.7 30.07.97 fixed halfduplex interrupt handlers/hotfix for CS423X
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include "sm.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 sm_drvname[] = "soundmodem";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+ &sm_afsk1200_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+ &sm_afsk2400_7_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+ &sm_afsk2400_8_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+ &sm_afsk2666_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+ &sm_psk4800_tx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+ &sm_hapn4800_8_tx,
+ &sm_hapn4800_10_tx,
+ &sm_hapn4800_pm8_tx,
+ &sm_hapn4800_pm10_tx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+ &sm_fsk9600_4_tx,
+ &sm_fsk9600_5_tx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+ NULL
+};
+
+/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+ &sm_afsk1200_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+ &sm_afsk2400_7_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+ &sm_afsk2400_8_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+ &sm_afsk2666_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+ &sm_psk4800_rx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+ &sm_hapn4800_8_rx,
+ &sm_hapn4800_10_rx,
+ &sm_hapn4800_pm8_rx,
+ &sm_hapn4800_pm10_rx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+ &sm_fsk9600_4_rx,
+ &sm_fsk9600_5_rx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+ NULL
+};
+
+static const struct hardware_info *sm_hardware_table[] = {
+#ifdef CONFIG_SOUNDMODEM_SBC
+ &sm_hw_sbc,
+ &sm_hw_sbcfdx,
+#endif /* CONFIG_SOUNDMODEM_SBC */
+#ifdef CONFIG_SOUNDMODEM_WSS
+ &sm_hw_wss,
+ &sm_hw_wssfdx,
+#endif /* CONFIG_SOUNDMODEM_WSS */
+ NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+ char *mode;
+ int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+ { NULL, -1, 0, 0, 0, -1, -1, -1 },
+};
+
+/* --------------------------------------------------------------------- */
+
+#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
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+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[] =
+ { "unknown", "8250", "16450", "16550", "16550A" };
+
+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;
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+}
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm)
+{
+ int invert_dcd = 0;
+ int invert_ptt = 0;
+
+ int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ 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 sm_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");
+ }
+ sm_output_status(sm);
+
+ printk(KERN_INFO "%s: ptt output:", sm_drvname);
+ if (sm->hdrv.ptt_out.flags & SP_SER)
+ printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+ uart_str[u]);
+ 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 sm_output_close(struct sm_state *sm)
+{
+ /* release regions used for PTT output */
+ sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+ sm_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;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev);
+static int sm_close(struct device *dev);
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static const struct hdlcdrv_ops sm_ops = {
+ sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev)
+{
+ struct sm_state *sm;
+ int err;
+
+ if (!dev || !dev->priv ||
+ ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "sm_open: invalid device struct\n");
+ return -EINVAL;
+ }
+ sm = (struct sm_state *)dev->priv;
+
+ if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
+ return -ENODEV;
+ sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
+ err = sm->hwdrv->open(dev, sm);
+ if (err)
+ return err;
+ sm_output_open(sm);
+ MOD_INC_USE_COUNT;
+ printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
+ sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
+ sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_close(struct device *dev)
+{
+ struct sm_state *sm;
+ int err = -ENODEV;
+
+ if (!dev || !dev->priv ||
+ ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "sm_close: invalid device struct\n");
+ return -EINVAL;
+ }
+ sm = (struct sm_state *)dev->priv;
+
+
+ if (sm->hwdrv && sm->hwdrv->close)
+ err = sm->hwdrv && sm->hwdrv->close(dev, sm);
+ sm_output_close(sm);
+ MOD_DEC_USE_COUNT;
+ printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
+ sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
+ return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+ char *cp = strchr(mode, ':');
+ const struct hardware_info **hwp = sm_hardware_table;
+
+ if (!cp)
+ cp = mode;
+ else {
+ *cp++ = '\0';
+ while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
+ hwp++;
+ if (!hwp || !*hwp || !(*hwp)->hw_name)
+ return -EINVAL;
+ if ((*hwp)->loc_storage > sizeof(sm->hw)) {
+ printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
+ sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
+ return -EINVAL;
+ }
+ sm->hwdrv = *hwp;
+ }
+ if (!*cp)
+ return 0;
+ if (sm->hwdrv && sm->hwdrv->sethw)
+ return sm->hwdrv->sethw(dev, sm, cp);
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct sm_state *sm;
+ struct sm_ioctl bi;
+ unsigned long flags;
+ unsigned int newdiagmode;
+ unsigned int newdiagflags;
+ char *cp;
+ const struct modem_tx_info **mtp = sm_modem_tx_table;
+ const struct modem_rx_info **mrp = sm_modem_rx_table;
+ const struct hardware_info **hwp = sm_hardware_table;
+
+ 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) {
+ if (!sm->hwdrv || !sm->hwdrv->ioctl)
+ return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+ return -ENOIOCTLCMD;
+ }
+ switch (hi->cmd) {
+ default:
+ if (sm->hwdrv && sm->hwdrv->ioctl)
+ return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+ return -ENOIOCTLCMD;
+
+ case HDLCDRVCTL_GETMODE:
+ cp = hi->data.modename;
+ if (sm->hwdrv && sm->hwdrv->hw_name)
+ cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
+ else
+ cp += sprintf(cp, "<unspec>:");
+ if (sm->mode_tx && sm->mode_tx->name)
+ cp += sprintf(cp, "%s", sm->mode_tx->name);
+ else
+ cp += sprintf(cp, "<unspec>");
+ if (!sm->mode_rx || !sm->mode_rx ||
+ strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
+ if (sm->mode_rx && sm->mode_rx->name)
+ cp += sprintf(cp, ",%s", sm->mode_rx->name);
+ else
+ cp += sprintf(cp, ",<unspec>");
+ }
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+ return -EFAULT;
+ return 0;
+
+ case HDLCDRVCTL_SETMODE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+ return sethw(dev, sm, hi->data.modename);
+
+ case HDLCDRVCTL_MODELIST:
+ cp = hi->data.modename;
+ while (*hwp) {
+ if ((*hwp)->hw_name)
+ cp += sprintf("%s:,", (*hwp)->hw_name);
+ hwp++;
+ }
+ while (*mtp) {
+ if ((*mtp)->name)
+ cp += sprintf(">%s,", (*mtp)->name);
+ mtp++;
+ }
+ while (*mrp) {
+ if ((*mrp)->name)
+ cp += sprintf("<%s,", (*mrp)->name);
+ mrp++;
+ }
+ cp[-1] = '\0';
+ if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+ return -EFAULT;
+ return 0;
+
+#ifdef SM_DEBUG
+ case SMCTL_GETDEBUG:
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+ bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
+ bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
+ bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
+ bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
+ sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
+ sm->debug_vals.dma_residue = 0;
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+#endif /* SM_DEBUG */
+
+ case SMCTL_DIAGNOSE:
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+ newdiagmode = bi.data.diag.mode;
+ newdiagflags = bi.data.diag.flags;
+ if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
+ return -EINVAL;
+ bi.data.diag.mode = sm->diag.mode;
+ bi.data.diag.flags = sm->diag.flags;
+ bi.data.diag.samplesperbit = sm->mode_rx->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);
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+ }
+ if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+ }
+ if (bi.data.diag.datalen > DIAGDATALEN)
+ bi.data.diag.datalen = DIAGDATALEN;
+ if (sm->diag.ptr < bi.data.diag.datalen) {
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+ }
+ if (copy_to_user(bi.data.diag.data, sm->diag.data,
+ bi.data.diag.datalen * sizeof(short)))
+ return -EFAULT;
+ 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);
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+int sm_x86_capability = 0;
+
+__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;
+
+ 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", sm_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", sm_drvname, id.s);
+ return;
+ }
+ printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s);
+ __asm__ ("cpuid" : "=a" (eax), "=d" (sm_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, sm_x86_capability);
+}
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+__initfunc(static int sm_init(void))
+#else /* MODULE */
+__initfunc(int sm_init(void))
+#endif /* MODULE */
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct sm_state *sm;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+ printk(sm_drvinfo);
+#ifdef __i386__
+ i386_capability();
+#endif /* __i386__ */
+ /*
+ * 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].mode)
+ set_hw = 0;
+ if (!set_hw)
+ sm_ports[i].iobase = sm_ports[i].irq = 0;
+ j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
+ ifname, sm_ports[i].iobase,
+ sm_ports[i].irq, sm_ports[i].dma);
+ if (!j) {
+ sm = (struct sm_state *)dev->priv;
+ sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
+ sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+ sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+ sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+ if (set_hw && sethw(dev, sm, sm_ports[i].mode))
+ set_hw = 0;
+ found++;
+ } else {
+ printk(KERN_WARNING "%s: cannot register net device\n",
+ sm_drvname);
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode = NULL;
+static int iobase = -1;
+static int irq = -1;
+static int dma = -1;
+static int dma2 = -1;
+static int serio = 0;
+static int pario = 0;
+static int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "soundmodem base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "soundmodem interrupt");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "soundmodem dma channel");
+MODULE_PARM(dma2, "i");
+MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ if (mode) {
+ if (iobase == -1)
+ iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
+ if (irq == -1)
+ irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
+ if (dma == -1)
+ dma = 1;
+ }
+ sm_ports[0].mode = mode;
+ sm_ports[0].iobase = iobase;
+ sm_ports[0].irq = irq;
+ sm_ports[0].dma = dma;
+ sm_ports[0].dma2 = dma2;
+ sm_ports[0].seriobase = serio;
+ sm_ports[0].pariobase = pario;
+ sm_ports[0].midiiobase = midiio;
+ sm_ports[1].mode = NULL;
+
+ return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+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=io,irq,dma[,dma2[,serio[,pario]]],mode
+ * mode: hw:modem
+ * hw: sbc, wss, wssfdx
+ * modem: afsk1200, fsk9600
+ */
+
+__initfunc(void sm_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
+ if ((i >= NR_PORTS) || (ints[0] < 3)) {
+ printk(KERN_INFO "%s: too many or invalid interface "
+ "specifications\n", sm_drvname);
+ return;
+ }
+ sm_ports[i].mode = str;
+ sm_ports[i].iobase = ints[1];
+ sm_ports[i].irq = ints[2];
+ sm_ports[i].dma = ints[3];
+ sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0;
+ sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0;
+ sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0;
+ sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0;
+ if (i < NR_PORTS-1)
+ sm_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm.h b/drivers/net/hamradio/soundmodem/sm.h
new file mode 100644
index 000000000..25bbc8ba9
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm.h
@@ -0,0 +1,382 @@
+/*****************************************************************************/
+
+/*
+ * sm.h -- soundcard radio modem driver internal header.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _SM_H
+#define _SM_H
+
+/* ---------------------------------------------------------------------- */
+
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+#define SM_DEBUG
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+ struct hdlcdrv_state hdrv;
+
+ const struct modem_tx_info *mode_tx;
+ const struct modem_rx_info *mode_rx;
+
+ const struct hardware_info *hwdrv;
+
+ /*
+ * Hardware (soundcard) access routines state
+ */
+ struct {
+ void *ibuf;
+ unsigned int ifragsz;
+ unsigned int ifragptr;
+ unsigned int i16bit;
+ void *obuf;
+ unsigned int ofragsz;
+ unsigned int ofragptr;
+ unsigned int o16bit;
+ int ptt_cnt;
+ } dma;
+
+ union {
+ long hw[32/sizeof(long)];
+ } hw;
+
+ /*
+ * state of the modem code
+ */
+ union {
+ long m[32/sizeof(long)];
+ } m;
+ union {
+ long d[256/sizeof(long)];
+ } d;
+
+#define DIAGDATALEN 64
+ struct diag_data {
+ 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;
+ unsigned mod_cyc;
+ unsigned demod_cyc;
+ unsigned dma_residue;
+ } debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Mode definition structure
+ */
+
+struct modem_tx_info {
+ const char *name;
+ unsigned int loc_storage;
+ int srate;
+ int bitrate;
+ void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
+ void (*modulator_s16)(struct sm_state *, short *, unsigned int);
+ void (*init)(struct sm_state *);
+};
+
+struct modem_rx_info {
+ const char *name;
+ unsigned int loc_storage;
+ int srate;
+ int bitrate;
+ unsigned int overlap;
+ unsigned int sperbit;
+ void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
+ void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
+ void (*init)(struct sm_state *);
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Soundcard driver definition structure
+ */
+
+struct hardware_info {
+ char *hw_name; /* used for request_{region,irq,dma} */
+ unsigned int loc_storage;
+ /*
+ * mode specific open/close
+ */
+ int (*open)(struct device *, struct sm_state *);
+ int (*close)(struct device *, struct sm_state *);
+ int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
+ struct hdlcdrv_ioctl *, int);
+ int (*sethw)(struct device *, struct sm_state *, char *);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+extern const char sm_drvname[];
+extern const char sm_drvinfo[];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+extern inline void diag_trigger(struct sm_state *sm)
+{
+ if (sm->diag.ptr < 0)
+ if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+ sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
+#define SHRT_MIN (-SHRT_MAX-1)
+
+extern 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;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void diag_add_one(struct sm_state *sm, int val)
+{
+ if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+ sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+ 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;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
+{
+ if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
+ sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
+ return;
+ /* clip */
+ if (vali > SHRT_MAX)
+ vali = SHRT_MAX;
+ if (vali < SHRT_MIN)
+ vali = SHRT_MIN;
+ if (valq > SHRT_MAX)
+ valq = SHRT_MAX;
+ if (valq < SHRT_MIN)
+ valq = SHRT_MIN;
+ sm->diag.data[sm->diag.ptr++] = vali;
+ sm->diag.data[sm->diag.ptr++] = valq;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== utility functions ===============================
+ */
+
+extern inline unsigned int hweight32(unsigned int w)
+ __attribute__ ((unused));
+extern inline unsigned int hweight16(unsigned short w)
+ __attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+ __attribute__ ((unused));
+
+extern inline unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+ res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+ return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+extern 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);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+ unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+ __attribute__ ((unused));
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+ __attribute__ ((unused));
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+{
+ for (;;) {
+ if (!x)
+ return y;
+ if (!y)
+ return x;
+ if (x > y)
+ x %= y;
+ else
+ y %= x;
+ }
+}
+
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+{
+ return x * y / gcd(x, y);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== profiling =======================================
+ */
+
+
+#ifdef __i386__
+
+extern int sm_x86_capability;
+
+#define HAS_RDTSC (sm_x86_capability & 0x10)
+
+/*
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
+ * in fact, overflowing modems would require over 2THz clock speeds :-)
+ */
+
+#define time_exec(var,cmd) \
+({ \
+ if (HAS_RDTSC) { \
+ unsigned int cnt1, cnt2, cnt3; \
+ __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \
+ cmd; \
+ __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \
+ var = cnt2-cnt1; \
+ } else { \
+ cmd; \
+ } \
+})
+
+#else /* __i386__ */
+
+#define time_exec(var,cmd) cmd
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern const struct modem_tx_info sm_afsk1200_tx;
+extern const struct modem_tx_info sm_afsk2400_7_tx;
+extern const struct modem_tx_info sm_afsk2400_8_tx;
+extern const struct modem_tx_info sm_afsk2666_tx;
+extern const struct modem_tx_info sm_psk4800_tx;
+extern const struct modem_tx_info sm_hapn4800_8_tx;
+extern const struct modem_tx_info sm_hapn4800_10_tx;
+extern const struct modem_tx_info sm_hapn4800_pm8_tx;
+extern const struct modem_tx_info sm_hapn4800_pm10_tx;
+extern const struct modem_tx_info sm_fsk9600_4_tx;
+extern const struct modem_tx_info sm_fsk9600_5_tx;
+
+extern const struct modem_rx_info sm_afsk1200_rx;
+extern const struct modem_rx_info sm_afsk2400_7_rx;
+extern const struct modem_rx_info sm_afsk2400_8_rx;
+extern const struct modem_rx_info sm_afsk2666_rx;
+extern const struct modem_rx_info sm_psk4800_rx;
+extern const struct modem_rx_info sm_hapn4800_8_rx;
+extern const struct modem_rx_info sm_hapn4800_10_rx;
+extern const struct modem_rx_info sm_hapn4800_pm8_rx;
+extern const struct modem_rx_info sm_hapn4800_pm10_rx;
+extern const struct modem_rx_info sm_fsk9600_4_rx;
+extern const struct modem_rx_info sm_fsk9600_5_rx;
+
+extern const struct hardware_info sm_hw_sbc;
+extern const struct hardware_info sm_hw_sbcfdx;
+extern const struct hardware_info sm_hw_wss;
+extern const struct hardware_info sm_hw_wssfdx;
+
+extern const struct modem_tx_info *sm_modem_tx_table[];
+extern const struct modem_rx_info *sm_modem_rx_table[];
+extern const struct hardware_info *sm_hardware_table[];
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm);
+/*void sm_output_open(struct sm_state *sm);*/
+/*void sm_output_close(struct sm_state *sm);*/
+
+/* --------------------------------------------------------------------- */
+
+extern 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 */
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SM_H */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk1200.c b/drivers/net/hamradio/soundmodem/sm_afsk1200.c
new file mode 100644
index 000000000..64b20a57c
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_afsk1200.c
@@ -0,0 +1,272 @@
+/*****************************************************************************/
+
+/*
+ * sm_afsk1200.c -- soundcard radio modem driver, 1200 baud AFSK modem
+ *
+ * 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.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk1200.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk12 {
+ unsigned int shreg;
+ unsigned int bit_pll;
+ unsigned char last_sample;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ unsigned char last_rxbit;
+};
+
+struct mod_state_afsk12 {
+ unsigned int shreg;
+ unsigned char tx_bit;
+ unsigned int bit_pll;
+ unsigned int dds_inc;
+ unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = {
+ AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
+ AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE
+};
+
+static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf,
+ unsigned int buflen)
+{
+ struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!((st->txphase++) & 7)) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ }
+ st->dds_inc = dds_inc[st->tx_bit & 1];
+ *buf++ = OFFSCOS(st->bit_pll);
+ st->bit_pll += st->dds_inc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!((st->txphase++) & 7)) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ }
+ st->dds_inc = dds_inc[st->tx_bit & 1];
+ *buf++ = COS(st->bit_pll);
+ st->bit_pll += st->dds_inc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
+{
+ int sum = -0x80 * csum;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+
+ sum >>= 7;
+ return sum * sum;
+}
+
+extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
+{
+ int sum = 0;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+
+ sum >>= 15;
+ return sum * sum;
+}
+
+extern __inline__ int do_filter_1200_u8(const unsigned char *buf)
+{
+ int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+ sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+ sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+ sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+ return sum;
+}
+
+extern __inline__ int do_filter_1200_s16(const short *buf)
+{
+ int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+ sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+ sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+ sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+ return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static const int pll_corr[2] = { -0x1000, 0x1000 };
+
+static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_1200_u8(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x2000;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr
+ [st->bit_pll < 0x9000];
+ j = 4 * hweight8(st->dcd_shreg & 0x38)
+ - hweight16(st->dcd_shreg & 0x7c0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_1200_s16(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x2000;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr
+ [st->bit_pll < 0x9000];
+ j = 4 * hweight8(st->dcd_shreg & 0x38)
+ - hweight16(st->dcd_shreg & 0x7c0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, *buf, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_1200(struct sm_state *sm)
+{
+ struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+
+ st->dcd_time = 120;
+ st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk1200_tx = {
+ "afsk1200", sizeof(struct mod_state_afsk12),
+ AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk1200_rx = {
+ "afsk1200", sizeof(struct demod_state_afsk12),
+ AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200,
+ demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c
new file mode 100644
index 000000000..d217936ab
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ * sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 7.3728MHz. The mark and space frequencies therefore
+ * lie at 3658 and 1996 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_7.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+ unsigned int shreg;
+ unsigned int bit_pll;
+ unsigned char last_sample;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+ unsigned int shreg;
+ unsigned char tx_bit;
+ unsigned int bit_pll;
+ unsigned int tx_seq;
+ unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+ AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (st->tx_seq < 0x5555) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ st->phinc = dds_inc[st->tx_bit & 1];
+ }
+ st->tx_seq += 0x5555;
+ st->tx_seq &= 0xffff;
+ *buf = OFFSCOS(st->bit_pll);
+ st->bit_pll += st->phinc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (st->tx_seq < 0x5555) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ st->phinc = dds_inc[st->tx_bit & 1];
+ }
+ st->tx_seq += 0x5555;
+ st->tx_seq &= 0xffff;
+ *buf = COS(st->bit_pll);
+ st->bit_pll += st->phinc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+ int sum = -0x80 * csum;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+ sum += (st[-8] * coeff[8]);
+ sum += (st[-9] * coeff[9]);
+ sum += (st[-10] * coeff[10]);
+ sum += (st[-11] * coeff[11]);
+ sum += (st[-12] * coeff[12]);
+ sum += (st[-13] * coeff[13]);
+
+ sum >>= 7;
+ return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+ int sum = 0;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+ sum += (st[-8] * coeff[8]);
+ sum += (st[-9] * coeff[9]);
+ sum += (st[-10] * coeff[10]);
+ sum += (st[-11] * coeff[11]);
+ sum += (st[-12] * coeff[12]);
+ sum += (st[-13] * coeff[13]);
+
+ sum >>= 15;
+ return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+ int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+ sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+ sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+ sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+ return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+ int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+ sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+ sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+ sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+ return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_2400_u8(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += AFSK24_BITPLL_INC;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+ st->bit_pll += AFSK24_BITPLL_INC/2;
+ else
+ st->bit_pll -= AFSK24_BITPLL_INC/2;
+ j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+ - hweight16(st->dcd_shreg & 0x1e0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_2400_s16(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += AFSK24_BITPLL_INC;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+ st->bit_pll += AFSK24_BITPLL_INC/2;
+ else
+ st->bit_pll -= AFSK24_BITPLL_INC/2;
+ j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+ - hweight16(st->dcd_shreg & 0x1e0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, *buf, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+ st->dcd_time = 120;
+ st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_7_tx = {
+ "afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
+ modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_7_rx = {
+ "afsk2400_7", sizeof(struct demod_state_afsk24),
+ AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
+ demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c
new file mode 100644
index 000000000..23d233746
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ * sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 8MHz. The mark and space frequencies therefore
+ * lie at 3970 and 2165 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_8.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+ unsigned int shreg;
+ unsigned int bit_pll;
+ unsigned char last_sample;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+ unsigned int shreg;
+ unsigned char tx_bit;
+ unsigned int bit_pll;
+ unsigned int tx_seq;
+ unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+ AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (st->tx_seq < 0x5555) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ st->phinc = dds_inc[st->tx_bit & 1];
+ }
+ st->tx_seq += 0x5555;
+ st->tx_seq &= 0xffff;
+ *buf = OFFSCOS(st->bit_pll);
+ st->bit_pll += st->phinc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (st->tx_seq < 0x5555) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+ st->shreg >>= 1;
+ st->phinc = dds_inc[st->tx_bit & 1];
+ }
+ st->tx_seq += 0x5555;
+ st->tx_seq &= 0xffff;
+ *buf = COS(st->bit_pll);
+ st->bit_pll += st->phinc;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+ int sum = -0x80 * csum;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+ sum += (st[-8] * coeff[8]);
+ sum += (st[-9] * coeff[9]);
+ sum += (st[-10] * coeff[10]);
+ sum += (st[-11] * coeff[11]);
+ sum += (st[-12] * coeff[12]);
+ sum += (st[-13] * coeff[13]);
+
+ sum >>= 7;
+ return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+ int sum = 0;
+
+ sum += (st[0] * coeff[0]);
+ sum += (st[-1] * coeff[1]);
+ sum += (st[-2] * coeff[2]);
+ sum += (st[-3] * coeff[3]);
+ sum += (st[-4] * coeff[4]);
+ sum += (st[-5] * coeff[5]);
+ sum += (st[-6] * coeff[6]);
+ sum += (st[-7] * coeff[7]);
+ sum += (st[-8] * coeff[8]);
+ sum += (st[-9] * coeff[9]);
+ sum += (st[-10] * coeff[10]);
+ sum += (st[-11] * coeff[11]);
+ sum += (st[-12] * coeff[12]);
+ sum += (st[-13] * coeff[13]);
+
+ sum >>= 15;
+ return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+ int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+ sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+ sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+ sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+ return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+ int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+ sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+ sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+ sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+ return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_2400_u8(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += AFSK24_BITPLL_INC;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+ st->bit_pll += AFSK24_BITPLL_INC/2;
+ else
+ st->bit_pll -= AFSK24_BITPLL_INC/2;
+ j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+ - hweight16(st->dcd_shreg & 0x1e0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+ int j;
+ int sum;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sum = do_filter_2400_s16(buf);
+ st->dcd_shreg <<= 1;
+ st->bit_pll += AFSK24_BITPLL_INC;
+ newsample = (sum > 0);
+ if (st->last_sample ^ newsample) {
+ st->last_sample = newsample;
+ st->dcd_shreg |= 1;
+ if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+ st->bit_pll += AFSK24_BITPLL_INC/2;
+ else
+ st->bit_pll -= AFSK24_BITPLL_INC/2;
+ j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+ - hweight16(st->dcd_shreg & 0x1e0);
+ st->dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 120;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->shreg >>= 1;
+ st->shreg |= (!(st->last_rxbit ^
+ st->last_sample)) << 16;
+ st->last_rxbit = st->last_sample;
+ diag_trigger(sm);
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ }
+ diag_add(sm, *buf, sum);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+ struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+ st->dcd_time = 120;
+ st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_8_tx = {
+ "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
+ modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_8_rx = {
+ "afsk2400_8", sizeof(struct demod_state_afsk24),
+ AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
+ demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_fsk9600.c b/drivers/net/hamradio/soundmodem/sm_fsk9600.c
new file mode 100644
index 000000000..bc2fb53b1
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_fsk9600.c
@@ -0,0 +1,391 @@
+/*****************************************************************************/
+
+/*
+ * sm_fsk9600.c -- soundcard radio modem driver,
+ * 9600 baud G3RUH compatible FSK modem
+ *
+ * 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.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_fsk9600.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_fsk96 {
+ unsigned int shreg;
+ unsigned long descram;
+ unsigned int bit_pll;
+ unsigned char last_sample;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+};
+
+struct mod_state_fsk96 {
+ unsigned int shreg;
+ unsigned long scram;
+ unsigned char tx_bit;
+ unsigned char *txtbl;
+ unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+#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 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!st->txphase++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->scram = (st->scram << 1) | (st->scram & 1);
+ st->scram ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ if (st->scram & (SCRAM_TAP1 << 1))
+ st->scram ^= SCRAM_TAPN << 1;
+ st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+ st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+ }
+ if (st->txphase >= 4)
+ st->txphase = 0;
+ *buf++ = *st->txtbl;
+ st->txtbl += 0x100;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!st->txphase++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->scram = (st->scram << 1) | (st->scram & 1);
+ st->scram ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ if (st->scram & (SCRAM_TAP1 << 1))
+ st->scram ^= SCRAM_TAPN << 1;
+ st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+ st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+ }
+ if (st->txphase >= 4)
+ st->txphase = 0;
+ *buf++ = ((*st->txtbl)-0x80) << 8;
+ st->txtbl += 0x100;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x4000;
+ curbit = (*buf >= 0x80);
+ if (st->last_sample ^ curbit) {
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+ st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
+ !!(st->dcd_shreg & 0x10);
+ }
+ st->last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->descram = (st->descram << 1) | curbit;
+ descx = st->descram ^ (st->descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ st->shreg >>= 1;
+ st->shreg |= (!(descx & 1)) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x4000;
+ curbit = (*buf >= 0);
+ if (st->last_sample ^ curbit) {
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+ st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
+ !!(st->dcd_shreg & 0x10);
+ }
+ st->last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->descram = (st->descram << 1) | curbit;
+ descx = st->descram ^ (st->descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ st->shreg >>= 1;
+ st->shreg |= (!(descx & 1)) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, *buf);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!st->txphase++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->scram = (st->scram << 1) | (st->scram & 1);
+ st->scram ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ if (st->scram & (SCRAM_TAP1 << 1))
+ st->scram ^= SCRAM_TAPN << 1;
+ st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+ st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+ }
+ if (st->txphase >= 5)
+ st->txphase = 0;
+ *buf++ = *st->txtbl;
+ st->txtbl += 0x100;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+ for (; buflen > 0; buflen--) {
+ if (!st->txphase++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->scram = (st->scram << 1) | (st->scram & 1);
+ st->scram ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ if (st->scram & (SCRAM_TAP1 << 1))
+ st->scram ^= SCRAM_TAPN << 1;
+ st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+ st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+ }
+ if (st->txphase >= 5)
+ st->txphase = 0;
+ *buf++ = ((*st->txtbl)-0x80)<<8;
+ st->txtbl += 0x100;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x3333;
+ curbit = (*buf >= 0x80);
+ if (st->last_sample ^ curbit) {
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+ st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
+ hweight8(st->dcd_shreg & 0x70);
+ }
+ st->last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->descram = (st->descram << 1) | curbit;
+ descx = st->descram ^ (st->descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ st->shreg >>= 1;
+ st->shreg |= (!(descx & 1)) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x3333;
+ curbit = (*buf >= 0);
+ if (st->last_sample ^ curbit) {
+ st->dcd_shreg |= 1;
+ st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+ st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
+ hweight8(st->dcd_shreg & 0x70);
+ }
+ st->last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->descram = (st->descram << 1) | curbit;
+ descx = st->descram ^ (st->descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ st->shreg >>= 1;
+ st->shreg |= (!(descx & 1)) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, *buf);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_9600(struct sm_state *sm)
+{
+ struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+
+ st->dcd_time = 240;
+ st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_4_tx = {
+ "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
+ modulator_9600_4_u8, modulator_9600_4_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_4_rx = {
+ "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
+ demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_5_tx = {
+ "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
+ modulator_9600_5_u8, modulator_9600_5_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_5_rx = {
+ "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5,
+ demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_hapn4800.c b/drivers/net/hamradio/soundmodem/sm_hapn4800.c
new file mode 100644
index 000000000..f6babcd9d
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_hapn4800.c
@@ -0,0 +1,560 @@
+/*****************************************************************************/
+
+/*
+ * sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN modem
+ *
+ * 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.
+ *
+ *
+ * This module implements a (hopefully) HAPN (Hamilton Area Packet
+ * Network) compatible 4800 baud modem.
+ * The HAPN modem uses kind of "duobinary signalling" (not really,
+ * duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
+ * instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
+ * Proakis, Digital Communications).
+ * The code is untested. It is compatible with itself (i.e. it can decode
+ * the packets it sent), but I could not test if it is compatible with
+ * any "real" HAPN modem, since noone uses it in my region of the world.
+ * Feedback therefore welcome.
+ */
+
+#include "sm.h"
+#include "sm_tbl_hapn4800.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_hapn48 {
+ unsigned int shreg;
+ unsigned int bit_pll;
+ unsigned char last_bit;
+ unsigned char last_bit2;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ int lvlhi, lvllo;
+};
+
+struct mod_state_hapn48 {
+ unsigned int shreg;
+ unsigned char tx_bit;
+ unsigned int tx_seq;
+ const unsigned char *tbl;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = ((st->tx_bit << 1) |
+ (st->tx_bit & 1));
+ st->tx_bit ^= (!(st->shreg & 1));
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 10)
+ st->tx_seq = 0;
+ *buf = *st->tbl;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = ((st->tx_bit << 1) |
+ (st->tx_bit & 1));
+ st->tx_bit ^= (!(st->shreg & 1));
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 10)
+ st->tx_seq = 0;
+ *buf = ((*st->tbl)-0x80)<<8;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+ st->tx_bit ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 8)
+ st->tx_seq = 0;
+ *buf = *st->tbl;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+ st->tx_bit ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 8)
+ st->tx_seq = 0;
+ *buf = ((*st->tbl)-0x80)<<8;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = ((st->tx_bit << 1) |
+ (st->tx_bit & 1));
+ st->tx_bit ^= (!(st->shreg & 1));
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 10)
+ st->tx_seq = 0;
+ *buf = *st->tbl;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = ((st->tx_bit << 1) |
+ (st->tx_bit & 1));
+ st->tx_bit ^= (!(st->shreg & 1));
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 10)
+ st->tx_seq = 0;
+ *buf = ((*st->tbl)-0x80)<<8;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+ st->tx_bit ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 8)
+ st->tx_seq = 0;
+ *buf = *st->tbl;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+ struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+ for (; buflen > 0; buflen--, buf++) {
+ if (!st->tx_seq++) {
+ if (st->shreg <= 1)
+ st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+ st->tx_bit ^= !(st->shreg & 1);
+ st->shreg >>= 1;
+ st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+ }
+ if (st->tx_seq >= 8)
+ st->tx_seq = 0;
+ *buf = ((*st->tbl)-0x80)<<8;
+ st->tbl += 0x10;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+ static const int pll_corr[2] = { -0x800, 0x800 };
+ int curst, cursync;
+ int inv;
+
+ for (; buflen > 0; buflen--, buf++) {
+ inv = ((int)(buf[-2])-0x80) << 8;
+ st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+ if (inv > st->lvlhi)
+ st->lvlhi = inv;
+ if (inv < st->lvllo)
+ st->lvllo = inv;
+ if (buflen & 1)
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x199a;
+ curst = cursync = 0;
+ if (inv > st->lvlhi >> 1) {
+ curst = 1;
+ cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+ buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+ } else if (inv < st->lvllo >> 1) {
+ curst = -1;
+ cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+ buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+ }
+ if (cursync) {
+ st->dcd_shreg |= cursync;
+ st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+ st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
+ hweight32(st->dcd_shreg & 0xe739ce70);
+ }
+ hdlcdrv_channelbit(&sm->hdrv, cursync);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->last_bit2 = st->last_bit;
+ if (curst < 0)
+ st->last_bit = 0;
+ else if (curst > 0)
+ st->last_bit = 1;
+ st->shreg >>= 1;
+ st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, inv);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+ static const int pll_corr[2] = { -0x800, 0x800 };
+ int curst, cursync;
+ int inv;
+
+ for (; buflen > 0; buflen--, buf++) {
+ inv = buf[-2];
+ st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+ if (inv > st->lvlhi)
+ st->lvlhi = inv;
+ if (inv < st->lvllo)
+ st->lvllo = inv;
+ if (buflen & 1)
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x199a;
+ curst = cursync = 0;
+ if (inv > st->lvlhi >> 1) {
+ curst = 1;
+ cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+ buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+ } else if (inv < st->lvllo >> 1) {
+ curst = -1;
+ cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+ buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+ }
+ if (cursync) {
+ st->dcd_shreg |= cursync;
+ st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+ st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
+ hweight32(st->dcd_shreg & 0xe739ce70);
+ }
+ hdlcdrv_channelbit(&sm->hdrv, cursync);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->last_bit2 = st->last_bit;
+ if (curst < 0)
+ st->last_bit = 0;
+ else if (curst > 0)
+ st->last_bit = 1;
+ st->shreg >>= 1;
+ st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, inv);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+ struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+ static const int pll_corr[2] = { -0x800, 0x800 };
+ int curst, cursync;
+ int inv;
+
+ for (; buflen > 0; buflen--, buf++) {
+ inv = ((int)(buf[-2])-0x80) << 8;
+ st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+ if (inv > st->lvlhi)
+ st->lvlhi = inv;
+ if (inv < st->lvllo)
+ st->lvllo = inv;
+ if (buflen & 1)
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x2000;
+ curst = cursync = 0;
+ if (inv > st->lvlhi >> 1) {
+ curst = 1;
+ cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+ buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+ } else if (inv < st->lvllo >> 1) {
+ curst = -1;
+ cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+ buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+ }
+ if (cursync) {
+ st->dcd_shreg |= cursync;
+ st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+ st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
+ hweight32(st->dcd_shreg & 0xbbbbbbbb);
+ }
+ hdlcdrv_channelbit(&sm->hdrv, cursync);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->last_bit2 = st->last_bit;
+ if (curst < 0)
+ st->last_bit = 0;
+ else if (curst > 0)
+ st->last_bit = 1;
+ st->shreg >>= 1;
+ st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, inv);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+ struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+ static const int pll_corr[2] = { -0x800, 0x800 };
+ int curst, cursync;
+ int inv;
+
+ for (; buflen > 0; buflen--, buf++) {
+ inv = buf[-2];
+ st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+ if (inv > st->lvlhi)
+ st->lvlhi = inv;
+ if (inv < st->lvllo)
+ st->lvllo = inv;
+ if (buflen & 1)
+ st->dcd_shreg <<= 1;
+ st->bit_pll += 0x2000;
+ curst = cursync = 0;
+ if (inv > st->lvlhi >> 1) {
+ curst = 1;
+ cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+ buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+ } else if (inv < st->lvllo >> 1) {
+ curst = -1;
+ cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+ buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+ }
+ if (cursync) {
+ st->dcd_shreg |= cursync;
+ st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+ st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
+ hweight32(st->dcd_shreg & 0xbbbbbbbb);
+ }
+ hdlcdrv_channelbit(&sm->hdrv, cursync);
+ if ((--st->dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
+ st->dcd_sum1 +
+ st->dcd_sum2) < 0);
+ st->dcd_sum2 = st->dcd_sum1;
+ st->dcd_sum1 = st->dcd_sum0;
+ st->dcd_sum0 = 2; /* slight bias */
+ st->dcd_time = 240;
+ }
+ if (st->bit_pll >= 0x10000) {
+ st->bit_pll &= 0xffff;
+ st->last_bit2 = st->last_bit;
+ if (curst < 0)
+ st->last_bit = 0;
+ else if (curst > 0)
+ st->last_bit = 1;
+ st->shreg >>= 1;
+ st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+ if (st->shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+ st->shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, inv);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_hapn4800(struct sm_state *sm)
+{
+ struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+
+ st->dcd_time = 120;
+ st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_8_tx = {
+ "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800,
+ modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_8_rx = {
+ "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
+ demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_10_tx = {
+ "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
+ modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_10_rx = {
+ "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
+ demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm8_tx = {
+ "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800,
+ modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm8_rx = {
+ "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
+ demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm10_tx = {
+ "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
+ modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm10_rx = {
+ "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
+ demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c
new file mode 100644
index 000000000..e66a2e5cb
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_sbc.c
@@ -0,0 +1,941 @@
+/*****************************************************************************/
+
+/*
+ * sm_sbc.c -- soundcard radio modem driver soundblaster hardware 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.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include <linux/delay.h>
+#include "sm.h"
+#include "smdma.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
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_sbc {
+ unsigned char revhi, revlo;
+ unsigned char fmt[2];
+ unsigned int sr[2];
+};
+
+#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+/*
+ * 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 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
+
+/* --------------------------------------------------------------------- */
+
+static int inline reset_dsp(struct device *dev)
+{
+ int i;
+
+ outb(1, DSP_RESET(dev->base_addr));
+ udelay(300);
+ 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 int config_resources(struct device *dev, struct sm_state *sm, int fdx)
+{
+ unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+ unsigned long flags;
+
+ switch (dev->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->dma) {
+ case 0:
+ dmareg |= 0x01;
+ break;
+
+ case 1:
+ dmareg |= 0x02;
+ break;
+
+ case 3:
+ dmareg |= 0x08;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ if (fdx) {
+ switch (sm->hdrv.ptt_out.dma2) {
+ 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->base_addr));
+ outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
+ realirq = inb(DSP_MIXER_DATA(dev->base_addr));
+ outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
+ outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
+ realdma = inb(DSP_MIXER_DATA(dev->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", sm_drvname);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_8bit(struct device *dev)
+{
+ inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_16bit(struct device *dev)
+{
+ inb(DSP_INTACK_16BIT(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
+{
+ 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 sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
+ static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+ unsigned int nsamps;
+
+ send = !!send;
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ sbc_int_ack_8bit(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+ write_dsp(dev, SCSTATE->fmt[send]);
+ write_dsp(dev, sbcskr[send]);
+ nsamps = dma_setup(sm, send, dev->dma) - 1;
+ sbc_int_ack_8bit(dev);
+ if (SCSTATE->revhi >= 4) {
+ write_dsp(dev, sbc4mode[send]);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, nsamps & 0xff);
+ write_dsp(dev, nsamps >> 8);
+ } else {
+ write_dsp(dev, SBC_BLOCKSIZE);
+ write_dsp(dev, nsamps & 0xff);
+ write_dsp(dev, nsamps >> 8);
+ write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
+ /* hispeed mode if sample rate > 13kHz */
+ }
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+ unsigned int curfrag;
+
+ if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ cli();
+ sbc_int_ack_8bit(dev);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
+ enable_dma(dev->dma);
+ sm_int_freq(sm);
+ sti();
+ if (sm->dma.ptt_cnt <= 0) {
+ dma_receive(sm, curfrag);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ if (hdlcdrv_ptt(&sm->hdrv)) {
+ /* starting to transmit */
+ disable_dma(dev->dma);
+ hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+ dma_start_transmit(sm);
+ setup_dma_dsp(dev, sm, 1);
+ dma_transmit(sm);
+ }
+ } else if (dma_end_transmit(sm, curfrag)) {
+ /* stopping transmission */
+ disable_dma(dev->dma);
+ sti();
+ dma_init_receive(sm);
+ setup_dma_dsp(dev, sm, 0);
+ } else
+ dma_transmit(sm);
+ sm_output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev, struct sm_state *sm)
+{
+ int err;
+ unsigned int dmasz, u;
+
+ if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+ printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
+ sizeof(struct sc_state_sbc), sizeof(sm->m));
+ return -ENODEV;
+ }
+ 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)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+ sm_drvname, dev->base_addr);
+ return -ENODEV;
+ }
+ write_dsp(dev, SBC_GET_REVISION);
+ if (!read_dsp(dev, &SCSTATE->revhi) ||
+ !read_dsp(dev, &SCSTATE->revlo))
+ return -ENODEV;
+ printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
+ SCSTATE->revhi, SCSTATE->revlo);
+ if (SCSTATE->revhi < 2) {
+ printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
+ "rev 2.00 required\n", sm_drvname);
+ return -ENODEV;
+ }
+ if (SCSTATE->revhi < 3 &&
+ (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
+ printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
+ "old, at least 3.00 required\n", sm_drvname,
+ dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
+ return -ENODEV;
+ }
+ if (SCSTATE->revhi >= 4 &&
+ (err = config_resources(dev, sm, 0))) {
+ printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+ return err;
+ }
+ /*
+ * initialize some variables
+ */
+ dma_init_receive(sm);
+ dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+ u = NUM_FRAGMENTS * sm->dma.ofragsz;
+ if (u > dmasz)
+ dmasz = u;
+ if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ dma_init_transmit(sm);
+ dma_init_receive(sm);
+
+ memset(&sm->m, 0, sizeof(sm->m));
+ memset(&sm->d, 0, sizeof(sm->d));
+ if (sm->mode_tx->init)
+ sm->mode_tx->init(sm);
+ if (sm->mode_rx->init)
+ sm->mode_rx->init(sm);
+
+ if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+ kfree_s(sm->dma.obuf, dmasz);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT,
+ sm->hwdrv->hw_name, dev)) {
+ free_dma(dev->dma);
+ kfree_s(sm->dma.obuf, dmasz);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+ setup_dma_dsp(dev, sm, 0);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev, struct sm_state *sm)
+{
+ 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->dma.obuf);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+ char *cp = strchr(mode, '.');
+ const struct modem_tx_info **mtp = sm_modem_tx_table;
+ const struct modem_rx_info **mrp;
+
+ if (!strcmp(mode, "off")) {
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return 0;
+ }
+ if (cp)
+ *cp++ = '\0';
+ else
+ cp = mode;
+ for (; *mtp; mtp++) {
+ if ((*mtp)->loc_storage > sizeof(sm->m)) {
+ printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+ sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+ continue;
+ }
+ if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+ continue;
+ if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+ continue;
+ if (!(*mtp)->modulator_u8)
+ continue;
+ for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+ if ((*mrp)->loc_storage > sizeof(sm->d)) {
+ printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+ sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+ continue;
+ }
+ if (!(*mrp)->demodulator_u8)
+ continue;
+ if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+ (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
+ sm->mode_tx = *mtp;
+ sm->mode_rx = *mrp;
+ SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
+ sm->mode_rx->srate);
+ SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
+ sm->mode_tx->srate);
+ sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+ sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+ if (sm->dma.ifragsz < sm->mode_rx->overlap)
+ sm->dma.ifragsz = sm->mode_rx->overlap;
+ sm->dma.i16bit = sm->dma.o16bit = 0;
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct sm_ioctl bi;
+ unsigned long flags;
+ int i;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+
+ if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+ HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
+ HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case SMCTL_GETMIXER:
+ i = 0;
+ bi.data.mix.sample_rate = sm->mode_rx->srate;
+ bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+ bi.data.mix.mixer_type = SM_MIXER_INVALID;
+ switch (SCSTATE->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;
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return i;
+
+ case SMCTL_SETMIXER:
+ if (!suser())
+ return -EACCES;
+ switch (SCSTATE->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;
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbc = {
+ "sbc", sizeof(struct sc_state_sbc),
+ sbc_open, sbc_close, sbc_ioctl, sbc_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm)
+{
+ unsigned long flags;
+ unsigned int isamps, osamps;
+
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ sbc_int_ack_8bit(dev);
+ sbc_int_ack_16bit(dev);
+ /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
+ write_dsp(dev, SBC_SAMPLE_RATE_IN);
+ write_dsp(dev, SCSTATE->sr[0] >> 8);
+ write_dsp(dev, SCSTATE->sr[0] & 0xff);
+ write_dsp(dev, SBC_SAMPLE_RATE_OUT);
+ write_dsp(dev, SCSTATE->sr[1] >> 8);
+ write_dsp(dev, SCSTATE->sr[1] & 0xff);
+ write_dsp(dev, SBC_SPEAKER_ON);
+ if (sm->dma.o16bit) {
+ /*
+ * DMA channel 1 (8bit) does input (capture),
+ * DMA channel 2 (16bit) does output (playback)
+ */
+ isamps = dma_setup(sm, 0, dev->dma) - 1;
+ osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
+ sbc_int_ack_8bit(dev);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC4_IN8_AI);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, isamps & 0xff);
+ write_dsp(dev, isamps >> 8);
+ write_dsp(dev, SBC4_OUT16_AI);
+ write_dsp(dev, SBC4_MODE_SIGN_MONO);
+ write_dsp(dev, osamps & 0xff);
+ write_dsp(dev, osamps >> 8);
+ } else {
+ /*
+ * DMA channel 1 (8bit) does output (playback),
+ * DMA channel 2 (16bit) does input (capture)
+ */
+ isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+ osamps = dma_setup(sm, 1, dev->dma) - 1;
+ sbc_int_ack_8bit(dev);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC4_OUT8_AI);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, osamps & 0xff);
+ write_dsp(dev, osamps >> 8);
+ write_dsp(dev, SBC4_IN16_AI);
+ write_dsp(dev, SBC4_MODE_SIGN_MONO);
+ write_dsp(dev, isamps & 0xff);
+ write_dsp(dev, isamps >> 8);
+ }
+ dma_init_receive(sm);
+ dma_init_transmit(sm);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbcfdx_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;
+ unsigned char intsrc, pbint = 0, captint = 0;
+ unsigned int ocfrag, icfrag;
+ unsigned long flags;
+
+ if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ save_flags(flags);
+ cli();
+ outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
+ intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
+ if (intsrc & 0x01) {
+ sbc_int_ack_8bit(dev);
+ if (sm->dma.o16bit) {
+ captint = 1;
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ dma_ptr(sm, 0, dev->dma, &icfrag);
+ enable_dma(dev->dma);
+ } else {
+ pbint = 1;
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ dma_ptr(sm, 1, dev->dma, &ocfrag);
+ enable_dma(dev->dma);
+ }
+ }
+ if (intsrc & 0x02) {
+ sbc_int_ack_16bit(dev);
+ if (sm->dma.o16bit) {
+ pbint = 1;
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ clear_dma_ff(sm->hdrv.ptt_out.dma2);
+ dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
+ enable_dma(sm->hdrv.ptt_out.dma2);
+ } else {
+ captint = 1;
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ clear_dma_ff(sm->hdrv.ptt_out.dma2);
+ dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
+ enable_dma(sm->hdrv.ptt_out.dma2);
+ }
+ }
+ restore_flags(flags);
+ sm_int_freq(sm);
+ sti();
+ if (pbint) {
+ if (dma_end_transmit(sm, ocfrag))
+ dma_clear_transmit(sm);
+ dma_transmit(sm);
+ }
+ if (captint) {
+ dma_receive(sm, icfrag);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ }
+ sm_output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_open(struct device *dev, struct sm_state *sm)
+{
+ int err;
+
+ if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+ printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
+ sizeof(struct sc_state_sbc), sizeof(sm->m));
+ return -ENODEV;
+ }
+ 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)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+ sm_drvname, dev->base_addr);
+ return -ENODEV;
+ }
+ write_dsp(dev, SBC_GET_REVISION);
+ if (!read_dsp(dev, &SCSTATE->revhi) ||
+ !read_dsp(dev, &SCSTATE->revlo))
+ return -ENODEV;
+ printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
+ SCSTATE->revhi, SCSTATE->revlo);
+ if (SCSTATE->revhi < 4) {
+ printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
+ return -ENODEV;
+ }
+ if ((err = config_resources(dev, sm, 1))) {
+ printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+ return err;
+ }
+ /*
+ * initialize some variables
+ */
+ if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+ kfree(sm->dma.ibuf);
+ return -ENOMEM;
+ }
+ dma_init_transmit(sm);
+ dma_init_receive(sm);
+
+ memset(&sm->m, 0, sizeof(sm->m));
+ memset(&sm->d, 0, sizeof(sm->d));
+ if (sm->mode_tx->init)
+ sm->mode_tx->init(sm);
+ if (sm->mode_rx->init)
+ sm->mode_rx->init(sm);
+
+ if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ return -EBUSY;
+ }
+ if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ free_dma(dev->dma);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT,
+ sm->hwdrv->hw_name, dev)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ free_dma(dev->dma);
+ free_dma(sm->hdrv.ptt_out.dma2);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+ setup_dma_fdx_dsp(dev, sm);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_close(struct device *dev, struct sm_state *sm)
+{
+ if (!dev || !sm)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ disable_dma(dev->dma);
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ reset_dsp(dev);
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ free_dma(sm->hdrv.ptt_out.dma2);
+ release_region(dev->base_addr, SBC_EXTENT);
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+ char *cp = strchr(mode, '.');
+ const struct modem_tx_info **mtp = sm_modem_tx_table;
+ const struct modem_rx_info **mrp;
+
+ if (!strcmp(mode, "off")) {
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return 0;
+ }
+ if (cp)
+ *cp++ = '\0';
+ else
+ cp = mode;
+ for (; *mtp; mtp++) {
+ if ((*mtp)->loc_storage > sizeof(sm->m)) {
+ printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+ sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+ continue;
+ }
+ if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+ continue;
+ if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+ continue;
+ for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+ if ((*mrp)->loc_storage > sizeof(sm->d)) {
+ printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+ sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+ continue;
+ }
+ if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+ (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
+ (*mrp)->srate == (*mtp)->srate) {
+ sm->mode_tx = *mtp;
+ sm->mode_rx = *mrp;
+ SCSTATE->sr[0] = sm->mode_rx->srate;
+ SCSTATE->sr[1] = sm->mode_tx->srate;
+ sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+ sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+ if (sm->dma.ifragsz < sm->mode_rx->overlap)
+ sm->dma.ifragsz = sm->mode_rx->overlap;
+ if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
+ sm->dma.i16bit = 1;
+ sm->dma.o16bit = 0;
+ sm->dma.ifragsz <<= 1;
+ } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
+ sm->dma.i16bit = 0;
+ sm->dma.o16bit = 1;
+ sm->dma.ofragsz <<= 1;
+ } else {
+ printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
+ sm->mode_rx->name, sm->mode_tx->name);
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return -EINVAL;
+ }
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+
+ if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+ HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE |
+ HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+ return sbc_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbcfdx = {
+ "sbcfdx", sizeof(struct sc_state_sbc),
+ sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c
new file mode 100644
index 000000000..a089544d2
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/sm_wss.c
@@ -0,0 +1,965 @@
+/*****************************************************************************/
+
+/*
+ * sm_wss.c -- soundcard radio modem driver, WSS (half duplex) 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.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+#include "smdma.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
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_wss {
+ unsigned char revwss, revid, revv, revcid;
+ unsigned char fmt[2];
+ unsigned char crystal;
+};
+
+#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+
+#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 CS423X_HOTFIX
+
+/* --------------------------------------------------------------------- */
+
+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 & 0x1f, WSS_CODEC_IA(dev->base_addr));
+ return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern void inline wss_ack_int(struct device *dev)
+{
+ outb(0, WSS_CODEC_STATUS(dev->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 device *dev, struct sm_state *sm, unsigned char fmt,
+ unsigned char fmt2, char fdx, char fullcalib)
+{
+ unsigned long time;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Clock and data format register */
+ write_codec(dev, 0x48, fmt);
+ if (SCSTATE->crystal) {
+ write_codec(dev, 0x5c, fmt2 & 0xf0);
+ /* MCE and interface config reg */
+ write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
+ } else
+ /* MCE and interface config reg */
+ write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
+ outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+ if (SCSTATE->crystal && !fullcalib)
+ return 0;
+ /*
+ * 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",
+ sm_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",
+ sm_drvname);
+ return -1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx,
+ unsigned char src_l, unsigned char src_r,
+ int igain_l, int igain_r,
+ int ogain_l, int ogain_r)
+{
+ 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 };
+
+ tmp = inb(WSS_STATUS(dev->base_addr));
+ if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
+ (tmp & 0x3f) != 0x0f) {
+ printk(KERN_WARNING "sm: WSS card id register not found, "
+ "address 0x%lx, ID register 0x%02x\n",
+ dev->base_addr, (int)tmp);
+ /* return -1; */
+ SCSTATE->revwss = 0;
+ } else {
+ if ((tmp & 0x80) && ((dev->dma == 0) ||
+ ((dev->irq >= 8) && (dev->irq != 9)))) {
+ printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+ "(except IRQ9) cannot be used on an 8bit "
+ "card\n", sm_drvname);
+ return -1;
+ }
+ if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid interrupt %d\n",
+ sm_drvname, (int)dev->irq);
+ return -1;
+ }
+ if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid dma channel %d\n",
+ sm_drvname, (int)dev->dma);
+ return -1;
+ }
+ tmp = irqtab[dev->irq] | dmatab[dev->dma];
+ /* irq probe */
+ outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
+ if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+ outb(0, WSS_CONFIG(dev->base_addr));
+ printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n",
+ sm_drvname, dev->irq);
+ }
+ outb(tmp, WSS_CONFIG(dev->base_addr));
+ SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+ }
+ /*
+ * 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);
+ write_codec(dev, 9, 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;
+ write_codec(dev, 12, 0x40); /* enable MODE2 */
+ write_codec(dev, 16, 0);
+ write_codec(dev, 0, 0x45);
+ SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
+ write_codec(dev, 0, 0xaa);
+ SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
+ if (SCSTATE->crystal) {
+ SCSTATE->revcid = read_codec(dev, 0x19);
+ SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
+ SCSTATE->revcid &= 7;
+ write_codec(dev, 0x10, 0x80); /* maximum output level */
+ write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
+ write_codec(dev, 0x12, 0x80); /* left line input control */
+ write_codec(dev, 0x13, 0x80); /* right line input control */
+ write_codec(dev, 0x16, 0); /* disable alternative freq sel */
+ write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
+ write_codec(dev, 0x1b, 0x00); /* left out no att */
+ write_codec(dev, 0x1d, 0x00); /* right out no att */
+ }
+
+ if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
+ 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 */
+ SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
+ /*
+ * print revisions
+ */
+ if (SCSTATE->crystal)
+ printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
+ " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
+ (int)SCSTATE->revv, (int)SCSTATE->revcid);
+ else
+ printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n",
+ sm_drvname, (int)SCSTATE->revwss,
+ (int)SCSTATE->revid);
+ return 0;
+ codec_err:
+ outb(0, WSS_CONFIG(dev->base_addr));
+ printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n",
+ sm_drvname, dev->base_addr);
+ return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
+{
+ unsigned long flags;
+ static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+ unsigned char oldcodecmode;
+ long abrt;
+ unsigned char fmt;
+ unsigned int numsamps;
+
+ send = !!send;
+ fmt = SCSTATE->fmt[send];
+ 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) {
+ dma_setup(sm, oldcodecmode & 1, dev->dma);
+ abrt = 0;
+ while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+ }
+#ifdef CS423X_HOTFIX
+ if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
+ wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#else /* CS423X_HOTFIX */
+ if (read_codec(dev, 0x8) != fmt)
+ wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#endif /* CS423X_HOTFIX */
+ numsamps = dma_setup(sm, send, dev->dma) - 1;
+ write_codec(dev, 15, numsamps & 0xff);
+ write_codec(dev, 14, numsamps >> 8);
+ write_codec(dev, 9, codecmode[send]);
+ 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;
+ unsigned int curfrag;
+ unsigned int nums;
+
+ if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
+ sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ cli();
+ wss_ack_int(dev);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
+ write_codec(dev, 15, nums & 0xff);
+ write_codec(dev, 14, nums >> 8);
+ enable_dma(dev->dma);
+ sm_int_freq(sm);
+ sti();
+ if (sm->dma.ptt_cnt <= 0) {
+ dma_receive(sm, curfrag);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ if (hdlcdrv_ptt(&sm->hdrv)) {
+ /* starting to transmit */
+ disable_dma(dev->dma);
+ hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+ dma_start_transmit(sm);
+ setup_dma_wss(dev, sm, 1);
+ dma_transmit(sm);
+ }
+ } else if (dma_end_transmit(sm, curfrag)) {
+ /* stopping transmission */
+ disable_dma(dev->dma);
+ dma_init_receive(sm);
+ setup_dma_wss(dev, sm, 0);
+ } else
+ dma_transmit(sm);
+ sm_output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev, struct sm_state *sm)
+{
+ unsigned int dmasz, u;
+
+ if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
+ printk(KERN_ERR "sm wss: wss state too big: %d > %d\n",
+ sizeof(struct sc_state_wss), sizeof(sm->m));
+ return -ENODEV;
+ }
+ if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+ 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, sm, 0, 1, 1, 0, 0, -45, -45))
+ return -ENODEV;
+ /*
+ * initialize some variables
+ */
+ dma_init_receive(sm);
+ dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+ u = NUM_FRAGMENTS * sm->dma.ofragsz;
+ if (u > dmasz)
+ dmasz = u;
+ if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ dma_init_transmit(sm);
+ dma_init_receive(sm);
+
+ memset(&sm->m, 0, sizeof(sm->m));
+ memset(&sm->d, 0, sizeof(sm->d));
+ if (sm->mode_tx->init)
+ sm->mode_tx->init(sm);
+ if (sm->mode_rx->init)
+ sm->mode_rx->init(sm);
+
+ if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+ kfree_s(sm->dma.obuf, dmasz);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT,
+ sm->hwdrv->hw_name, dev)) {
+ free_dma(dev->dma);
+ kfree_s(sm->dma.obuf, dmasz);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+ setup_dma_wss(dev, sm, 0);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev, struct sm_state *sm)
+{
+ 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(sm->dma.obuf);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+ char *cp = strchr(mode, '.');
+ const struct modem_tx_info **mtp = sm_modem_tx_table;
+ const struct modem_rx_info **mrp;
+ int i, j;
+
+ if (!strcmp(mode, "off")) {
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return 0;
+ }
+ if (cp)
+ *cp++ = '\0';
+ else
+ cp = mode;
+ for (; *mtp; mtp++) {
+ if ((*mtp)->loc_storage > sizeof(sm->m)) {
+ printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+ sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+ continue;
+ }
+ if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+ continue;
+ if ((i = wss_srate_index((*mtp)->srate)) < 0)
+ continue;
+ for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+ if ((*mrp)->loc_storage > sizeof(sm->d)) {
+ printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+ sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+ continue;
+ }
+ if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+ ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
+ sm->mode_tx = *mtp;
+ sm->mode_rx = *mrp;
+ SCSTATE->fmt[0] = j;
+ SCSTATE->fmt[1] = i;
+ sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+ sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+ if (sm->dma.ifragsz < sm->mode_rx->overlap)
+ sm->dma.ifragsz = sm->mode_rx->overlap;
+ /* prefer same data format if possible to minimize switching times */
+ sm->dma.i16bit = sm->dma.o16bit = 2;
+ if (sm->mode_rx->srate == sm->mode_tx->srate) {
+ if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
+ sm->dma.i16bit = sm->dma.o16bit = 1;
+ else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
+ sm->dma.i16bit = sm->dma.o16bit = 0;
+ }
+ if (sm->dma.i16bit == 2) {
+ if (sm->mode_rx->demodulator_s16)
+ sm->dma.i16bit = 1;
+ else if (sm->mode_rx->demodulator_u8)
+ sm->dma.i16bit = 0;
+ }
+ if (sm->dma.o16bit == 2) {
+ if (sm->mode_tx->modulator_s16)
+ sm->dma.o16bit = 1;
+ else if (sm->mode_tx->modulator_u8)
+ sm->dma.o16bit = 0;
+ }
+ if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
+ printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
+ sm->mode_rx->name, sm->mode_tx->name);
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return -EINVAL;
+ }
+#ifdef __BIG_ENDIAN
+ /* big endian 16bit only works on crystal cards... */
+ if (sm->dma.i16bit) {
+ SCSTATE->fmt[0] |= 0xc0;
+ sm->dma.ifragsz <<= 1;
+ }
+ if (sm->dma.o16bit) {
+ SCSTATE->fmt[1] |= 0xc0;
+ sm->dma.ofragsz <<= 1;
+ }
+#else /* __BIG_ENDIAN */
+ if (sm->dma.i16bit) {
+ SCSTATE->fmt[0] |= 0x40;
+ sm->dma.ifragsz <<= 1;
+ }
+ if (sm->dma.o16bit) {
+ SCSTATE->fmt[1] |= 0x40;
+ sm->dma.ofragsz <<= 1;
+ }
+#endif /* __BIG_ENDIAN */
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ struct sm_ioctl bi;
+ int i;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+
+ if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+ HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
+ HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case SMCTL_GETMIXER:
+ i = 0;
+ bi.data.mix.sample_rate = sm->mode_rx->srate;
+ bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+ bi.data.mix.mixer_type = SCSTATE->crystal ?
+ SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
+ if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu)
+ >> 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 ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
+ !SCSTATE->crystal) &&
+ (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
+ bi.data.mix.reg >= 0x10))
+ return -EINVAL;
+ if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
+ return -EACCES;
+ write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+ return 0;
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wss = {
+ "wss", sizeof(struct sc_state_wss),
+ wss_open, wss_close, wss_ioctl, wss_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
+{
+ unsigned long flags;
+ unsigned char oldcodecmode, codecdma;
+ long abrt;
+ unsigned int osamps, isamps;
+
+ save_flags(flags);
+ cli();
+ /*
+ * perform the final DMA sequence to disable the codec request
+ */
+ oldcodecmode = read_codec(dev, 9);
+ write_codec(dev, 9, 0); /* disable codec DMA */
+ wss_ack_int(dev);
+ if ((codecdma = read_codec(dev, 11)) & 0x10) {
+ dma_setup(sm, 1, dev->dma);
+ dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
+ abrt = 0;
+ while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
+ }
+ wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
+ osamps = dma_setup(sm, 1, dev->dma) - 1;
+ isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+ write_codec(dev, 15, osamps & 0xff);
+ write_codec(dev, 14, osamps >> 8);
+ if (SCSTATE->crystal) {
+ write_codec(dev, 31, isamps & 0xff);
+ write_codec(dev, 30, isamps >> 8);
+ }
+ write_codec(dev, 9, 3);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+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;
+ unsigned long flags;
+ unsigned char cry_int_src;
+ unsigned icfrag, ocfrag, isamps, osamps;
+
+ if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
+ sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ save_flags(flags);
+ cli();
+ if (SCSTATE->crystal) {
+ /* Crystal has an essentially different interrupt handler! */
+ cry_int_src = read_codec(dev, 0x18);
+ wss_ack_int(dev);
+ if (cry_int_src & 0x10) { /* playback interrupt */
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+ write_codec(dev, 15, osamps & 0xff);
+ write_codec(dev, 14, osamps >> 8);
+ enable_dma(dev->dma);
+ }
+ if (cry_int_src & 0x20) { /* capture interrupt */
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ clear_dma_ff(sm->hdrv.ptt_out.dma2);
+ isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+ write_codec(dev, 31, isamps & 0xff);
+ write_codec(dev, 30, isamps >> 8);
+ enable_dma(sm->hdrv.ptt_out.dma2);
+ }
+ restore_flags(flags);
+ sm_int_freq(sm);
+ sti();
+ if (cry_int_src & 0x10) {
+ if (dma_end_transmit(sm, ocfrag))
+ dma_clear_transmit(sm);
+ dma_transmit(sm);
+ }
+ if (cry_int_src & 0x20) {
+ dma_receive(sm, icfrag);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ }
+ sm_output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+ return;
+ }
+ wss_ack_int(dev);
+ disable_dma(dev->dma);
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ clear_dma_ff(dev->dma);
+ clear_dma_ff(sm->hdrv.ptt_out.dma2);
+ osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+ isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+ write_codec(dev, 15, osamps & 0xff);
+ write_codec(dev, 14, osamps >> 8);
+ if (SCSTATE->crystal) {
+ write_codec(dev, 31, isamps & 0xff);
+ write_codec(dev, 30, isamps >> 8);
+ }
+ enable_dma(dev->dma);
+ enable_dma(sm->hdrv.ptt_out.dma2);
+ restore_flags(flags);
+ sm_int_freq(sm);
+ sti();
+ if (dma_end_transmit(sm, ocfrag))
+ dma_clear_transmit(sm);
+ dma_transmit(sm);
+ dma_receive(sm, icfrag);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ sm_output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev, struct sm_state *sm)
+{
+ if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+ 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, sm, 1, 1, 1, 0, 0, -45, -45))
+ return -ENODEV;
+ /*
+ * initialize some variables
+ */
+ if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+ kfree(sm->dma.ibuf);
+ return -ENOMEM;
+ }
+ dma_init_transmit(sm);
+ dma_init_receive(sm);
+
+ memset(&sm->m, 0, sizeof(sm->m));
+ memset(&sm->d, 0, sizeof(sm->d));
+ if (sm->mode_tx->init)
+ sm->mode_tx->init(sm);
+ if (sm->mode_rx->init)
+ sm->mode_rx->init(sm);
+
+ if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ return -EBUSY;
+ }
+ if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ free_dma(dev->dma);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT,
+ sm->hwdrv->hw_name, dev)) {
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ free_dma(dev->dma);
+ free_dma(sm->hdrv.ptt_out.dma2);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+ setup_fdx_dma_wss(dev, sm);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev, struct sm_state *sm)
+{
+ if (!dev || !sm)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ disable_dma(dev->dma);
+ disable_dma(sm->hdrv.ptt_out.dma2);
+ write_codec(dev, 9, 0xc); /* disable codec */
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ free_dma(sm->hdrv.ptt_out.dma2);
+ release_region(dev->base_addr, WSS_EXTENT);
+ kfree(sm->dma.ibuf);
+ kfree(sm->dma.obuf);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+ char *cp = strchr(mode, '.');
+ const struct modem_tx_info **mtp = sm_modem_tx_table;
+ const struct modem_rx_info **mrp;
+ int i;
+
+ if (!strcmp(mode, "off")) {
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return 0;
+ }
+ if (cp)
+ *cp++ = '\0';
+ else
+ cp = mode;
+ for (; *mtp; mtp++) {
+ if ((*mtp)->loc_storage > sizeof(sm->m)) {
+ printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+ sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+ continue;
+ }
+ if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+ continue;
+ if ((i = wss_srate_index((*mtp)->srate)) < 0)
+ continue;
+ for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+ if ((*mrp)->loc_storage > sizeof(sm->d)) {
+ printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+ sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+ continue;
+ }
+ if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+ (*mtp)->srate == (*mrp)->srate) {
+ sm->mode_tx = *mtp;
+ sm->mode_rx = *mrp;
+ SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
+ sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
+ if (sm->dma.ifragsz < sm->mode_rx->overlap)
+ sm->dma.ifragsz = sm->mode_rx->overlap;
+ sm->dma.i16bit = sm->dma.o16bit = 2;
+ if (sm->mode_rx->demodulator_s16) {
+ sm->dma.i16bit = 1;
+ sm->dma.ifragsz <<= 1;
+#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
+ SCSTATE->fmt[0] |= 0xc0;
+#else /* __BIG_ENDIAN */
+ SCSTATE->fmt[0] |= 0x40;
+#endif /* __BIG_ENDIAN */
+ } else if (sm->mode_rx->demodulator_u8)
+ sm->dma.i16bit = 0;
+ if (sm->mode_tx->modulator_s16) {
+ sm->dma.o16bit = 1;
+ sm->dma.ofragsz <<= 1;
+#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
+ SCSTATE->fmt[1] |= 0xc0;
+#else /* __BIG_ENDIAN */
+ SCSTATE->fmt[1] |= 0x40;
+#endif /* __BIG_ENDIAN */
+ } else if (sm->mode_tx->modulator_u8)
+ sm->dma.o16bit = 0;
+ if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
+ printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
+ sm->mode_rx->name, sm->mode_tx->name);
+ sm->mode_tx = NULL;
+ sm->mode_rx = NULL;
+ return -EINVAL;
+ }
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr,
+ struct hdlcdrv_ioctl *hi, int cmd)
+{
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+
+ if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+ return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+ HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
+ HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
+ HDLCDRV_PARMASK_MIDIIOBASE;
+
+ return wss_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wssfdx = {
+ "wssfdx", sizeof(struct sc_state_wss),
+ wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+#undef SCSTATE
diff --git a/drivers/net/hamradio/soundmodem/smdma.h b/drivers/net/hamradio/soundmodem/smdma.h
new file mode 100644
index 000000000..44e457a7a
--- /dev/null
+++ b/drivers/net/hamradio/soundmodem/smdma.h
@@ -0,0 +1,217 @@
+/*****************************************************************************/
+
+/*
+ * smdma.h -- soundcard radio modem driver dma buffer routines.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _SMDMA_H
+#define _SMDMA_H
+
+/* ---------------------------------------------------------------------- */
+
+#include "sm.h"
+
+/* ---------------------------------------------------------------------- */
+
+#define DMA_MODE_AUTOINIT 0x10
+#define NUM_FRAGMENTS 4
+
+/*
+ * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
+ * for the modulator to fill the whole DMA buffer without underrun
+ * at the highest possible baud rate, otherwise the TX state machine will
+ * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
+ */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== DMA buffer management ===========================
+ */
+
+/*
+ * returns the number of samples per fragment
+ */
+extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
+{
+ if (send) {
+ disable_dma(dmanr);
+ clear_dma_ff(dmanr);
+ set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
+ set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
+ enable_dma(dmanr);
+ if (sm->dma.o16bit)
+ return sm->dma.ofragsz/2;
+ return sm->dma.ofragsz;
+ } else {
+ disable_dma(dmanr);
+ clear_dma_ff(dmanr);
+ set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
+ set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
+ enable_dma(dmanr);
+ if (sm->dma.i16bit)
+ return sm->dma.ifragsz/2;
+ return sm->dma.ifragsz;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
+ unsigned int *curfrag)
+{
+ unsigned int dmaptr, sz, frg, offs;
+
+ dmaptr = get_dma_residue(dmanr);
+ if (send) {
+ sz = sm->dma.ofragsz * NUM_FRAGMENTS;
+ if (dmaptr == 0 || dmaptr > sz)
+ dmaptr = sz;
+ dmaptr--;
+ frg = dmaptr / sm->dma.ofragsz;
+ offs = (dmaptr % sm->dma.ofragsz) + 1;
+ *curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+ if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+ sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+ if (sm->dma.o16bit)
+ return offs/2;
+ return offs;
+ } else {
+ sz = sm->dma.ifragsz * NUM_FRAGMENTS;
+ if (dmaptr == 0 || dmaptr > sz)
+ dmaptr = sz;
+ dmaptr--;
+ frg = dmaptr / sm->dma.ifragsz;
+ offs = (dmaptr % sm->dma.ifragsz) + 1;
+ *curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+ if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+ sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+ if (sm->dma.i16bit)
+ return offs/2;
+ return offs;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
+{
+ unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
+
+ sm->dma.ofragptr = curfrag;
+ if (sm->dma.ptt_cnt <= 0) {
+ sm->dma.ptt_cnt = 0;
+ return 0;
+ }
+ sm->dma.ptt_cnt -= diff;
+ if (sm->dma.ptt_cnt <= 0) {
+ sm->dma.ptt_cnt = 0;
+ return -1;
+ }
+ return 0;
+}
+
+extern __inline__ void dma_transmit(struct sm_state *sm)
+{
+ void *p;
+
+ while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
+ p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
+ ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
+ if (sm->dma.o16bit) {
+ time_exec(sm->debug_vals.mod_cyc,
+ sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
+ } else {
+ time_exec(sm->debug_vals.mod_cyc,
+ sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
+ }
+ sm->dma.ptt_cnt++;
+ }
+}
+
+extern __inline__ void dma_init_transmit(struct sm_state *sm)
+{
+ sm->dma.ofragptr = 0;
+ sm->dma.ptt_cnt = 0;
+}
+
+extern __inline__ void dma_start_transmit(struct sm_state *sm)
+{
+ sm->dma.ofragptr = 0;
+ if (sm->dma.o16bit) {
+ time_exec(sm->debug_vals.mod_cyc,
+ sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
+ } else {
+ time_exec(sm->debug_vals.mod_cyc,
+ sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
+ }
+ sm->dma.ptt_cnt = 1;
+}
+
+extern __inline__ void dma_clear_transmit(struct sm_state *sm)
+{
+ sm->dma.ptt_cnt = 0;
+ memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
+{
+ void *p;
+
+ while (sm->dma.ifragptr != curfrag) {
+ if (sm->dma.ifragptr)
+ p = (unsigned char *)sm->dma.ibuf +
+ sm->dma.ifragsz * sm->dma.ifragptr;
+ else {
+ p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
+ memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
+ }
+ if (sm->dma.o16bit) {
+ time_exec(sm->debug_vals.demod_cyc,
+ sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
+ } else {
+ time_exec(sm->debug_vals.demod_cyc,
+ sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
+ }
+ sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
+ }
+}
+
+extern __inline__ void dma_init_receive(struct sm_state *sm)
+{
+ sm->dma.ifragptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SMDMA_H */
+
+
+
diff --git a/drivers/net/hamradio/z8530.h b/drivers/net/hamradio/z8530.h
new file mode 100644
index 000000000..3d4a918d7
--- /dev/null
+++ b/drivers/net/hamradio/z8530.h
@@ -0,0 +1,243 @@
+
+/* 8530 Serial Communications Controller Register definitions */
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENABLE 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+
+#define PAR_ENA 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENABL 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC_HUNT 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define CRC_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* 8580/85180/85280 Enhanced SCC register definitions */
+
+/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
+#define AUTOTXF 0x01 /* Auto Tx Flag */
+#define AUTOEOM 0x02 /* Auto EOM Latch Reset */
+#define AUTORTS 0x04 /* Auto RTS */
+#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */
+#define FASTDTR 0x10 /* Fast DTR/REQ Mode */
+#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */
+#define EXTRDEN 0x40 /* Extended Read Enabled */
+
+/* Write Register 15 (external/status interrupt control) */
+#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */
+#define FIFOE 4 /* FIFO Enable */
+
+/* Read Register 6 (frame status FIFO) */
+#define BCLSB 0xff /* LSB of 14 bits count */
+
+/* Read Register 7 (frame status FIFO) */
+#define BCMSB 0x3f /* MSB of 14 bits count */
+#define FDA 0x40 /* FIFO Data Available Status */
+#define FOY 0x80 /* FIFO Overflow Status */