diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /drivers/net/hamradio | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'drivers/net/hamradio')
-rw-r--r-- | drivers/net/hamradio/Config.in | 11 | ||||
-rw-r--r-- | drivers/net/hamradio/Makefile | 10 | ||||
-rw-r--r-- | drivers/net/hamradio/baycom_epp.c | 1557 | ||||
-rw-r--r-- | drivers/net/hamradio/baycom_par.c | 10 | ||||
-rw-r--r-- | drivers/net/hamradio/baycom_ser_fdx.c | 326 | ||||
-rw-r--r-- | drivers/net/hamradio/baycom_ser_hdx.c | 111 | ||||
-rw-r--r-- | drivers/net/hamradio/bpqether.c | 1 | ||||
-rw-r--r-- | drivers/net/hamradio/dmascc.c | 5 | ||||
-rw-r--r-- | drivers/net/hamradio/hdlcdrv.c | 81 | ||||
-rw-r--r-- | drivers/net/hamradio/mkiss.c | 164 | ||||
-rw-r--r-- | drivers/net/hamradio/mkiss.h | 4 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/gentbl.c | 2 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/sm.c | 107 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/sm.h | 17 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/sm_afsk2666.c | 356 | ||||
-rw-r--r-- | drivers/net/hamradio/soundmodem/sm_psk4800.c | 418 |
17 files changed, 2724 insertions, 458 deletions
diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in index 29e182fe9..2aaa087a1 100644 --- a/drivers/net/hamradio/Config.in +++ b/drivers/net/hamradio/Config.in @@ -11,17 +11,20 @@ if [ "$CONFIG_SCC" != "n" ]; then bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO fi -dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25 -dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25 -dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_AX25 +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 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP -dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_AX25 +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 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 bool ' soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 + bool ' soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 bool ' soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 fi diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile index 07e2aade5..aeb14f32c 100644 --- a/drivers/net/hamradio/Makefile +++ b/drivers/net/hamradio/Makefile @@ -107,6 +107,16 @@ else endif endif +ifeq ($(CONFIG_BAYCOM_EPP),y) +L_OBJS += baycom_epp.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM_EPP),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom_epp.o + endif +endif + ifeq ($(CONFIG_SOUNDMODEM),y) ALL_SUB_DIRS += soundmodem SUB_DIRS += soundmodem diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c new file mode 100644 index 000000000..2a2f4e909 --- /dev/null +++ b/drivers/net/hamradio/baycom_epp.c @@ -0,0 +1,1557 @@ +/*****************************************************************************/ + +/* + * baycom_epp.c -- baycom epp radio modem driver. + * + * Copyright (C) 1998 + * 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. + * + * + * History: + * 0.1 xx.xx.98 Initial version by Matthias Welwarsky (dg2fef) + * 0.2 21.04.98 Massive rework by Thomas Sailer + * Integrated FPGA EPP modem configuration routines + * 0.3 11.05.98 Took FPGA config out and moved it into a separate program + * + */ + +/*****************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/socket.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/string.h> +#include <linux/parport.h> +#include <linux/bitops.h> +#include <linux/sched.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +//#include <net/ax25dev.h> +#include <linux/kmod.h> +#include <linux/hdlcdrv.h> +#include <linux/baycom.h> +#include <linux/soundmodem.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 */ + +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.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 +#define BAYCOM_MAGIC 19730510 + +/* --------------------------------------------------------------------- */ + +static const char paranoia_str[] = KERN_ERR +"baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; + +#define baycom_paranoia_check(dev,routine,retval) \ +({ \ + if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \ + printk(paranoia_str, routine); \ + return retval; \ + } \ +}) + +#define baycom_paranoia_check_void(dev,routine) \ +({ \ + if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \ + printk(paranoia_str, routine); \ + return; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const char bc_drvname[] = "baycom_epp"; +static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_epp: version 0.3 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 }, }; + +/* --------------------------------------------------------------------- */ + +/* EPP status register */ +#define EPP_DCDBIT 0x80 +#define EPP_PTTBIT 0x08 +#define EPP_NREF 0x01 +#define EPP_NRAEF 0x02 +#define EPP_NRHF 0x04 +#define EPP_NTHF 0x20 +#define EPP_NTAEF 0x10 +#define EPP_NTEF EPP_PTTBIT + +/* EPP control register */ +#define EPP_TX_FIFO_ENABLE 0x10 +#define EPP_RX_FIFO_ENABLE 0x08 +#define EPP_MODEM_ENABLE 0x20 +#define EPP_LEDS 0xC0 +#define EPP_IRQ_ENABLE 0x10 + +/* LPT registers */ +#define LPTREG_ECONTROL 0x402 +#define LPTREG_CONFIGB 0x401 +#define LPTREG_CONFIGA 0x400 +#define LPTREG_EPPDATA 0x004 +#define LPTREG_EPPADDR 0x003 +#define LPTREG_CONTROL 0x002 +#define LPTREG_STATUS 0x001 +#define LPTREG_DATA 0x000 + +/* LPT control register */ +#define LPTCTRL_PROGRAM 0x04 /* 0 to reprogram */ +#define LPTCTRL_WRITE 0x01 +#define LPTCTRL_ADDRSTB 0x08 +#define LPTCTRL_DATASTB 0x02 +#define LPTCTRL_INTEN 0x10 + +/* LPT status register */ +#define LPTSTAT_SHIFT_NINTR 6 +#define LPTSTAT_WAIT 0x80 +#define LPTSTAT_NINTR (1<<LPTSTAT_SHIFT_NINTR) +#define LPTSTAT_PE 0x20 +#define LPTSTAT_DONE 0x10 +#define LPTSTAT_NERROR 0x08 +#define LPTSTAT_EPPTIMEOUT 0x01 + +/* LPT data register */ +#define LPTDATA_SHIFT_TDI 0 +#define LPTDATA_SHIFT_TMS 2 +#define LPTDATA_TDI (1<<LPTDATA_SHIFT_TDI) +#define LPTDATA_TCK 0x02 +#define LPTDATA_TMS (1<<LPTDATA_SHIFT_TMS) +#define LPTDATA_INITBIAS 0x80 + + +/* EPP modem config/status bits */ +#define EPP_DCDBIT 0x80 +#define EPP_PTTBIT 0x08 +#define EPP_RXEBIT 0x01 +#define EPP_RXAEBIT 0x02 +#define EPP_RXHFULL 0x04 + +#define EPP_NTHF 0x20 +#define EPP_NTAEF 0x10 +#define EPP_NTEF EPP_PTTBIT + +#define EPP_TX_FIFO_ENABLE 0x10 +#define EPP_RX_FIFO_ENABLE 0x08 +#define EPP_MODEM_ENABLE 0x20 +#define EPP_LEDS 0xC0 +#define EPP_IRQ_ENABLE 0x10 + +/* Xilinx 4k JTAG instructions */ +#define XC4K_IRLENGTH 3 +#define XC4K_EXTEST 0 +#define XC4K_PRELOAD 1 +#define XC4K_CONFIGURE 5 +#define XC4K_BYPASS 7 + +#define EPP_CONVENTIONAL 0 +#define EPP_FPGA 1 +#define EPP_FPGAEXTSTATUS 2 + +#define TXBUFFER_SIZE ((HDLCDRV_MAXFLEN*6/5)+8) + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct baycom_state { + int magic; + + struct pardevice *pdev; + unsigned int bh_running; + struct tq_struct run_bh; + unsigned int modem; + unsigned int bitrate; + unsigned char stat; + + char ifname[HDLCDRV_IFNAMELEN]; + + struct { + unsigned int intclk; + unsigned int divider; + unsigned int extmodem; + unsigned int loopback; + } cfg; + + struct hdlcdrv_channel_params ch_params; + + struct { + unsigned int bitbuf, bitstream, numbits, state; + unsigned char *bufptr; + int bufcnt; + unsigned char buf[TXBUFFER_SIZE]; + } hdlcrx; + + struct { + int calibrate; + int slotcnt; + int flags; + enum { tx_idle = 0, tx_keyup, tx_data, tx_tail } state; + unsigned char *bufptr; + int bufcnt; + unsigned char buf[TXBUFFER_SIZE]; + } hdlctx; + + struct net_device_stats stats; + unsigned int ptt_keyed; + struct sk_buff_head send_queue; /* Packets awaiting transmission */ + + +#ifdef BAYCOM_DEBUG + struct debug_vals { + unsigned long last_jiffies; + unsigned cur_intcnt; + unsigned last_intcnt; + int cur_pllcorr; + int last_pllcorr; + unsigned int mod_cycles; + unsigned int demod_cycles; + } debug_vals; +#endif /* BAYCOM_DEBUG */ +}; + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ + +#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 + +/* --------------------------------------------------------------------- */ +/* + * 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 +}; + +/*---------------------------------------------------------------------------*/ + +#if 0 +extern 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; +} +#endif + +/*---------------------------------------------------------------------------*/ + +extern 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; +} + +/*---------------------------------------------------------------------------*/ + +extern inline 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); +} + +/* ---------------------------------------------------------------------- */ + +#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) + +/* --------------------------------------------------------------------- */ + +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 */ +} + +/* ---------------------------------------------------------------------- */ +/* + * eppconfig_path should be setable via /proc/sys. + */ + +char eppconfig_path[256] = "/sbin/eppfpga"; + +static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; + +static int errno; + +static int exec_eppfpga(void *b) +{ + struct baycom_state *bc = (struct baycom_state *)b; + char modearg[256]; + char portarg[16]; + char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, NULL}; + int i; + + /* set up arguments */ + sprintf(modearg, "%sclk,%smodem,divider=%d%s,extstat", + bc->cfg.intclk ? "int" : "ext", + bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider, + bc->cfg.loopback ? ",loopback" : ""); + sprintf(portarg, "%ld", bc->pdev->port->base); + printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); + + for (i = 0; i < current->files->max_fds; i++ ) + if (current->files->fd[i]) + close(i); + set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */ + current->uid = current->euid = current->fsuid = 0; + if (execve(eppconfig_path, argv, envp) < 0) { + printk(KERN_ERR "%s: failed to exec %s -s -p %s -m %s, errno = %d\n", + bc_drvname, eppconfig_path, portarg, modearg, errno); + return -errno; + } + return 0; +} + + +/* eppconfig: called during ifconfig up to configure the modem */ + +static int eppconfig(struct baycom_state *bc) +{ + int i, pid, r; + mm_segment_t fs; + + pid = kernel_thread(exec_eppfpga, bc, CLONE_FS); + if (pid < 0) { + printk(KERN_ERR "%s: fork failed, errno %d\n", bc_drvname, -pid); + return pid; + } + fs = get_fs(); + set_fs(KERNEL_DS); /* Allow i to be in kernel space. */ + r = waitpid(pid, &i, __WCLONE); + set_fs(fs); + if (r != pid) { + printk(KERN_ERR "%s: waitpid(%d) failed, returning %d\n", + bc_drvname, pid, r); + return -1; + } + printk(KERN_DEBUG "%s: eppfpga returned %d\n", bc_drvname, i); + return i; +} + +/* ---------------------------------------------------------------------- */ + +static void epp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +/* ---------------------------------------------------------------------- */ + +static void inline do_kiss_params(struct baycom_state *bc, + unsigned char *data, unsigned long len) +{ + +#ifdef KISS_VERBOSE +#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", bc->ifname, b) +#else /* KISS_VERBOSE */ +#define PKP(a,b) +#endif /* KISS_VERBOSE */ + + if (len < 2) + return; + switch(data[0]) { + case PARAM_TXDELAY: + bc->ch_params.tx_delay = data[1]; + PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); + break; + case PARAM_PERSIST: + bc->ch_params.ppersist = data[1]; + PKP("p persistence = %u", bc->ch_params.ppersist); + break; + case PARAM_SLOTTIME: + bc->ch_params.slottime = data[1]; + PKP("slot time = %ums", bc->ch_params.slottime); + break; + case PARAM_TXTAIL: + bc->ch_params.tx_tail = data[1]; + PKP("TX tail = %ums", bc->ch_params.tx_tail); + break; + case PARAM_FULLDUP: + bc->ch_params.fulldup = !!data[1]; + PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); + break; + default: + break; + } +#undef PKP +} + +/* --------------------------------------------------------------------- */ +/* + * high performance HDLC encoder + * yes, it's ugly, but generates pretty good code + */ + +#define ENCODEITERA(j) \ +({ \ + if (!(notbitstream & (0x1f0 << j))) \ + goto stuff##j; \ + encodeend##j: \ +}) + +#define ENCODEITERB(j) \ +({ \ + stuff##j: \ + bitstream &= ~(0x100 << j); \ + bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | \ + ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); \ + numbit++; \ + notbitstream = ~bitstream; \ + goto encodeend##j; \ +}) + + +static void encode_hdlc(struct baycom_state *bc) +{ + struct sk_buff *skb; + unsigned char *wp, *bp; + int pkt_len; + unsigned bitstream, notbitstream, bitbuf, numbit, crc; + unsigned char crcarr[2]; + + if (bc->hdlctx.bufcnt > 0) + return; + while ((skb = skb_dequeue(&bc->send_queue))) { + if (skb->data[0] != 0) { + do_kiss_params(bc, skb->data, skb->len); + dev_kfree_skb(skb); + continue; + } + pkt_len = skb->len-1; /* strip KISS byte */ + if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { + dev_kfree_skb(skb); + continue; + } + wp = bc->hdlctx.buf; + bp = skb->data+1; + crc = calc_crc_ccitt(bp, pkt_len); + crcarr[0] = crc; + crcarr[1] = crc >> 8; + *wp++ = 0x7e; + bitstream = bitbuf = numbit = 0; + while (pkt_len > -2) { + bitstream >>= 8; + bitstream |= ((unsigned int)*bp) << 8; + bitbuf |= ((unsigned int)*bp) << numbit; + notbitstream = ~bitstream; + bp++; + pkt_len--; + if (!pkt_len) + bp = crcarr; + ENCODEITERA(0); + ENCODEITERA(1); + ENCODEITERA(2); + ENCODEITERA(3); + ENCODEITERA(4); + ENCODEITERA(5); + ENCODEITERA(6); + ENCODEITERA(7); + goto enditer; + ENCODEITERB(0); + ENCODEITERB(1); + ENCODEITERB(2); + ENCODEITERB(3); + ENCODEITERB(4); + ENCODEITERB(5); + ENCODEITERB(6); + ENCODEITERB(7); + enditer: + numbit += 8; + while (numbit >= 8) { + *wp++ = bitbuf; + bitbuf >>= 8; + numbit -= 8; + } + } + bitbuf |= 0x7e7e << numbit; + numbit += 16; + while (numbit >= 8) { + *wp++ = bitbuf; + bitbuf >>= 8; + numbit -= 8; + } + bc->hdlctx.bufptr = bc->hdlctx.buf; + bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; + dev_kfree_skb(skb); + bc->stats.tx_packets++; + return; + } +} + +/* ---------------------------------------------------------------------- */ + +static unsigned short random_seed; + +static inline unsigned short random_num(void) +{ + random_seed = 28629 * random_seed + 157; + return random_seed; +} + +/* ---------------------------------------------------------------------- */ + +static void transmit(struct baycom_state *bc, int cnt, unsigned char stat) +{ + struct parport *pp = bc->pdev->port; + int i; + + if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) + bc->hdlctx.state = tx_idle; + if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { + if (bc->hdlctx.bufcnt <= 0) + encode_hdlc(bc); + if (bc->hdlctx.bufcnt <= 0) + return; + if (!bc->ch_params.fulldup) { + if (!(stat & EPP_DCDBIT)) { + bc->hdlctx.slotcnt = bc->ch_params.slottime; + return; + } + if ((--bc->hdlctx.slotcnt) > 0) + return; + bc->hdlctx.slotcnt = bc->ch_params.slottime; + if ((random_num() % 256) > bc->ch_params.ppersist) + return; + } + } + if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { + bc->hdlctx.state = tx_keyup; + bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); + bc->ptt_keyed++; + } + while (cnt > 0) { + switch (bc->hdlctx.state) { + case tx_keyup: + i = min(cnt, bc->hdlctx.flags); + cnt -= i; + bc->hdlctx.flags -= i; + if (bc->hdlctx.flags <= 0) + bc->hdlctx.state = tx_data; + for (; i > 0; i--) + parport_epp_write_data(pp, 0x7e); + break; + + case tx_data: + if (bc->hdlctx.bufcnt <= 0) { + encode_hdlc(bc); + if (bc->hdlctx.bufcnt <= 0) { + bc->hdlctx.state = tx_tail; + bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); + break; + } + } + i = min(cnt, bc->hdlctx.bufcnt); + bc->hdlctx.bufcnt -= i; + cnt -= i; + for (; i > 0; i--) + parport_epp_write_data(pp, *(bc->hdlctx.bufptr)++); + break; + + case tx_tail: + encode_hdlc(bc); + if (bc->hdlctx.bufcnt > 0) { + bc->hdlctx.state = tx_data; + break; + } + i = min(cnt, bc->hdlctx.flags); + if (i) { + cnt -= i; + bc->hdlctx.flags -= i; + for (; i > 0; i--) + parport_epp_write_data(pp, 0x7e); + break; + } + + default: /* fall through */ + if (bc->hdlctx.calibrate <= 0) + return; + i = min(cnt, bc->hdlctx.calibrate); + cnt -= i; + bc->hdlctx.calibrate -= i; + for (; i > 0; i--) + parport_epp_write_data(pp, 0); + break; + } + } +} + +/* ---------------------------------------------------------------------- */ + +static void do_rxpacket(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + struct sk_buff *skb; + unsigned char *cp; + unsigned pktlen; + + if (bc->hdlcrx.bufcnt < 4) + return; + if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) + return; + pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ + if (!(skb = dev_alloc_skb(pktlen))) { + printk("%s: memory squeeze, dropping packet\n", bc->ifname); + bc->stats.rx_dropped++; + return; + } + skb->dev = dev; + cp = skb_put(skb, pktlen); + *cp++ = 0; /* KISS kludge */ + memcpy(cp, bc->hdlcrx.buf, pktlen - 1); + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + netif_rx(skb); + bc->stats.rx_packets++; +} + +#define DECODEITERA(j) \ +({ \ + if (!(notbitstream & (0x0fc << j))) /* flag or abort */ \ + goto flgabrt##j; \ + if ((bitstream & (0x1f8 << j)) == (0xf8 << j)) /* stuffed bit */ \ + goto stuff##j; \ + enditer##j: \ +}) + +#define DECODEITERB(j) \ +({ \ + flgabrt##j: \ + if (!(notbitstream & (0x1fc << j))) { /* abort received */ \ + state = 0; \ + goto enditer##j; \ + } \ + if ((bitstream & (0x1fe << j)) != (0x0fc << j)) /* flag received */ \ + goto enditer##j; \ + if (state) \ + do_rxpacket(dev); \ + bc->hdlcrx.bufcnt = 0; \ + bc->hdlcrx.bufptr = bc->hdlcrx.buf; \ + state = 1; \ + numbits = 7-j; \ + goto enditer##j; \ + stuff##j: \ + numbits--; \ + bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); \ + goto enditer##j; \ +}) + +static void receive(struct device *dev, int cnt) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + struct parport *pp = bc->pdev->port; + unsigned int bitbuf, notbitstream, bitstream, numbits, state; + unsigned char ch; + + numbits = bc->hdlcrx.numbits; + state = bc->hdlcrx.state; + bitstream = bc->hdlcrx.bitstream; + bitbuf = bc->hdlcrx.bitbuf; + for (; cnt > 0; cnt--) { + ch = parport_epp_read_data(pp); + bitstream >>= 8; + bitstream |= ch << 8; + bitbuf >>= 8; + bitbuf |= ch << 8; + numbits += 8; + notbitstream = ~bitstream; + DECODEITERA(0); + DECODEITERA(1); + DECODEITERA(2); + DECODEITERA(3); + DECODEITERA(4); + DECODEITERA(5); + DECODEITERA(6); + DECODEITERA(7); + goto enddec; + DECODEITERB(0); + DECODEITERB(1); + DECODEITERB(2); + DECODEITERB(3); + DECODEITERB(4); + DECODEITERB(5); + DECODEITERB(6); + DECODEITERB(7); + enddec: + while (state && numbits >= 8) { + if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { + state = 0; + } else { + *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); + bc->hdlcrx.bufcnt++; + numbits -= 8; + } + } + } + bc->hdlcrx.numbits = numbits; + bc->hdlcrx.state = state; + bc->hdlcrx.bitstream = bitstream; + bc->hdlcrx.bitbuf = bitbuf; +} + +/* --------------------------------------------------------------------- */ + +#ifdef __i386__ +#define GETTICK(x) \ +({ \ + if (current_cpu_data.x86_capability & X86_FEATURE_TSC) \ + __asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\ +}) +#else /* __i386__ */ +#define GETTICK(x) +#endif /* __i386__ */ + +static void epp_bh(struct device *dev) +{ + struct baycom_state *bc; + struct parport *pp; + unsigned char stat; + unsigned int time1 = 0, time2 = 0, time3 = 0; + int cnt, cnt2; + + baycom_paranoia_check_void(dev, "epp_bh"); + bc = (struct baycom_state *)dev->priv; + if (!bc->bh_running) + return; + baycom_int_freq(bc); + pp = bc->pdev->port; + /* update status */ + bc->stat = stat = parport_epp_read_addr(pp); + bc->debug_vals.last_pllcorr = stat; + GETTICK(time1); + if (bc->modem == EPP_FPGAEXTSTATUS) { + /* get input count */ + parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1); + cnt = parport_epp_read_addr(pp); + cnt |= parport_epp_read_addr(pp) << 8; + cnt &= 0x7fff; + /* get output count */ + parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2); + cnt2 = parport_epp_read_addr(pp); + cnt2 |= parport_epp_read_addr(pp) << 8; + cnt2 = 16384 - (cnt2 & 0x7fff); + /* return to normal */ + parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE); + transmit(bc, cnt2, stat); + GETTICK(time2); + receive(dev, cnt); + bc->stat = stat = parport_epp_read_addr(pp); + } else { + /* try to tx */ + switch (stat & (EPP_NTAEF|EPP_NTHF)) { + case EPP_NTHF: + cnt = 2048 - 256; + break; + + case EPP_NTAEF: + cnt = 2048 - 1793; + break; + + case 0: + cnt = 0; + break; + + default: + cnt = 2048 - 1025; + break; + } + transmit(bc, cnt, stat); + GETTICK(time2); + /* do receiver */ + while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { + switch (stat & (EPP_NRAEF|EPP_NRHF)) { + case EPP_NRAEF: + cnt = 1025; + break; + + case 0: + cnt = 1793; + break; + + default: + cnt = 256; + break; + } + receive(dev, cnt); + stat = parport_epp_read_addr(pp); + if (parport_epp_check_timeout(pp)) + goto epptimeout; + } + cnt = 0; + if (bc->bitrate < 50000) + cnt = 256; + else if (bc->bitrate < 100000) + cnt = 128; + while (cnt > 0 && stat & EPP_NREF) { + receive(dev, 1); + cnt--; + stat = parport_epp_read_addr(pp); + } + } + GETTICK(time3); +#ifdef BAYCOM_DEBUG + bc->debug_vals.mod_cycles = time2 - time1; + bc->debug_vals.demod_cycles = time3 - time2; +#endif /* BAYCOM_DEBUG */ + if (parport_epp_check_timeout(pp)) + goto epptimeout; + queue_task(&bc->run_bh, &tq_timer); + return; + epptimeout: + printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); +} + +/* ---------------------------------------------------------------------- */ +/* + * ===================== network driver interface ========================= + */ + +static int baycom_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct baycom_state *bc; + + baycom_paranoia_check(dev, "baycom_send_packet", 0); + bc = (struct baycom_state *)dev->priv; + skb_queue_tail(&bc->send_queue, skb); + dev->trans_start = jiffies; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int baycom_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; +} + +/* --------------------------------------------------------------------- */ + +static struct net_device_stats *baycom_get_stats(struct device *dev) +{ + struct baycom_state *bc; + + baycom_paranoia_check(dev, "baycom_get_stats", NULL); + bc = (struct baycom_state *)dev->priv; + /* + * Get the current statistics. This may be called with the + * card open or closed. + */ + return &bc->stats; +} + +/* --------------------------------------------------------------------- */ + +static int epp_preempt(void *handle) +{ + /* we cannot relinquish the port in the middle of an operation */ + return 1; +} + +/* --------------------------------------------------------------------- */ + +static void epp_wakeup(void *handle) +{ + struct device *dev = (struct device *)handle; + struct baycom_state *bc; + + baycom_paranoia_check_void(dev, "epp_wakeup"); + bc = (struct baycom_state *)dev->priv; + printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); + if (!parport_claim(bc->pdev)) + printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); +} + +/* --------------------------------------------------------------------- */ + +/* + * 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 epp_open(struct device *dev) +{ + struct baycom_state *bc; + struct parport *pp; + const struct tq_struct run_bh = { + 0, 0, (void *)(void *)epp_bh, dev + }; + unsigned int i, j; + unsigned char stat; + unsigned long tstart; + + baycom_paranoia_check(dev, "epp_open", -ENXIO); + bc = (struct baycom_state *)dev->priv; + if (dev->start) + return 0; + pp = parport_enumerate(); + while (pp && pp->base != dev->base_addr) + pp = pp->next; + if (!pp) { + printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); + return -ENXIO; + } +#if 0 + if (pp->irq < 0) { + printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); + return -ENXIO; + } +#endif + memset(&bc->modem, 0, sizeof(bc->modem)); + if (!(bc->pdev = parport_register_device(pp, dev->name, epp_preempt, epp_wakeup, + epp_interrupt, PARPORT_DEV_LURK, dev))) { + printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); + return -ENXIO; + } + if (parport_claim(bc->pdev)) { + printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); + parport_unregister_device(bc->pdev); + return -EBUSY; + } + if (!(pp->modes & (PARPORT_MODE_PCECPEPP|PARPORT_MODE_PCEPP))) { + printk(KERN_ERR "%s: parport at 0x%lx does not support any EPP mode\n", + bc_drvname, pp->base); + parport_unregister_device(bc->pdev); + return -EIO; + } + dev->irq = /*pp->irq*/ 0; + bc->run_bh = run_bh; + bc->bh_running = 1; + if (pp->modes & PARPORT_MODE_PCECPEPP) { + printk(KERN_INFO "%s: trying to enable EPP mode\n", bc_drvname); + parport_frob_econtrol(pp, 0xe0, 0x80); + } + /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCEPP); not yet implemented */ + bc->modem = EPP_CONVENTIONAL; + if (eppconfig(bc)) + printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); + else + bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; + parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ + /* reset the modem */ + parport_epp_write_addr(pp, 0); + parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE); + /* autoprobe baud rate */ + tstart = jiffies; + i = 0; + while ((signed)(jiffies-tstart-HZ/3) < 0) { + stat = parport_epp_read_addr(pp); + if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { + schedule(); + continue; + } + for (j = 0; j < 256; j++) + parport_epp_read_data(pp); + i += 256; + } + for (j = 0; j < 256; j++) { + stat = parport_epp_read_addr(pp); + if (!(stat & EPP_NREF)) + break; + parport_epp_read_data(pp); + i++; + } + tstart = jiffies - tstart; + bc->bitrate = i * (8 * HZ) / tstart; + j = 1; + i = bc->bitrate >> 3; + while (j < 7 && i > 150) { + j++; + i >>= 1; + } + printk(KERN_INFO "%s: autoprobed bitrate: %d int divider: %d int rate: %d\n", + bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); + parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/); + /* + * initialise hdlc variables + */ + bc->hdlcrx.state = 0; + bc->hdlcrx.numbits = 0; + bc->hdlctx.state = tx_idle; + bc->hdlctx.bufcnt = 0; + bc->hdlctx.slotcnt = bc->ch_params.slottime; + bc->hdlctx.calibrate = 0; + dev->start = 1; + dev->tbusy = 0; + dev->interrupt = 0; + /* start the bottom half stuff */ + queue_task(&bc->run_bh, &tq_timer); + MOD_INC_USE_COUNT; + return 0; + +#if 0 + errreturn: + parport_release(bc->pdev); + parport_unregister_device(bc->pdev); + return -EIO; +#endif +} + +/* --------------------------------------------------------------------- */ + +static int epp_close(struct device *dev) +{ + struct baycom_state *bc; + struct parport *pp; + struct sk_buff *skb; + + baycom_paranoia_check(dev, "epp_close", -EINVAL); + if (!dev->start) + return 0; + bc = (struct baycom_state *)dev->priv; + pp = bc->pdev->port; + bc->bh_running = 0; + dev->start = 0; + dev->tbusy = 1; + run_task_queue(&tq_timer); /* dequeue bottom half */ + bc->stat = EPP_DCDBIT; + parport_epp_write_addr(pp, 0); + parport_write_control(pp, 0); /* reset the adapter */ + parport_release(bc->pdev); + parport_unregister_device(bc->pdev); + /* Free any buffers left in the hardware transmit queue */ + while ((skb = skb_dequeue(&bc->send_queue))) + dev_kfree_skb(skb); + printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", + bc_drvname, dev->base_addr, dev->irq); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int baycom_setmode(struct baycom_state *bc, const char *modestr) +{ + const char *cp; + + if (strstr(modestr,"intclk")) + bc->cfg.intclk = 1; + if (strstr(modestr,"extclk")) + bc->cfg.intclk = 0; + if (strstr(modestr,"intmodem")) + bc->cfg.extmodem = 0; + if (strstr(modestr,"extmodem")) + bc->cfg.extmodem = 1; + if (strstr(modestr,"noloopback")) + bc->cfg.loopback = 0; + if (strstr(modestr,"loopback")) + bc->cfg.loopback = 1; + if ((cp = strstr(modestr,"divider="))) { + bc->cfg.divider = simple_strtoul(cp+8, NULL, 0); + if (bc->cfg.divider < 1) + bc->cfg.divider = 1; + if (bc->cfg.divider > 1023) + bc->cfg.divider = 1023; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct baycom_state *bc; + struct baycom_ioctl bi; + struct hdlcdrv_ioctl hi; + struct sm_ioctl si; + + baycom_paranoia_check(dev, "baycom_ioctl", -EINVAL); + bc = (struct baycom_state *)dev->priv; + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + if (get_user(cmd, (int *)ifr->ifr_data)) + return -EFAULT; +#ifdef BAYCOM_DEBUG + if (cmd == BAYCOMCTL_GETDEBUG) { + bi.data.dbg.debug1 = bc->ptt_keyed; + bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; + bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; + bc->debug_vals.last_intcnt = 0; + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + } + if (cmd == SMCTL_GETDEBUG) { + si.data.dbg.int_rate = bc->debug_vals.last_intcnt; + si.data.dbg.mod_cycles = bc->debug_vals.mod_cycles; + si.data.dbg.demod_cycles = bc->debug_vals.demod_cycles; + si.data.dbg.dma_residue = 0; + bc->debug_vals.mod_cycles = bc->debug_vals.demod_cycles = 0; + bc->debug_vals.last_intcnt = 0; + if (copy_to_user(ifr->ifr_data, &si, sizeof(si))) + return -EFAULT; + return 0; + } +#endif /* BAYCOM_DEBUG */ + + if (copy_from_user(&hi, ifr->ifr_data, sizeof(hi))) + return -EFAULT; + switch (hi.cmd) { + default: + return -ENOIOCTLCMD; + + case HDLCDRVCTL_GETCHANNELPAR: + hi.data.cp.tx_delay = bc->ch_params.tx_delay; + hi.data.cp.tx_tail = bc->ch_params.tx_tail; + hi.data.cp.slottime = bc->ch_params.slottime; + hi.data.cp.ppersist = bc->ch_params.ppersist; + hi.data.cp.fulldup = bc->ch_params.fulldup; + break; + + case HDLCDRVCTL_SETCHANNELPAR: + if (!suser()) + return -EACCES; + bc->ch_params.tx_delay = hi.data.cp.tx_delay; + bc->ch_params.tx_tail = hi.data.cp.tx_tail; + bc->ch_params.slottime = hi.data.cp.slottime; + bc->ch_params.ppersist = hi.data.cp.ppersist; + bc->ch_params.fulldup = hi.data.cp.fulldup; + bc->hdlctx.slotcnt = 1; + return 0; + + case HDLCDRVCTL_GETMODEMPAR: + hi.data.mp.iobase = dev->base_addr; + hi.data.mp.irq = dev->irq; + hi.data.mp.dma = dev->dma; + hi.data.mp.dma2 = 0; + hi.data.mp.seriobase = 0; + hi.data.mp.pariobase = 0; + hi.data.mp.midiiobase = 0; + break; + + case HDLCDRVCTL_SETMODEMPAR: + if ((!suser()) || dev->start) + return -EACCES; + dev->base_addr = hi.data.mp.iobase; + dev->irq = /*hi.data.mp.irq*/0; + dev->dma = /*hi.data.mp.dma*/0; + return 0; + + case HDLCDRVCTL_GETSTAT: + hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); + hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); + hi.data.cs.ptt_keyed = bc->ptt_keyed; + hi.data.cs.tx_packets = bc->stats.tx_packets; + hi.data.cs.tx_errors = bc->stats.tx_errors; + hi.data.cs.rx_packets = bc->stats.rx_packets; + hi.data.cs.rx_errors = bc->stats.rx_errors; + break; + + case HDLCDRVCTL_OLDGETSTAT: + hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); + hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); + hi.data.ocs.ptt_keyed = bc->ptt_keyed; + break; + + case HDLCDRVCTL_CALIBRATE: + bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; + return 0; + + case HDLCDRVCTL_DRIVERNAME: + strncpy(hi.data.drivername, "baycom_epp", sizeof(hi.data.drivername)); + break; + + case HDLCDRVCTL_GETMODE: + sprintf(hi.data.modename, "%sclk,%smodem,divider=%d%s", + bc->cfg.intclk ? "int" : "ext", + bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider, + bc->cfg.loopback ? ",loopback" : ""); + break; + + 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: + strncpy(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x", + sizeof(hi.data.modename)); + break; + + case HDLCDRVCTL_MODEMPARMASK: + return HDLCDRV_PARMASK_IOBASE; + + } + if (copy_to_user(ifr->ifr_data, &hi, sizeof(hi))) + 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 baycom_probe(struct device *dev) +{ + 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 + }; + const struct hdlcdrv_channel_params dflt_ch_params = { + 20, 2, 10, 40, 0 + }; + struct baycom_state *bc; + + if (!dev) + return -ENXIO; + baycom_paranoia_check(dev, "baycom_probe", -ENXIO); + /* + * not a real probe! only initialize data structures + */ + bc = (struct baycom_state *)dev->priv; + /* + * initialize the baycom_state struct + */ + bc->ch_params = dflt_ch_params; + bc->ptt_keyed = 0; + + /* + * initialize the device struct + */ + dev->open = epp_open; + dev->stop = epp_close; + dev->do_ioctl = baycom_ioctl; + dev->hard_start_xmit = baycom_send_packet; + dev->get_stats = baycom_get_stats; + + /* Fill in the fields of the device structure */ + dev_init_buffers(dev); + + skb_queue_head_init(&bc->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 = baycom_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; +} + +/* --------------------------------------------------------------------- */ + +__initfunc(int baycom_epp_init(void)) +{ + struct device *dev; + int i, found = 0; + char set_hw = 1; + struct baycom_state *bc; + + printk(bc_drvinfo); + /* + * register net devices + */ + for (i = 0; i < NR_PORTS; i++) { + dev = baycom_device+i; + if (!baycom_ports[i].mode) + set_hw = 0; + if (!set_hw) + baycom_ports[i].iobase = 0; + memset(dev, 0, sizeof(struct device)); + if (!(bc = dev->priv = kmalloc(sizeof(struct baycom_state), GFP_KERNEL))) + return -ENOMEM; + /* + * initialize part of the baycom_state struct + */ + memset(bc, 0, sizeof(struct baycom_state)); + bc->magic = BAYCOM_MAGIC; + sprintf(bc->ifname, "bce%d", i); + /* + * initialize part of the device struct + */ + dev->name = bc->ifname; + dev->if_port = 0; + dev->init = baycom_probe; + dev->start = 0; + dev->tbusy = 1; + dev->base_addr = baycom_ports[i].iobase; + dev->irq = 0; + dev->dma = 0; + if (register_netdev(dev)) { + printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, bc->ifname); + kfree(dev->priv); + return -ENXIO; + } + if (set_hw && baycom_setmode(bc, baycom_ports[i].mode)) + set_hw = 0; + found++; + } + if (!found) + return -ENXIO; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * command line settable parameters + */ +static const char *mode[NR_PORTS] = { "epp", }; +static int iobase[NR_PORTS] = { 0x378, }; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "s"); +MODULE_PARM_DESC(mode, "baycom operating mode; epp"); +MODULE_PARM(iobase, "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 epp 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_epp_init(); +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + struct device *dev; + struct baycom_state *bc; + int i; + + for(i = 0; i < NR_PORTS; i++) { + dev = baycom_device+i; + bc = (struct baycom_state *)dev->priv; + if (bc) { + if (bc->magic == BAYCOM_MAGIC) { + unregister_netdev(dev); + kfree(dev->priv); + } else + printk(paranoia_str, "cleanup_module"); + } + } +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ +/* + * format: baycom=io,mode + * mode: epp + */ + +__initfunc(void baycom_epp_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].irq = ints[1]; + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 25fcd137e..e79a00ef6 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -329,9 +329,7 @@ static __inline__ void par96_rx(struct device *dev, struct baycom_state *bc) 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 device *dev = (struct device *)dev_id; struct baycom_state *bc = (struct baycom_state *)dev->priv; if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) @@ -347,13 +345,14 @@ static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) par96_rx(dev, bc); if (--bc->modem.arb_divider <= 0) { bc->modem.arb_divider = 6; - sti(); + __sti(); hdlcdrv_arbitrate(dev, &bc->hdrv); } } - sti(); + __sti(); hdlcdrv_transmitter(dev, &bc->hdrv); hdlcdrv_receiver(dev, &bc->hdrv); + __cli(); } /* --------------------------------------------------------------------- */ @@ -409,6 +408,7 @@ static int par96_open(struct device *dev) } dev->irq = pp->irq; /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP); not yet implemented */ + bc->hdrv.par.bitrate = 9600; /* switch off PTT */ outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev)); /*bc->pdev->port->ops->enable_irq(bc->pdev->port); not yet implemented */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index b70e4efde..cf6d0dfeb 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -3,7 +3,7 @@ /* * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver. * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1997-1998 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 @@ -34,6 +34,14 @@ * port, the kernel driver for serial ports cannot be used, and this * driver only supports standard serial hardware (8250, 16450, 16550A) * + * This modem usually draws its supply current out of the otherwise unused + * TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes + * is transmitted to achieve a positive supply voltage. + * + * hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine + * in 'baycom-mode' :-) In contrast to the TCM3105 modem, power is + * externally supplied. So there's no need to provide the 0x00-byte-stream + * when receiving or idle, which drastically reduces interrupt load. * * Command line options (insmod command line) * @@ -49,6 +57,9 @@ * 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 + * 0.6 24.01.98 Thorsten Kranzkowski, dl8bcu and Thomas Sailer: + * reduced interrupt load in transmit case + * reworked receiver */ /*****************************************************************************/ @@ -62,8 +73,10 @@ #include <linux/ioport.h> #include <linux/in.h> #include <linux/string.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <asm/uaccess.h> #include <asm/system.h> -#include <asm/bitops.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/errno.h> @@ -73,56 +86,6 @@ /* --------------------------------------------------------------------- */ -/* - * 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 /* @@ -133,8 +96,8 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n) /* --------------------------------------------------------------------- */ 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"; +static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997-1998 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_ser_fdx: version 0.6 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -172,22 +135,18 @@ static struct { struct baycom_state { struct hdlcdrv_state hdrv; - unsigned int baud, baud_us8, baud_arbdiv; + unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout; unsigned int options; struct modem_state { - short arb_divider; unsigned char flags; + unsigned char ptt; 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; + int dcd_sum0, dcd_sum1, dcd_sum2; + int dcd_time; unsigned int pll_time; unsigned int txshreg; } ser12; @@ -236,12 +195,36 @@ static void inline baycom_int_freq(struct baycom_state *bc) /* --------------------------------------------------------------------- */ -extern inline unsigned int hweight16(unsigned short w) +static inline void ser12_set_divisor(struct device *dev, + unsigned int divisor) +{ + outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ + outb(divisor, DLL(dev->base_addr)); + outb(divisor >> 8, 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) + */ +} + +/* --------------------------------------------------------------------- */ + +#if 0 +extern inline unsigned int hweight16(unsigned int w) __attribute__ ((unused)); -extern inline unsigned int hweight8(unsigned char w) +extern inline unsigned int hweight8(unsigned int w) __attribute__ ((unused)); -extern inline unsigned int hweight16(unsigned short w) +extern inline unsigned int hweight16(unsigned int w) { unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); res = (res & 0x3333) + ((res >> 2) & 0x3333); @@ -249,85 +232,67 @@ extern inline unsigned int hweight16(unsigned short w) return (res & 0x00FF) + ((res >> 8) & 0x00FF); } -extern inline unsigned int hweight8(unsigned char w) +extern inline unsigned int hweight8(unsigned int w) { unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); res = (res & 0x33) + ((res >> 2) & 0x33); return (res & 0x0F) + ((res >> 4) & 0x0F); } +#endif /* --------------------------------------------------------------------- */ -static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news) +static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, struct timeval *tv, unsigned char curs) { - 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); + int timediff; + int bdus8 = bc->baud_us >> 3; + int bdus4 = bc->baud_us >> 2; + int bdus2 = bc->baud_us >> 1; + + timediff = 1000000 + tv->tv_usec - bc->modem.ser12.pll_time; + while (timediff >= 500000) + timediff -= 1000000; + while (timediff >= bdus2) { + timediff -= bc->baud_us; + bc->modem.ser12.pll_time += bc->baud_us; + bc->modem.ser12.dcd_time--; + /* first check if there is room to add a bit */ + if (bc->modem.shreg & 1) { + hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff); + bc->modem.shreg = 0x10000; + } + /* add a one bit */ + bc->modem.shreg >>= 1; } - 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); + if (bc->modem.ser12.dcd_time <= 0) { + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) + 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; + 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; + if (bc->modem.ser12.last_rxbit != curs) { + bc->modem.ser12.last_rxbit = curs; + bc->modem.shreg |= 0x10000; + /* adjust the PLL */ + if (timediff > 0) + bc->modem.ser12.pll_time += bdus8; + else + bc->modem.ser12.pll_time += 1000000 - bdus8; + /* update DCD */ + if (abs(timediff) > bdus4) + bc->modem.ser12.dcd_sum0 += 4; + else + bc->modem.ser12.dcd_sum0--; +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr = timediff; +#endif /* BAYCOM_DEBUG */ } - 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); + while (bc->modem.ser12.pll_time >= 1000000) + bc->modem.ser12.pll_time -= 1000000; } /* --------------------------------------------------------------------- */ @@ -336,25 +301,30 @@ 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; + struct timeval tv; + unsigned char iir, msr; unsigned int txcount = 0; - unsigned int rxcount = 0; - if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC) return; - - for (;;) { - iir = inb(IIR(dev->base_addr)); - if (iir & 1) - break; + /* fast way out for shared irq */ + if ((iir = inb(IIR(dev->base_addr))) & 1) + return; + /* get current time */ + do_gettimeofday(&tv); + msr = inb(MSR(dev->base_addr)); + /* delta DCD */ + if ((msr & 8) && !(bc->options & BAYCOM_OPTIONS_SOFTDCD)) + hdlcdrv_setdcd(&bc->hdrv, !(msr & 0x80)); + do { switch (iir & 6) { case 6: inb(LSR(dev->base_addr)); - continue; + break; case 4: inb(RBR(dev->base_addr)); - continue; + break; case 2: /* @@ -363,45 +333,53 @@ static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) * 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 - */ + txcount++; + /* + * first output the last bit (!) then call HDLC transmitter, + * since this may take quite long + */ + if (bc->modem.ptt) outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - txcount++; - } else + else outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - continue; + break; default: msr = inb(MSR(dev->base_addr)); - if (msr & 1) /* delta CTS interrupt */ - rxcount++; - continue; + /* delta DCD */ + if ((msr & 8) && !(bc->options & BAYCOM_OPTIONS_SOFTDCD)) + hdlcdrv_setdcd(&bc->hdrv, !(msr & 0x80)); + break; } - } - 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) + iir = inb(IIR(dev->base_addr)); + } while (!(iir & 1)); + ser12_rx(dev, bc, &tv, msr & 0x10); /* CTS */ + if (bc->modem.ptt && txcount) { + if (bc->modem.ser12.txshreg <= 1) { bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); + if (!hdlcdrv_ptt(&bc->hdrv)) { + ser12_set_divisor(dev, 115200/100/8); + bc->modem.ptt = 0; + goto end_transmit; + } + } 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; + end_transmit: + __sti(); + if (!bc->modem.ptt && txcount) { hdlcdrv_arbitrate(dev, &bc->hdrv); + if (hdlcdrv_ptt(&bc->hdrv)) { + ser12_set_divisor(dev, bc->baud_uartdiv); + bc->modem.ser12.txshreg = 1; + bc->modem.ptt = 1; + } } hdlcdrv_transmitter(dev, &bc->hdrv); hdlcdrv_receiver(dev, &bc->hdrv); + __cli(); } /* --------------------------------------------------------------------- */ @@ -461,26 +439,25 @@ static int ser12_open(struct device *dev) 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; + bc->baud_us = 1000000/bc->baud; + bc->baud_uartdiv = (115200/8)/bc->baud; 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, + if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ, "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 + * set the SIO to 6 Bits/character; during receive, + * the baud rate is set to produce 100 ints/sec + * to feed the channel arbitration process, + * during transmit to baud ints/sec to run + * the transmitter */ - 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 */ + ser12_set_divisor(dev, 115200/100/8); /* * enable transmitter empty interrupt and modem status interrupt */ @@ -491,6 +468,7 @@ static int ser12_open(struct device *dev) * power from the TxD line */ outb(0x00, THR(dev->base_addr)); + hdlcdrv_setdcd(&bc->hdrv, 0); 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]); @@ -732,7 +710,7 @@ void cleanup_module(void) #else /* MODULE */ /* --------------------------------------------------------------------- */ /* - * format: baycom_ser_=io,irq,mode + * format: baycom_ser_fdx=io,irq,mode * mode: [*] * * indicates sofware DCD */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index f1024658d..f472c8b9e 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -48,6 +48,7 @@ * 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 + * 0.6 14.04.98 cleanups */ /*****************************************************************************/ @@ -61,6 +62,8 @@ #include <linux/ioport.h> #include <linux/in.h> #include <linux/string.h> +#include <linux/init.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <asm/bitops.h> #include <asm/io.h> @@ -72,56 +75,6 @@ /* --------------------------------------------------------------------- */ -/* - * 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 /* @@ -132,8 +85,8 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n) /* --------------------------------------------------------------------- */ 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"; +static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997-1998 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_ser_hdx: version 0.6 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -439,27 +392,52 @@ 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; if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) return; - + /* fast way out */ + if ((iir = inb(IIR(dev->base_addr))) & 1) + 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); + do { + switch (iir & 6) { + case 6: + inb(LSR(dev->base_addr)); + break; + + case 4: + inb(RBR(dev->base_addr)); + break; + + case 2: + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + ser12_tx(dev, bc); + else { + ser12_rx(dev, bc); + bc->modem.arb_divider--; + } + outb(0x00, THR(dev->base_addr)); + break; + + default: + inb(MSR(dev->base_addr)); + break; } + iir = inb(IIR(dev->base_addr)); + } while (!(iir & 1)); + if (bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); + __sti(); + hdlcdrv_arbitrate(dev, &bc->hdrv); } - sti(); + __sti(); hdlcdrv_transmitter(dev, &bc->hdrv); hdlcdrv_receiver(dev, &bc->hdrv); + __cli(); } /* --------------------------------------------------------------------- */ @@ -521,9 +499,8 @@ static int ser12_open(struct device *dev) 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, + if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ, "baycom_ser12", dev)) return -EBUSY; request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index fcc9ed7cc..6116f3642 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -318,6 +318,7 @@ static int bpq_xmit(struct sk_buff *skb, struct device *dev) } skb->dev = dev; + skb->nh.raw = skb->data; dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); bpq->stats.tx_packets++; bpq->stats.tx_bytes+=skb->len; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index c07f90249..bf453dc53 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -1,5 +1,5 @@ /* - * $Id: dmascc.c,v 1.2.1.3 1997/12/19 13:40:15 oe1kib Exp $ + * $Id: dmascc.c,v 1.2.1.4 1998/06/10 02:24:11 kudielka Exp $ * * Driver for high-speed SCC boards (those with DMA support) * Copyright (C) 1997 Klaus Kudielka @@ -381,7 +381,7 @@ __initfunc(int dmascc_init(void)) /* Check valid I/O address regions */ for (i = 0; i < hw[h].num_devs; i++) - if (base[i]) + if (base[i]) { if (check_region(base[i], hw[h].io_size)) base[i] = 0; else { @@ -389,6 +389,7 @@ __initfunc(int dmascc_init(void)) t0[i] = base[i] + hw[h].tmr_offset + TMR_CNT0; t1[i] = base[i] + hw[h].tmr_offset + TMR_CNT1; } + } /* Start timers */ for (i = 0; i < hw[h].num_devs; i++) diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index f4fb73844..1b2ee9eac 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -3,7 +3,7 @@ /* * hdlcdrv.c -- HDLC packet radio network driver. * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1996-1998 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 @@ -35,6 +35,7 @@ * 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) + * 0.6 05.04.98 add spinlocks */ /*****************************************************************************/ @@ -47,7 +48,9 @@ #include <linux/if.h> #include <linux/malloc.h> #include <linux/errno.h> +#include <linux/init.h> #include <asm/bitops.h> +#include <asm/uaccess.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -67,78 +70,6 @@ /* --------------------------------------------------------------------- */ /* - * 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 */ @@ -844,10 +775,12 @@ static int hdlcdrv_probe(struct device *dev) s->ch_params = dflt_ch_params; s->ptt_keyed = 0; + spin_lock_init(&s->hdlcrx.hbuf.lock); s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; s->hdlcrx.in_hdlc_rx = 0; s->hdlcrx.rx_state = 0; + spin_lock_init(&s->hdlctx.hbuf.lock); s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; s->hdlctx.in_hdlc_tx = 0; s->hdlctx.tx_state = 1; @@ -1006,7 +939,7 @@ MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); __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"); + printk(KERN_INFO "hdlcdrv: version 0.6 compiled " __TIME__ " " __DATE__ "\n"); #if LINUX_VERSION_CODE < 0x20115 register_symtab(&hdlcdrv_syms); #endif diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 21cdbf5c6..4d7b96c40 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -10,7 +10,7 @@ * 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 + * AX.25 needs to be separated 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. @@ -19,6 +19,9 @@ * * History * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. + * Matthias (DG2FEF) Added support for FlexNet CRC (on special request) + * Fixed bug in ax25_close(): dev_lock_wait() was + * called twice, causing a deadlock. */ #include <linux/config.h> @@ -91,8 +94,80 @@ 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 int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, int); static void kiss_unesc(struct ax_disp *, unsigned char); +/*---------------------------------------------------------------------------*/ + +static const unsigned short Crc_flex_table[] = { + 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, + 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, + 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, + 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, + 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, + 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, + 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, + 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, + 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, + 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, + 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, + 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, + 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, + 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, + 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, + 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, + 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, + 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, + 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, + 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, + 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, + 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, + 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, + 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, + 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, + 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, + 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, + 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, + 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, + 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, + 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, + 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short +calc_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + return crc; +} + +/*---------------------------------------------------------------------------*/ + +static int +check_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + if (size < 3) + return -1; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + if ((crc & 0xffff) != 0x7070) + return -1; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + /* Find a free channel, and link in this `tty' line. */ static inline struct ax_disp *ax_alloc(void) { @@ -272,6 +347,13 @@ static void ax_bump(struct ax_disp *ax) mkiss= ax->mkiss->tty->driver_data; if (mkiss->magic == MKISS_DRIVER_MAGIC) tmp_ax = ax->mkiss; + } else if (ax->rbuff[0] & 0x20) { + ax->crcmode = CRC_MODE_FLEX; + if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { + ax->rx_errors++; + return; + } + ax->rcount -= 2; } } @@ -312,7 +394,19 @@ static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len) p = icp; if (mkiss->magic != MKISS_DRIVER_MAGIC) { - count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + switch (ax->crcmode) { + unsigned short crc; + + case CRC_MODE_FLEX: + *p |= 0x20; + crc = calc_crc_flex(p, len); + count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); + break; + + default: + count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + break; + } ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count); ax->tx_packets++; @@ -629,12 +723,8 @@ static void ax25_close(struct tty_struct *tty) return; mkiss = ax->mode; - if (ax->dev->flags & IFF_UP) - { - dev_lock_wait(); - dev_close(ax->dev); - dev_unlock_list(); - } + + dev_close(ax->dev); tty->disc_data = 0; ax->tty = NULL; @@ -704,6 +794,45 @@ int kiss_esc(unsigned char *s, unsigned char *d, int len) return ptr - d; } +/* + * MW: + * OK its ugly, but tell me a better solution without copying the + * packet to a temporary buffer :-) + */ +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + *ptr++ = END; + while (len > 0) { + if (len > 2) + c = *s++; + else if (len > 1) + c = crc >> 8; + else if (len > 0) + c = crc & 0xff; + + len--; + + switch (c) { + 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) { @@ -746,14 +875,8 @@ static void kiss_unesc(struct ax_disp *ax, unsigned char s) 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); - + if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN)) + return -EFAULT; return 0; } @@ -780,20 +903,15 @@ static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *ar 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); + if (copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1)) + return -EFAULT; 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 */ diff --git a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h index 0e32aa82b..e1531b728 100644 --- a/drivers/net/hamradio/mkiss.h +++ b/drivers/net/hamradio/mkiss.h @@ -50,6 +50,10 @@ struct ax_disp { #define AXF_OUTWAIT 4 /* is outpacket was flag */ int mode; + int crcmode; /* MW: for FlexNet, SMACK etc. */ +#define CRC_MODE_NONE 0 +#define CRC_MODE_FLEX 1 +#define CRC_MODE_SMACK 2 }; #define AX25_MAGIC 0x5316 diff --git a/drivers/net/hamradio/soundmodem/Makefile b/drivers/net/hamradio/soundmodem/Makefile index 1aabdd9e8..a5adf62d0 100644 --- a/drivers/net/hamradio/soundmodem/Makefile +++ b/drivers/net/hamradio/soundmodem/Makefile @@ -46,7 +46,7 @@ all: all_targets .PHONY: all gentbl: gentbl.c - $(HOSTCC) -Wall $< -o $@ -lm + $(HOSTCC) $(HOSTCFLAGS) $< -o $@ -lm TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h diff --git a/drivers/net/hamradio/soundmodem/gentbl.c b/drivers/net/hamradio/soundmodem/gentbl.c index e67733831..a481a56f9 100644 --- a/drivers/net/hamradio/soundmodem/gentbl.c +++ b/drivers/net/hamradio/soundmodem/gentbl.c @@ -438,7 +438,7 @@ static void gentbl_hapn4800(FILE *f) { int i, j, k, l; float s; - float c[40]; + float c[44]; float min, max; fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n"); diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c index 3423807ac..fbfbb7e36 100644 --- a/drivers/net/hamradio/soundmodem/sm.c +++ b/drivers/net/hamradio/soundmodem/sm.c @@ -3,7 +3,7 @@ /* * sm.c -- soundcard radio modem driver. * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1996-1998 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 @@ -40,6 +40,7 @@ * 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 + * 0.8 14.04.98 cleanups */ /*****************************************************************************/ @@ -53,6 +54,8 @@ #include <linux/net.h> #include <linux/in.h> #include <linux/string.h> +#include <linux/init.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> #include <asm/bitops.h> @@ -62,59 +65,9 @@ /* --------------------------------------------------------------------- */ -/* - * 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 char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1998 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "soundmodem: version 0.8 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -685,51 +638,6 @@ static int sm_ioctl(struct device *dev, struct ifreq *ifr, /* --------------------------------------------------------------------- */ -#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 */ @@ -742,9 +650,6 @@ __initfunc(int sm_init(void)) char ifname[HDLCDRV_IFNAMELEN]; printk(sm_drvinfo); -#ifdef __i386__ - i386_capability(); -#endif /* __i386__ */ /* * register net devices */ diff --git a/drivers/net/hamradio/soundmodem/sm.h b/drivers/net/hamradio/soundmodem/sm.h index 25bbc8ba9..66e8f936c 100644 --- a/drivers/net/hamradio/soundmodem/sm.h +++ b/drivers/net/hamradio/soundmodem/sm.h @@ -3,7 +3,7 @@ /* * sm.h -- soundcard radio modem driver internal header. * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1996-1998 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 @@ -32,6 +32,8 @@ #include <linux/hdlcdrv.h> #include <linux/soundmodem.h> +#include <asm/processor.h> +#include <linux/bitops.h> #define SM_DEBUG @@ -71,7 +73,7 @@ struct sm_state { * state of the modem code */ union { - long m[32/sizeof(long)]; + long m[48/sizeof(long)]; } m; union { long d[256/sizeof(long)]; @@ -228,6 +230,7 @@ static inline void diag_add_constellation(struct sm_state *sm, int vali, int val * ===================== utility functions =============================== */ +#if 0 extern inline unsigned int hweight32(unsigned int w) __attribute__ ((unused)); extern inline unsigned int hweight16(unsigned short w) @@ -259,6 +262,8 @@ extern inline unsigned int hweight8(unsigned char w) return (res & 0x0F) + ((res >> 4) & 0x0F); } +#endif + extern inline unsigned int gcd(unsigned int x, unsigned int y) __attribute__ ((unused)); extern inline unsigned int lcm(unsigned int x, unsigned int y) @@ -291,13 +296,13 @@ extern inline unsigned int lcm(unsigned int x, unsigned int y) #ifdef __i386__ -extern int sm_x86_capability; +#include <asm/processor.h> -#define HAS_RDTSC (sm_x86_capability & 0x10) +#define HAS_RDTSC (current_cpu_data.x86_capability & X86_FEATURE_TSC) /* - * only do 32bit cycle counter arithmetic; we hope we won't overflow :-) - * in fact, overflowing modems would require over 2THz clock speeds :-) + * only do 32bit cycle counter arithmetic; we hope we won't overflow. + * in fact, overflowing modems would require over 2THz CPU clock speeds :-) */ #define time_exec(var,cmd) \ diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2666.c b/drivers/net/hamradio/soundmodem/sm_afsk2666.c new file mode 100644 index 000000000..2aa2972e4 --- /dev/null +++ b/drivers/net/hamradio/soundmodem/sm_afsk2666.c @@ -0,0 +1,356 @@ +/*****************************************************************************/ + +/* + * sm_afsk2666.c -- soundcard radio modem driver, 2666 baud AFSK modem + * + * 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. + * + */ + +#include "sm.h" +#include "sm_tbl_afsk2666.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk26 { + unsigned int shreg; + unsigned long descram; + int dem_sum[8]; + int dem_sum_mean; + int dem_cnt; + 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_afsk26 { + unsigned int shreg; + unsigned long scram; + unsigned int bit_pll; + unsigned int phinc; + unsigned int tx_seq; +}; + +/* --------------------------------------------------------------------- */ + +#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_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + 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->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))]; + } + if (st->tx_seq >= 6) + st->tx_seq = 0; + *buf = OFFSCOS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + 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->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))]; + } + if (st->tx_seq >= 6) + st->tx_seq = 0; + *buf = COS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution12_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]); + + return sum; +} + +extern __inline__ int convolution12_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 >>= 8; + return sum; +} + +/* ---------------------------------------------------------------------- */ + +#if 0 +static int binexp(unsigned int i) +{ + int ret = 31; + + if (!i) + return 0; + if (i < 0x10000LU) { + i <<= 16; + ret -= 16; + } + if (i < 0x1000000LU) { + i <<= 8; + ret -= 8; + } + if (i < 0x10000000LU) { + i <<= 4; + ret -= 4; + } + if (i < 0x40000000LU) { + i <<= 2; + ret -= 2; + } + if (i < 0x80000000LU) + ret -= 1; + return ret; +} + +static const sqrt_tab[16] = { + 00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348, + 46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455 +}; + + +static unsigned int int_sqrt_approx(unsigned int i) +{ + unsigned int j; + + if (i < 16) + return sqrt_tab[i] >> 14; + j = binexp(i) >> 1; + i >>= (j * 2 - 2); + return (sqrt_tab[i & 0xf] << j) >> 15; +} +#endif + +/* --------------------------------------------------------------------- */ + +extern unsigned int est_pwr(int i, int q) +{ + unsigned int ui = abs(i); + unsigned int uq = abs(q); + + if (uq > ui) { + unsigned int tmp; + tmp = ui; + ui = uq; + uq = tmp; + } + if (uq > (ui >> 1)) + return 7*(ui>>3) + 9*(uq>>4); + else + return ui + (uq>>2); +} + +/* --------------------------------------------------------------------- */ + +static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval, + int loi, int loq, int hii, int hiq) +{ + static const int pll_corr[2] = { -0xa00, 0xa00 }; + unsigned char curbit; + unsigned int descx; + int val; + + /* + * estimate power + */ + val = est_pwr(hii, hiq) - est_pwr(loi, loq); + /* + * estimate center value + */ + st->dem_sum[0] += val >> 8; + if ((++st->dem_cnt) >= 256) { + st->dem_cnt = 0; + st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+ + st->dem_sum[2]+st->dem_sum[3]+ + st->dem_sum[4]+st->dem_sum[5]+ + st->dem_sum[6]+st->dem_sum[7]) >> 3; + memmove(st->dem_sum+1, st->dem_sum, + sizeof(st->dem_sum)-sizeof(st->dem_sum[0])); + st->dem_sum[0] = 0; + } + /* + * decision and bit clock regen + */ + val -= st->dem_sum_mean; + diag_add(sm, curval, val); + + st->dcd_shreg <<= 1; + st->bit_pll += 0x1555; + curbit = (val > 0); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)]; + st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) - + hweight16(st->dcd_shreg & 0xfe00); + } + st->last_sample = curbit; + hdlcdrv_channelbit(&sm->hdrv, curbit); + 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 = 400; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffffu; + 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); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); + + for (; buflen > 0; buflen--, buf++) { + demod_one_sample(sm, st, (*buf-0x80)<<8, + convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0), + convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0), + convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1), + convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1)); + demod_one_sample(sm, st, (*buf-0x80)<<8, + convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0), + convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0), + convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1), + convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1)); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); + + for (; buflen > 0; buflen--, buf++) { + demod_one_sample(sm, st, *buf, + convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0), + convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0), + convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1), + convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1)); + demod_one_sample(sm, st, *buf, + convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0), + convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0), + convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1), + convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1)); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_2666(struct sm_state *sm) +{ + struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); + + st->dcd_time = 400; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_afsk2666_tx = { + "afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666, + modulator_2666_u8, modulator_2666_s16, NULL +}; + +const struct modem_rx_info sm_afsk2666_rx = { + "afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6, + demodulator_2666_u8, demodulator_2666_s16, demod_init_2666 +}; + +/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_psk4800.c b/drivers/net/hamradio/soundmodem/sm_psk4800.c new file mode 100644 index 000000000..cbb49042b --- /dev/null +++ b/drivers/net/hamradio/soundmodem/sm_psk4800.c @@ -0,0 +1,418 @@ +/*****************************************************************************/ + +/* + * sm_psk4800.c -- soundcard radio modem driver, 4800 baud 8PSK modem + * + * 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. + * + */ + +#include "sm.h" +#include "sm_tbl_psk4800.h" + +/* --------------------------------------------------------------------- */ + +#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 */ + +#define SCRAM_SHIFT 17 + +/* --------------------------------------------------------------------- */ + +struct demod_state_psk48 { + /* + * input mixer and lowpass + */ + short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2]; + unsigned int downmixer; + int ovrphase; + short magi, magq; + /* + * sampling instant recovery + */ + int pwrhist[5]; + unsigned int s_phase; + int cur_sync; + /* + * phase recovery + */ + short cur_phase_dev; + short last_ph_err; + unsigned short pskph; + unsigned int phase; + unsigned short last_pskph; + unsigned char cur_raw, last_raw, rawbits; + /* + * decoding + */ + 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_psk48 { + unsigned char txbits[PSK48_TXF_NUMSAMPLES]; + unsigned short txphase; + unsigned int shreg; + unsigned long scram; + const short *tbl; + unsigned int txseq; +}; + +/* --------------------------------------------------------------------- */ + +static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); + int i, j; + int si, sq; + + for (; buflen > 0; buflen--, buf++) { + if (!st->txseq++) { + memmove(st->txbits+1, st->txbits, + sizeof(st->txbits)-sizeof(st->txbits[0])); + for (i = 0; i < 3; i++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | + (st->shreg & 1); + st->shreg >>= 1; + if (st->scram & SCRAM_TAP1) + st->scram ^= SCRAM_TAPN; + } + j = (st->scram >> (SCRAM_SHIFT+3)) & 7; + st->txbits[0] -= (j ^ (j >> 1)); + st->txbits[0] &= 7; + st->tbl = psk48_tx_table; + } + if (st->txseq >= PSK48_TXF_OVERSAMPLING) + st->txseq = 0; + for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) { + si += st->tbl[st->txbits[j]]; + sq += st->tbl[st->txbits[j]+8]; + } + *buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80; + st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); + int i, j; + int si, sq; + + for (; buflen > 0; buflen--, buf++) { + if (!st->txseq++) { + memmove(st->txbits+1, st->txbits, + sizeof(st->txbits)-sizeof(st->txbits[0])); + for (i = 0; i < 3; i++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | + (st->shreg & 1); + st->shreg >>= 1; + if (st->scram & SCRAM_TAP1) + st->scram ^= SCRAM_TAPN; + } + j = (st->scram >> (SCRAM_SHIFT+3)) & 7; + st->txbits[0] -= (j ^ (j >> 1)); + st->txbits[0] &= 7; + st->tbl = psk48_tx_table; + } + if (st->txseq >= PSK48_TXF_OVERSAMPLING) + st->txseq = 0; + for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) { + si += st->tbl[st->txbits[j]]; + sq += st->tbl[st->txbits[j]+8]; + } + *buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15; + st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu; + } +} + +/* --------------------------------------------------------------------- */ + +static __inline__ unsigned short tbl_atan(short q, short i) +{ + short tmp; + unsigned short argoffs = 0; + + if (i == 0 && q == 0) + return 0; + switch (((q < 0) << 1) | (i < 0)) { + case 0: + break; + case 1: + tmp = q; + q = -i; + i = tmp; + argoffs = 0x4000; + break; + case 3: + q = -q; + i = -i; + argoffs = 0x8000; + break; + case 2: + tmp = -q; + q = i; + i = tmp; + argoffs = 0xc000; + break; + } + if (q > i) { + tmp = i / q * ATAN_TABLEN; + return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)]) + &0xffffu; + } + return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu; +} + +#define ATAN(q,i) tbl_atan(q, i) + +/* --------------------------------------------------------------------- */ + +static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st, + short vali, short valq) +{ + int i, j; + + st->magi = vali; + st->magq = valq; + memmove(st->pwrhist+1, st->pwrhist, + sizeof(st->pwrhist)-sizeof(st->pwrhist[0])); + st->pwrhist[0] = st->magi * st->magi + + st->magq * st->magq; + st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] && + (st->pwrhist[0] >> 2) > st->pwrhist[2] && + st-> pwrhist[3] > st->pwrhist[2] && + st->pwrhist[1] > st->pwrhist[2]); + st->s_phase &= 0xffff; + st->s_phase += PSK48_SPHASEINC; + st->dcd_shreg <<= 1; + if (st->cur_sync) { + if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2)) + st->s_phase -= PSK48_SPHASEINC/6; + else + st->s_phase += PSK48_SPHASEINC/6; + st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)- + hweight16(st->dcd_shreg & 0x1f00); + } + 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->s_phase < 0x10000) + return; + /* + * sample one constellation + */ + st->last_pskph = st->pskph; + st->pskph = (ATAN(st->magq, st->magi)- + st->phase) & 0xffffu; + st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000; + st->phase += st->last_ph_err/16; + st->last_raw = st->cur_raw; + st->cur_raw = ((st->pskph >> 13) & 7); + i = (st->cur_raw - st->last_raw) & 7; + st->rawbits = i ^ (i >> 1) ^ (i >> 2); + st->descram = (st->descram << 3) | (st->rawbits); + hdlcdrv_channelbit(&sm->hdrv, st->descram & 4); + hdlcdrv_channelbit(&sm->hdrv, st->descram & 2); + hdlcdrv_channelbit(&sm->hdrv, st->descram & 1); + i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^ + ((st->descram >> DESCRAM_TAPSH2) & 7) ^ + ((st->descram >> DESCRAM_TAPSH3) & 7)); + for (j = 4; j; j >>= 1) { + st->shreg >>= 1; + st->shreg |= (!!(i & j)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + +#if 0 + 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 &= 0xffffu; + 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); +#endif + + diag_trigger(sm); + diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13, + (valq*COS(st->phase) - vali*SIN(st->phase)) >> 13); +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); + int i, si, sq; + const short *coeff; + + for (; buflen > 0; buflen--, buf++) { + memmove(st->infi+1, st->infi, + sizeof(st->infi)-sizeof(st->infi[0])); + memmove(st->infq+1, st->infq, + sizeof(st->infq)-sizeof(st->infq[0])); + si = *buf; + si &= 0xff; + si -= 128; + diag_add_one(sm, si << 8); + st->infi[0] = (si * COS(st->downmixer))>>7; + st->infq[0] = (si * SIN(st->downmixer))>>7; + st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu; + for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); + i++, coeff += 2) { + si += st->infi[i] * (*coeff); + sq += st->infq[i] * (*coeff); + } + demod_psk48_baseband(sm, st, si >> 15, sq >> 15); + for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); + i++, coeff += 2) { + si += st->infi[i] * (*coeff); + sq += st->infq[i] * (*coeff); + } + demod_psk48_baseband(sm, st, si >> 15, sq >> 15); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); + int i, si, sq; + const short *coeff; + + for (; buflen > 0; buflen--, buf++) { + memmove(st->infi+1, st->infi, + sizeof(st->infi)-sizeof(st->infi[0])); + memmove(st->infq+1, st->infq, + sizeof(st->infq)-sizeof(st->infq[0])); + si = *buf; + diag_add_one(sm, si); + st->infi[0] = (si * COS(st->downmixer))>>15; + st->infq[0] = (si * SIN(st->downmixer))>>15; + st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu; + for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); + i++, coeff += 2) { + si += st->infi[i] * (*coeff); + sq += st->infq[i] * (*coeff); + } + demod_psk48_baseband(sm, st, si >> 15, sq >> 15); + for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); + i++, coeff += 2) { + si += st->infi[i] * (*coeff); + sq += st->infq[i] * (*coeff); + } + demod_psk48_baseband(sm, st, si >> 15, sq >> 15); + } +} + +/* --------------------------------------------------------------------- */ + +static void mod_init_4800(struct sm_state *sm) +{ + struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); + + st->scram = 1; +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_4800(struct sm_state *sm) +{ + struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_psk4800_tx = { + "psk4800", sizeof(struct mod_state_psk48), + PSK48_SAMPLERATE, 4800, + modulator_4800_u8, modulator_4800_s16, mod_init_4800 +}; + +const struct modem_rx_info sm_psk4800_rx = { + "psk4800", sizeof(struct demod_state_psk48), + PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING, + demodulator_4800_u8, demodulator_4800_s16, demod_init_4800 +}; + +/* --------------------------------------------------------------------- */ |