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