brintos

brintos / linux-shallow public Read only

0
0
Text · 16.6 KiB · 00ebc25 Raw
599 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*****************************************************************************/3 4/*5 *	baycom_par.c  -- baycom par96 and picpar radio modem driver.6 *7 *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)8 *9 *  Please note that the GPL allows you to use the driver, NOT the radio.10 *  In order to use the radio, you need a license from the communications11 *  authority of your country.12 *13 *  Supported modems14 *15 *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.16 *          The modem does all the filtering and regenerates the receiver clock.17 *          Data is transferred from and to the PC via a shift register.18 *          The shift register is filled with 16 bits and an interrupt is19 *          signalled. The PC then empties the shift register in a burst. This20 *          modem connects to the parallel port, hence the name. The modem21 *          leaves the implementation of the HDLC protocol and the scrambler22 *          polynomial to the PC. This modem is no longer available (at least23 *          from Baycom) and has been replaced by the PICPAR modem (see below).24 *          You may however still build one from the schematics published in25 *          cq-DL :-).26 *27 *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The28 *          modem is protocol compatible to par96, but uses only three low29 *          power ICs and can therefore be fed from the parallel port and30 *          does not require an additional power supply. It features31 *          built in DCD circuitry. The driver should therefore be configured32 *          for hardware DCD.33 *34 *  Command line options (insmod command line)35 *36 *  mode     driver mode string. Valid choices are par96 and picpar.37 *  iobase   base address of the port; common values are 0x378, 0x278, 0x3bc38 *39 *  History:40 *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface41 *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)42 *   0.3  26.04.1997  init code/data tagged43 *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)44 *   0.5  11.11.1997  split into separate files for ser12/par9645 *   0.6  03.08.1999  adapt to Linus' new __setup/__initcall46 *                    removed some pre-2.2 kernel compatibility cruft47 *   0.7  10.08.1999  Check if parport can do SPP and is safe to access during interrupt contexts48 *   0.8  12.02.2000  adapted to softnet driver interface49 *                    removed direct parport access, uses parport driver methods50 *   0.9  03.07.2000  fix interface name handling51 */52 53/*****************************************************************************/54 55#include <linux/module.h>56#include <linux/kernel.h>57#include <linux/types.h>58#include <linux/fcntl.h>59#include <linux/interrupt.h>60#include <linux/ioport.h>61#include <linux/in.h>62#include <linux/string.h>63#include <linux/init.h>64#include <linux/delay.h>65#include <linux/errno.h>66#include <linux/netdevice.h>67#include <linux/hdlcdrv.h>68#include <linux/baycom.h>69#include <linux/parport.h>70#include <linux/bitops.h>71#include <linux/jiffies.h>72 73#include <linux/uaccess.h>74 75/* --------------------------------------------------------------------- */76 77#define BAYCOM_DEBUG78 79/*80 * modem options; bit mask81 */82#define BAYCOM_OPTIONS_SOFTDCD  183 84/* --------------------------------------------------------------------- */85 86static const char bc_drvname[] = "baycom_par";87static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"88"baycom_par: version 0.9\n";89 90/* --------------------------------------------------------------------- */91 92#define NR_PORTS 493 94static struct net_device *baycom_device[NR_PORTS];95 96/* --------------------------------------------------------------------- */97 98#define PAR96_BURSTBITS 1699#define PAR96_BURST     4100#define PAR96_PTT       2101#define PAR96_TXBIT     1102#define PAR96_ACK       0x40103#define PAR96_RXBIT     0x20104#define PAR96_DCD       0x10105#define PAR97_POWER     0xf8106 107/* ---------------------------------------------------------------------- */108/*109 * Information that need to be kept for each board.110 */111 112struct baycom_state {113	struct hdlcdrv_state hdrv;114 115	struct pardevice *pdev;116	unsigned int options;117 118	struct modem_state {119		short arb_divider;120		unsigned char flags;121		unsigned int shreg;122		struct modem_state_par96 {123			int dcd_count;124			unsigned int dcd_shreg;125			unsigned long descram;126			unsigned long scram;127		} par96;128	} modem;129 130#ifdef BAYCOM_DEBUG131	struct debug_vals {132		unsigned long last_jiffies;133		unsigned cur_intcnt;134		unsigned last_intcnt;135		int cur_pllcorr;136		int last_pllcorr;137	} debug_vals;138#endif /* BAYCOM_DEBUG */139};140 141/* --------------------------------------------------------------------- */142 143static inline void baycom_int_freq(struct baycom_state *bc)144{145#ifdef BAYCOM_DEBUG146	unsigned long cur_jiffies = jiffies;147	/*148	 * measure the interrupt frequency149	 */150	bc->debug_vals.cur_intcnt++;151	if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {152		bc->debug_vals.last_jiffies = cur_jiffies;153		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;154		bc->debug_vals.cur_intcnt = 0;155		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;156		bc->debug_vals.cur_pllcorr = 0;157	}158#endif /* BAYCOM_DEBUG */159}160 161/* --------------------------------------------------------------------- */162/*163 * ===================== PAR96 specific routines =========================164 */165 166#define PAR96_DESCRAM_TAP1 0x20000167#define PAR96_DESCRAM_TAP2 0x01000168#define PAR96_DESCRAM_TAP3 0x00001169 170#define PAR96_DESCRAM_TAPSH1 17171#define PAR96_DESCRAM_TAPSH2 12172#define PAR96_DESCRAM_TAPSH3 0173 174#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */175#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */176 177/* --------------------------------------------------------------------- */178 179static inline void par96_tx(struct net_device *dev, struct baycom_state *bc)180{181	int i;182	unsigned int data = hdlcdrv_getbits(&bc->hdrv);183	struct parport *pp = bc->pdev->port;184 185	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {186		unsigned char val = PAR97_POWER;187		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |188					 (bc->modem.par96.scram & 1));189		if (!(data & 1))190			bc->modem.par96.scram ^= 1;191		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))192			bc->modem.par96.scram ^=193				(PAR96_SCRAM_TAPN << 1);194		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))195			val |= PAR96_TXBIT;196		pp->ops->write_data(pp, val);197		pp->ops->write_data(pp, val | PAR96_BURST);198	}199}200 201/* --------------------------------------------------------------------- */202 203static inline void par96_rx(struct net_device *dev, struct baycom_state *bc)204{205	int i;206	unsigned int data, mask, mask2, descx;207	struct parport *pp = bc->pdev->port;208 209	/*210	 * do receiver; differential decode and descramble on the fly211	 */212	for(data = i = 0; i < PAR96_BURSTBITS; i++) {213		bc->modem.par96.descram = (bc->modem.par96.descram << 1);214		if (pp->ops->read_status(pp) & PAR96_RXBIT)215			bc->modem.par96.descram |= 1;216		descx = bc->modem.par96.descram ^217			(bc->modem.par96.descram >> 1);218		/* now the diff decoded data is inverted in descram */219		pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT);220		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^221			  (descx >> PAR96_DESCRAM_TAPSH2));222		data >>= 1;223		if (!(descx & 1))224			data |= 0x8000;225		pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST);226	}227	hdlcdrv_putbits(&bc->hdrv, data);228	/*229	 * do DCD algorithm230	 */231	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {232		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)233			| (data << 16);234		/* search for flags and set the dcd counter appropriately */235		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;236		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)237			if ((bc->modem.par96.dcd_shreg & mask) == mask2)238				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;239		/* check for abort/noise sequences */240		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;241		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)242			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&243			    (bc->modem.par96.dcd_count >= 0))244				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;245		/* decrement and set the dcd variable */246		if (bc->modem.par96.dcd_count >= 0)247			bc->modem.par96.dcd_count -= 2;248		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);249	} else {250		hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD));251	}252}253 254/* --------------------------------------------------------------------- */255 256static void par96_interrupt(void *dev_id)257{258	struct net_device *dev = dev_id;259	struct baycom_state *bc = netdev_priv(dev);260 261	baycom_int_freq(bc);262	/*263	 * check if transmitter active264	 */265	if (hdlcdrv_ptt(&bc->hdrv))266		par96_tx(dev, bc);267	else {268		par96_rx(dev, bc);269		if (--bc->modem.arb_divider <= 0) {270			bc->modem.arb_divider = 6;271			local_irq_enable();272			hdlcdrv_arbitrate(dev, &bc->hdrv);273		}274	}275	local_irq_enable();276	hdlcdrv_transmitter(dev, &bc->hdrv);277	hdlcdrv_receiver(dev, &bc->hdrv);278        local_irq_disable();279}280 281/* --------------------------------------------------------------------- */282 283static void par96_wakeup(void *handle)284{285        struct net_device *dev = (struct net_device *)handle;286	struct baycom_state *bc = netdev_priv(dev);287 288	printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);289	if (!parport_claim(bc->pdev))290		printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);291}292 293/* --------------------------------------------------------------------- */294 295static int par96_open(struct net_device *dev)296{297	struct baycom_state *bc = netdev_priv(dev);298	struct pardev_cb par_cb;299	struct parport *pp;300	int i;301 302	if (!dev || !bc)303		return -ENXIO;304	pp = parport_find_base(dev->base_addr);305	if (!pp) {306		printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);307		return -ENXIO;308	}309	if (pp->irq < 0) {310		printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base);311		parport_put_port(pp);312		return -ENXIO;313	}314	if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) {315		printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base);316		parport_put_port(pp);317		return -ENXIO;318	}319	memset(&bc->modem, 0, sizeof(bc->modem));320	bc->hdrv.par.bitrate = 9600;321	memset(&par_cb, 0, sizeof(par_cb));322	par_cb.wakeup = par96_wakeup;323	par_cb.irq_func = par96_interrupt;324	par_cb.private = (void *)dev;325	par_cb.flags = PARPORT_DEV_EXCL;326	for (i = 0; i < NR_PORTS; i++)327		if (baycom_device[i] == dev)328			break;329 330	if (i == NR_PORTS) {331		pr_err("%s: no device found\n", bc_drvname);332		parport_put_port(pp);333		return -ENODEV;334	}335	bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i);336	parport_put_port(pp);337	if (!bc->pdev) {338		printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr);339		return -ENXIO;340	}341	if (parport_claim(bc->pdev)) {342		printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base);343		parport_unregister_device(bc->pdev);344		return -EBUSY;345	}346	pp = bc->pdev->port;347	dev->irq = pp->irq;348	pp->ops->data_forward(pp);349        bc->hdrv.par.bitrate = 9600;350	pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */351	pp->ops->enable_irq(pp);352	printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",353	       bc_drvname, dev->base_addr, dev->irq, bc->options);354	return 0;355}356 357/* --------------------------------------------------------------------- */358 359static int par96_close(struct net_device *dev)360{361	struct baycom_state *bc = netdev_priv(dev);362	struct parport *pp;363 364	if (!dev || !bc)365		return -EINVAL;366	pp = bc->pdev->port;367	/* disable interrupt */368	pp->ops->disable_irq(pp);369	/* switch off PTT */370	pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER);371	parport_release(bc->pdev);372	parport_unregister_device(bc->pdev);373	printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",374	       bc_drvname, dev->base_addr, dev->irq);375	return 0;376}377 378/* --------------------------------------------------------------------- */379/*380 * ===================== hdlcdrv driver interface =========================381 */382 383static int baycom_ioctl(struct net_device *dev, void __user *data,384			struct hdlcdrv_ioctl *hi, int cmd);385 386/* --------------------------------------------------------------------- */387 388static const struct hdlcdrv_ops par96_ops = {389	.drvname = bc_drvname,390	.drvinfo = bc_drvinfo,391	.open    = par96_open,392	.close   = par96_close,393	.ioctl   = baycom_ioctl394};395 396/* --------------------------------------------------------------------- */397 398static int baycom_setmode(struct baycom_state *bc, const char *modestr)399{400	if (!strncmp(modestr, "picpar", 6))401		bc->options = 0;402	else if (!strncmp(modestr, "par96", 5))403		bc->options = BAYCOM_OPTIONS_SOFTDCD;404	else405		bc->options = !!strchr(modestr, '*');406	return 0;407}408 409/* --------------------------------------------------------------------- */410 411static int baycom_ioctl(struct net_device *dev, void __user *data,412			struct hdlcdrv_ioctl *hi, int cmd)413{414	struct baycom_state *bc;415	struct baycom_ioctl bi;416 417	if (!dev)418		return -EINVAL;419 420	bc = netdev_priv(dev);421	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);422 423	if (cmd != SIOCDEVPRIVATE)424		return -ENOIOCTLCMD;425	switch (hi->cmd) {426	default:427		break;428 429	case HDLCDRVCTL_GETMODE:430		strcpy(hi->data.modename, bc->options ? "par96" : "picpar");431		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))432			return -EFAULT;433		return 0;434 435	case HDLCDRVCTL_SETMODE:436		if (netif_running(dev) || !capable(CAP_NET_ADMIN))437			return -EACCES;438		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';439		return baycom_setmode(bc, hi->data.modename);440 441	case HDLCDRVCTL_MODELIST:442		strcpy(hi->data.modename, "par96,picpar");443		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))444			return -EFAULT;445		return 0;446 447	case HDLCDRVCTL_MODEMPARMASK:448		return HDLCDRV_PARMASK_IOBASE;449 450	}451 452	if (copy_from_user(&bi, data, sizeof(bi)))453		return -EFAULT;454	switch (bi.cmd) {455	default:456		return -ENOIOCTLCMD;457 458#ifdef BAYCOM_DEBUG459	case BAYCOMCTL_GETDEBUG:460		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;461		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;462		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;463		break;464#endif /* BAYCOM_DEBUG */465 466	}467	if (copy_to_user(data, &bi, sizeof(bi)))468		return -EFAULT;469	return 0;470 471}472 473/* --------------------------------------------------------------------- */474 475/*476 * command line settable parameters477 */478static char *mode[NR_PORTS] = { "picpar", };479static int iobase[NR_PORTS] = { 0x378, };480 481module_param_array(mode, charp, NULL, 0);482MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");483module_param_hw_array(iobase, int, ioport, NULL, 0);484MODULE_PARM_DESC(iobase, "baycom io base address");485 486MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");487MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");488MODULE_LICENSE("GPL");489 490/* --------------------------------------------------------------------- */491 492static int baycom_par_probe(struct pardevice *par_dev)493{494	struct device_driver *drv = par_dev->dev.driver;495	int len = strlen(drv->name);496 497	if (strncmp(par_dev->name, drv->name, len))498		return -ENODEV;499 500	return 0;501}502 503static struct parport_driver baycom_par_driver = {504	.name = "bcp",505	.probe = baycom_par_probe,506};507 508static int __init init_baycompar(void)509{510	int i, found = 0, ret;511	char set_hw = 1;512 513	printk(bc_drvinfo);514 515	ret = parport_register_driver(&baycom_par_driver);516	if (ret)517		return ret;518 519	/*520	 * register net devices521	 */522	for (i = 0; i < NR_PORTS; i++) {523		struct net_device *dev;524		struct baycom_state *bc;525		char ifname[IFNAMSIZ];526 527		sprintf(ifname, "bcp%d", i);528 529		if (!mode[i])530			set_hw = 0;531		if (!set_hw)532			iobase[i] = 0;533 534		dev = hdlcdrv_register(&par96_ops,535				       sizeof(struct baycom_state),536				       ifname, iobase[i], 0, 0);537		if (IS_ERR(dev)) 538			break;539 540		bc = netdev_priv(dev);541		if (set_hw && baycom_setmode(bc, mode[i]))542			set_hw = 0;543		found++;544		baycom_device[i] = dev;545	}546 547	if (!found) {548		parport_unregister_driver(&baycom_par_driver);549		return -ENXIO;550	}551	return 0;552}553 554static void __exit cleanup_baycompar(void)555{556	int i;557 558	for(i = 0; i < NR_PORTS; i++) {559		struct net_device *dev = baycom_device[i];560 561		if (dev)562			hdlcdrv_unregister(dev);563	}564	parport_unregister_driver(&baycom_par_driver);565}566 567module_init(init_baycompar);568module_exit(cleanup_baycompar);569 570/* --------------------------------------------------------------------- */571 572#ifndef MODULE573 574/*575 * format: baycom_par=io,mode576 * mode: par96,picpar577 */578 579static int __init baycom_par_setup(char *str)580{581        static unsigned nr_dev;582	int ints[2];583 584        if (nr_dev >= NR_PORTS)585                return 0;586        str = get_options(str, 2, ints);587        if (ints[0] < 1)588                return 0;589        mode[nr_dev] = str;590        iobase[nr_dev] = ints[1];591	nr_dev++;592	return 1;593}594 595__setup("baycom_par=", baycom_par_setup);596 597#endif /* MODULE */598/* --------------------------------------------------------------------- */599