📄 baycom_par.c
字号:
/*****************************************************************************//* * baycom_par.c -- baycom par96 and picpar radio modem driver. * * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Please note that the GPL allows you to use the driver, NOT the radio. * In order to use the radio, you need a license from the communications * authority of your country. * * * Supported modems * * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. * The modem does all the filtering and regenerates the receiver clock. * Data is transferred from and to the PC via a shift register. * The shift register is filled with 16 bits and an interrupt is * signalled. The PC then empties the shift register in a burst. This * modem connects to the parallel port, hence the name. The modem * leaves the implementation of the HDLC protocol and the scrambler * polynomial to the PC. This modem is no longer available (at least * from Baycom) and has been replaced by the PICPAR modem (see below). * You may however still build one from the schematics published in * cq-DL :-). * * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The * modem is protocol compatible to par96, but uses only three low * power ICs and can therefore be fed from the parallel port and * does not require an additional power supply. It features * built in DCD circuitry. The driver should therefore be configured * for hardware DCD. * * * Command line options (insmod command line) * * mode driver mode string. Valid choices are par96 and picpar. * iobase base address of the port; common values are 0x378, 0x278, 0x3bc * * * History: * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) * 0.3 26.04.1997 init code/data tagged * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) * 0.5 11.11.1997 split into separate files for ser12/par96 * 0.6 03.08.1999 adapt to Linus' new __setup/__initcall * removed some pre-2.2 kernel compatibility cruft * 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts * 0.8 12.02.2000 adapted to softnet driver interface * removed direct parport access, uses parport driver methods * 0.9 03.07.2000 fix interface name handling *//*****************************************************************************/#include <linux/version.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/string.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/hdlcdrv.h>#include <linux/baycom.h>#include <linux/parport.h>/* --------------------------------------------------------------------- */#define BAYCOM_DEBUG/* * modem options; bit mask */#define BAYCOM_OPTIONS_SOFTDCD 1/* --------------------------------------------------------------------- */static const char bc_drvname[] = "baycom_par";static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"KERN_INFO "baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n";/* --------------------------------------------------------------------- */#define NR_PORTS 4static struct net_device baycom_device[NR_PORTS];/* --------------------------------------------------------------------- */#define PAR96_BURSTBITS 16#define PAR96_BURST 4#define PAR96_PTT 2#define PAR96_TXBIT 1#define PAR96_ACK 0x40#define PAR96_RXBIT 0x20#define PAR96_DCD 0x10#define PAR97_POWER 0xf8/* ---------------------------------------------------------------------- *//* * Information that need to be kept for each board. */struct baycom_state { struct hdlcdrv_state hdrv; struct pardevice *pdev; unsigned int options; struct modem_state { short arb_divider; unsigned char flags; unsigned int shreg; struct modem_state_par96 { int dcd_count; unsigned int dcd_shreg; unsigned long descram; unsigned long scram; } par96; } modem;#ifdef BAYCOM_DEBUG struct debug_vals { unsigned long last_jiffies; unsigned cur_intcnt; unsigned last_intcnt; int cur_pllcorr; int last_pllcorr; } debug_vals;#endif /* BAYCOM_DEBUG */};/* --------------------------------------------------------------------- */static void __inline__ baycom_int_freq(struct baycom_state *bc){#ifdef BAYCOM_DEBUG unsigned long cur_jiffies = jiffies; /* * measure the interrupt frequency */ bc->debug_vals.cur_intcnt++; if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) { bc->debug_vals.last_jiffies = cur_jiffies; bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; bc->debug_vals.cur_intcnt = 0; bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; bc->debug_vals.cur_pllcorr = 0; }#endif /* BAYCOM_DEBUG */}/* --------------------------------------------------------------------- *//* * ===================== PAR96 specific routines ========================= */#define PAR96_DESCRAM_TAP1 0x20000#define PAR96_DESCRAM_TAP2 0x01000#define PAR96_DESCRAM_TAP3 0x00001#define PAR96_DESCRAM_TAPSH1 17#define PAR96_DESCRAM_TAPSH2 12#define PAR96_DESCRAM_TAPSH3 0#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 *//* --------------------------------------------------------------------- */static __inline__ void par96_tx(struct net_device *dev, struct baycom_state *bc){ int i; unsigned int data = hdlcdrv_getbits(&bc->hdrv); struct parport *pp = bc->pdev->port; for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { unsigned char val = PAR97_POWER; bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | (bc->modem.par96.scram & 1)); if (!(data & 1)) bc->modem.par96.scram ^= 1; if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) bc->modem.par96.scram ^= (PAR96_SCRAM_TAPN << 1); if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) val |= PAR96_TXBIT; pp->ops->write_data(pp, val); pp->ops->write_data(pp, val | PAR96_BURST); }}/* --------------------------------------------------------------------- */static __inline__ void par96_rx(struct net_device *dev, struct baycom_state *bc){ int i; unsigned int data, mask, mask2, descx; struct parport *pp = bc->pdev->port; /* * do receiver; differential decode and descramble on the fly */ for(data = i = 0; i < PAR96_BURSTBITS; i++) { bc->modem.par96.descram = (bc->modem.par96.descram << 1); if (pp->ops->read_status(pp) & PAR96_RXBIT) bc->modem.par96.descram |= 1; descx = bc->modem.par96.descram ^ (bc->modem.par96.descram >> 1); /* now the diff decoded data is inverted in descram */ pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT); descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ (descx >> PAR96_DESCRAM_TAPSH2)); data >>= 1; if (!(descx & 1)) data |= 0x8000; pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST); } hdlcdrv_putbits(&bc->hdrv, data); /* * do DCD algorithm */ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) | (data << 16); /* search for flags and set the dcd counter appropriately */ for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) if ((bc->modem.par96.dcd_shreg & mask) == mask2) bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; /* check for abort/noise sequences */ for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) if (((bc->modem.par96.dcd_shreg & mask) == mask2) && (bc->modem.par96.dcd_count >= 0)) bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; /* decrement and set the dcd variable */ if (bc->modem.par96.dcd_count >= 0) bc->modem.par96.dcd_count -= 2; hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); } else { hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD)); }}/* --------------------------------------------------------------------- */static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *)dev_id; struct baycom_state *bc = (struct baycom_state *)dev->priv; if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) return; baycom_int_freq(bc); /* * check if transmitter active */ if (hdlcdrv_ptt(&bc->hdrv)) par96_tx(dev, bc); else { par96_rx(dev, bc); if (--bc->modem.arb_divider <= 0) { bc->modem.arb_divider = 6; __sti(); hdlcdrv_arbitrate(dev, &bc->hdrv); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -