1 /*****************************************************************************/
 
   4  *      baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
 
   6  *      Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
 
   8  *      This program is free software; you can redistribute it and/or modify
 
   9  *      it under the terms of the GNU General Public License as published by
 
  10  *      the Free Software Foundation; either version 2 of the License, or
 
  11  *      (at your option) any later version.
 
  13  *      This program is distributed in the hope that it will be useful,
 
  14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  16  *      GNU General Public License for more details.
 
  18  *      You should have received a copy of the GNU General Public License
 
  19  *      along with this program; if not, write to the Free Software
 
  20  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  22  *  Please note that the GPL allows you to use the driver, NOT the radio.
 
  23  *  In order to use the radio, you need a license from the communications
 
  24  *  authority of your country.
 
  29  *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
 
  30  *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
 
  31  *          is responsible for regenerating the receiver bit clock, as well as
 
  32  *          for handling the HDLC protocol. The modem connects to a serial port,
 
  33  *          hence the name. Since the serial port is not used as an async serial
 
  34  *          port, the kernel driver for serial ports cannot be used, and this
 
  35  *          driver only supports standard serial hardware (8250, 16450, 16550A)
 
  38  *  Command line options (insmod command line)
 
  40  *  mode     ser12    hardware DCD
 
  42  *           ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
 
  43  *                    mutes audio input to the modem
 
  44  *           ser12+   hardware DCD, inverted signal at DCD pin
 
  45  *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
 
  46  *  irq      interrupt line of the port; common values are 4,3
 
  50  *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
 
  51  *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
 
  52  *   0.3  26.04.1997  init code/data tagged
 
  53  *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
 
  54  *   0.5  11.11.1997  ser12/par96 split into separate files
 
  55  *   0.6  14.04.1998  cleanups
 
  56  *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
 
  57  *   0.8  10.08.1999  use module_init/module_exit
 
  58  *   0.9  12.02.2000  adapted to softnet driver interface
 
  59  *   0.10 03.07.2000  fix interface name handling
 
  62 /*****************************************************************************/
 
  64 #include <linux/module.h>
 
  65 #include <linux/ioport.h>
 
  66 #include <linux/string.h>
 
  67 #include <linux/init.h>
 
  68 #include <asm/uaccess.h>
 
  70 #include <linux/hdlcdrv.h>
 
  71 #include <linux/baycom.h>
 
  72 #include <linux/jiffies.h>
 
  74 /* --------------------------------------------------------------------- */
 
  78 /* --------------------------------------------------------------------- */
 
  80 static const char bc_drvname[] = "baycom_ser_hdx";
 
  81 static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
 
  82 KERN_INFO "baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n";
 
  84 /* --------------------------------------------------------------------- */
 
  88 static struct net_device *baycom_device[NR_PORTS];
 
  90 /* --------------------------------------------------------------------- */
 
  92 #define RBR(iobase) (iobase+0)
 
  93 #define THR(iobase) (iobase+0)
 
  94 #define IER(iobase) (iobase+1)
 
  95 #define IIR(iobase) (iobase+2)
 
  96 #define FCR(iobase) (iobase+2)
 
  97 #define LCR(iobase) (iobase+3)
 
  98 #define MCR(iobase) (iobase+4)
 
  99 #define LSR(iobase) (iobase+5)
 
 100 #define MSR(iobase) (iobase+6)
 
 101 #define SCR(iobase) (iobase+7)
 
 102 #define DLL(iobase) (iobase+0)
 
 103 #define DLM(iobase) (iobase+1)
 
 105 #define SER12_EXTENT 8
 
 107 /* ---------------------------------------------------------------------- */
 
 109  * Information that need to be kept for each board.
 
 112 struct baycom_state {
 
 113         struct hdlcdrv_state hdrv;
 
 121                 struct modem_state_ser12 {
 
 122                         unsigned char tx_bit;
 
 123                         int dcd_sum0, dcd_sum1, dcd_sum2;
 
 124                         unsigned char last_sample;
 
 125                         unsigned char last_rxbit;
 
 126                         unsigned int dcd_shreg;
 
 127                         unsigned int dcd_time;
 
 128                         unsigned int bit_pll;
 
 129                         unsigned char interm_sample;
 
 135                 unsigned long last_jiffies;
 
 137                 unsigned last_intcnt;
 
 141 #endif /* BAYCOM_DEBUG */
 
 144 /* --------------------------------------------------------------------- */
 
 146 static inline void baycom_int_freq(struct baycom_state *bc)
 
 149         unsigned long cur_jiffies = jiffies;
 
 151          * measure the interrupt frequency
 
 153         bc->debug_vals.cur_intcnt++;
 
 154         if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
 
 155                 bc->debug_vals.last_jiffies = cur_jiffies;
 
 156                 bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
 
 157                 bc->debug_vals.cur_intcnt = 0;
 
 158                 bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
 
 159                 bc->debug_vals.cur_pllcorr = 0;
 
 161 #endif /* BAYCOM_DEBUG */
 
 164 /* --------------------------------------------------------------------- */
 
 166  * ===================== SER12 specific routines =========================
 
 169 static inline void ser12_set_divisor(struct net_device *dev,
 
 170                                      unsigned char divisor)
 
 172         outb(0x81, LCR(dev->base_addr));        /* DLAB = 1 */
 
 173         outb(divisor, DLL(dev->base_addr));
 
 174         outb(0, DLM(dev->base_addr));
 
 175         outb(0x01, LCR(dev->base_addr));        /* word length = 6 */
 
 177          * make sure the next interrupt is generated;
 
 178          * 0 must be used to power the modem; the modem draws its
 
 179          * power from the TxD line
 
 181         outb(0x00, THR(dev->base_addr));
 
 183          * it is important not to set the divider while transmitting;
 
 184          * this reportedly makes some UARTs generating interrupts
 
 185          * in the hundredthousands per second region
 
 186          * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
 
 190 /* --------------------------------------------------------------------- */
 
 193  * must call the TX arbitrator every 10ms
 
 195 #define SER12_ARB_DIVIDER(bc)  (bc->opt_dcd ? 24 : 36)
 
 197 #define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
 
 199 static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
 
 201         /* one interrupt per channel bit */
 
 202         ser12_set_divisor(dev, 12);
 
 204          * first output the last bit (!) then call HDLC transmitter,
 
 205          * since this may take quite long
 
 207         outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
 
 208         if (bc->modem.shreg <= 1)
 
 209                 bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
 
 210         bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
 
 211                                    (bc->modem.shreg & 1));
 
 212         bc->modem.shreg >>= 1;
 
 215 /* --------------------------------------------------------------------- */
 
 217 static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
 
 223         cur_s = inb(MSR(dev->base_addr)) & 0x10;        /* the CTS line */
 
 224         hdlcdrv_channelbit(&bc->hdrv, cur_s);
 
 225         bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
 
 226                 (cur_s != bc->modem.ser12.last_sample);
 
 227         bc->modem.ser12.last_sample = cur_s;
 
 228         if(bc->modem.ser12.dcd_shreg & 1) {
 
 230                         unsigned int dcdspos, dcdsneg;
 
 232                         dcdspos = dcdsneg = 0;
 
 233                         dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
 
 234                         if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
 
 236                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
 
 237                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
 
 238                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
 
 240                         bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
 
 242                         bc->modem.ser12.dcd_sum0--;
 
 244         if(!bc->modem.ser12.dcd_time) {
 
 245                 hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
 
 246                                            bc->modem.ser12.dcd_sum1 +
 
 247                                            bc->modem.ser12.dcd_sum2) < 0);
 
 248                 bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
 
 249                 bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
 
 250                 /* offset to ensure DCD off on silent input */
 
 251                 bc->modem.ser12.dcd_sum0 = 2;
 
 252                 bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
 
 254         bc->modem.ser12.dcd_time--;
 
 257                  * PLL code for the improved software DCD algorithm
 
 259                 if (bc->modem.ser12.interm_sample) {
 
 261                          * intermediate sample; set timing correction to normal
 
 263                         ser12_set_divisor(dev, 4);
 
 266                          * do PLL correction and call HDLC receiver
 
 268                         switch (bc->modem.ser12.dcd_shreg & 7) {
 
 269                         case 1: /* transition too late */
 
 270                                 ser12_set_divisor(dev, 5);
 
 272                                 bc->debug_vals.cur_pllcorr++;
 
 273 #endif /* BAYCOM_DEBUG */
 
 275                         case 4: /* transition too early */
 
 276                                 ser12_set_divisor(dev, 3);
 
 278                                 bc->debug_vals.cur_pllcorr--;
 
 279 #endif /* BAYCOM_DEBUG */
 
 282                                 ser12_set_divisor(dev, 4);
 
 285                         bc->modem.shreg >>= 1;
 
 286                         if (bc->modem.ser12.last_sample ==
 
 287                             bc->modem.ser12.last_rxbit)
 
 288                                 bc->modem.shreg |= 0x10000;
 
 289                         bc->modem.ser12.last_rxbit =
 
 290                                 bc->modem.ser12.last_sample;
 
 292                 if (++bc->modem.ser12.interm_sample >= 3)
 
 293                         bc->modem.ser12.interm_sample = 0;
 
 297                 if (bc->modem.ser12.dcd_shreg & 1) {
 
 298                         unsigned int dcdspos, dcdsneg;
 
 300                         dcdspos = dcdsneg = 0;
 
 301                         dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
 
 302                         dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
 
 304                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
 
 305                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
 
 306                         dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
 
 308                         bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
 
 312                  * PLL algorithm for the hardware squelch DCD algorithm
 
 314                 if (bc->modem.ser12.interm_sample) {
 
 316                          * intermediate sample; set timing correction to normal
 
 318                         ser12_set_divisor(dev, 6);
 
 321                          * do PLL correction and call HDLC receiver
 
 323                         switch (bc->modem.ser12.dcd_shreg & 3) {
 
 324                         case 1: /* transition too late */
 
 325                                 ser12_set_divisor(dev, 7);
 
 327                                 bc->debug_vals.cur_pllcorr++;
 
 328 #endif /* BAYCOM_DEBUG */
 
 330                         case 2: /* transition too early */
 
 331                                 ser12_set_divisor(dev, 5);
 
 333                                 bc->debug_vals.cur_pllcorr--;
 
 334 #endif /* BAYCOM_DEBUG */
 
 337                                 ser12_set_divisor(dev, 6);
 
 340                         bc->modem.shreg >>= 1;
 
 341                         if (bc->modem.ser12.last_sample ==
 
 342                             bc->modem.ser12.last_rxbit)
 
 343                                 bc->modem.shreg |= 0x10000;
 
 344                         bc->modem.ser12.last_rxbit =
 
 345                                 bc->modem.ser12.last_sample;
 
 347                 bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
 
 351                 bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
 
 353         outb(0x0d, MCR(dev->base_addr));                /* transmitter off */
 
 354         if (bc->modem.shreg & 1) {
 
 355                 hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
 
 356                 bc->modem.shreg = 0x10000;
 
 358         if(!bc->modem.ser12.dcd_time) {
 
 360                         hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
 
 362                         hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
 
 363                                                    bc->modem.ser12.dcd_sum1 +
 
 364                                                    bc->modem.ser12.dcd_sum2) < 0);
 
 365                 bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
 
 366                 bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
 
 367                 /* offset to ensure DCD off on silent input */
 
 368                 bc->modem.ser12.dcd_sum0 = 2;
 
 369                 bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
 
 371         bc->modem.ser12.dcd_time--;
 
 374 /* --------------------------------------------------------------------- */
 
 376 static irqreturn_t ser12_interrupt(int irq, void *dev_id)
 
 378         struct net_device *dev = (struct net_device *)dev_id;
 
 379         struct baycom_state *bc = netdev_priv(dev);
 
 382         if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
 
 385         if ((iir = inb(IIR(dev->base_addr))) & 1)
 
 391                         inb(LSR(dev->base_addr));
 
 395                         inb(RBR(dev->base_addr));
 
 400                          * check if transmitter active
 
 402                         if (hdlcdrv_ptt(&bc->hdrv))
 
 406                                 bc->modem.arb_divider--;
 
 408                         outb(0x00, THR(dev->base_addr));
 
 412                         inb(MSR(dev->base_addr));
 
 415                 iir = inb(IIR(dev->base_addr));
 
 416         } while (!(iir & 1));
 
 417         if (bc->modem.arb_divider <= 0) {
 
 418                 bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
 
 420                 hdlcdrv_arbitrate(dev, &bc->hdrv);
 
 423         hdlcdrv_transmitter(dev, &bc->hdrv);
 
 424         hdlcdrv_receiver(dev, &bc->hdrv);
 
 429 /* --------------------------------------------------------------------- */
 
 431 enum uart { c_uart_unknown, c_uart_8250,
 
 432             c_uart_16450, c_uart_16550, c_uart_16550A};
 
 433 static const char *uart_str[] = { 
 
 434         "unknown", "8250", "16450", "16550", "16550A" 
 
 437 static enum uart ser12_check_uart(unsigned int iobase)
 
 439         unsigned char b1,b2,b3;
 
 441         enum uart uart_tab[] =
 
 442                 { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
 
 444         b1 = inb(MCR(iobase));
 
 445         outb(b1 | 0x10, MCR(iobase));   /* loopback mode */
 
 446         b2 = inb(MSR(iobase));
 
 447         outb(0x1a, MCR(iobase));
 
 448         b3 = inb(MSR(iobase)) & 0xf0;
 
 449         outb(b1, MCR(iobase));                  /* restore old values */
 
 450         outb(b2, MSR(iobase));
 
 452                 return c_uart_unknown;
 
 455         outb(0x01, FCR(iobase));                /* enable FIFOs */
 
 456         u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
 
 457         if (u == c_uart_16450) {
 
 458                 outb(0x5a, SCR(iobase));
 
 459                 b1 = inb(SCR(iobase));
 
 460                 outb(0xa5, SCR(iobase));
 
 461                 b2 = inb(SCR(iobase));
 
 462                 if ((b1 != 0x5a) || (b2 != 0xa5))
 
 468 /* --------------------------------------------------------------------- */
 
 470 static int ser12_open(struct net_device *dev)
 
 472         struct baycom_state *bc = netdev_priv(dev);
 
 477         if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
 
 478             dev->irq < 2 || dev->irq > 15)
 
 480         if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
 
 482         memset(&bc->modem, 0, sizeof(bc->modem));
 
 483         bc->hdrv.par.bitrate = 1200;
 
 484         if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
 
 485                 release_region(dev->base_addr, SER12_EXTENT);       
 
 488         outb(0, FCR(dev->base_addr));  /* disable FIFOs */
 
 489         outb(0x0d, MCR(dev->base_addr));
 
 490         outb(0, IER(dev->base_addr));
 
 491         if (request_irq(dev->irq, ser12_interrupt, IRQF_DISABLED | IRQF_SHARED,
 
 492                         "baycom_ser12", dev)) {
 
 493                 release_region(dev->base_addr, SER12_EXTENT);       
 
 497          * enable transmitter empty interrupt
 
 499         outb(2, IER(dev->base_addr));
 
 501          * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
 
 502          * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
 
 503          * depending on the usage of the software DCD routine
 
 505         ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
 
 506         printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", 
 
 507                bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
 
 511 /* --------------------------------------------------------------------- */
 
 513 static int ser12_close(struct net_device *dev)
 
 515         struct baycom_state *bc = netdev_priv(dev);
 
 522         outb(0, IER(dev->base_addr));
 
 523         outb(1, MCR(dev->base_addr));
 
 524         free_irq(dev->irq, dev);
 
 525         release_region(dev->base_addr, SER12_EXTENT);
 
 526         printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
 
 527                bc_drvname, dev->base_addr, dev->irq);
 
 531 /* --------------------------------------------------------------------- */
 
 533  * ===================== hdlcdrv driver interface =========================
 
 536 /* --------------------------------------------------------------------- */
 
 538 static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
 
 539                         struct hdlcdrv_ioctl *hi, int cmd);
 
 541 /* --------------------------------------------------------------------- */
 
 543 static struct hdlcdrv_ops ser12_ops = {
 
 544         .drvname = bc_drvname,
 
 545         .drvinfo = bc_drvinfo,
 
 547         .close   = ser12_close,
 
 548         .ioctl   = baycom_ioctl,
 
 551 /* --------------------------------------------------------------------- */
 
 553 static int baycom_setmode(struct baycom_state *bc, const char *modestr)
 
 555         if (strchr(modestr, '*'))
 
 557         else if (strchr(modestr, '+'))
 
 559         else if (strchr(modestr, '@'))
 
 566 /* --------------------------------------------------------------------- */
 
 568 static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
 
 569                         struct hdlcdrv_ioctl *hi, int cmd)
 
 571         struct baycom_state *bc;
 
 572         struct baycom_ioctl bi;
 
 577         bc = netdev_priv(dev);
 
 578         BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
 
 580         if (cmd != SIOCDEVPRIVATE)
 
 586         case HDLCDRVCTL_GETMODE:
 
 587                 strcpy(hi->data.modename, "ser12");
 
 588                 if (bc->opt_dcd <= 0)
 
 589                         strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
 
 590                 if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
 
 594         case HDLCDRVCTL_SETMODE:
 
 595                 if (netif_running(dev) || !capable(CAP_NET_ADMIN))
 
 597                 hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
 
 598                 return baycom_setmode(bc, hi->data.modename);
 
 600         case HDLCDRVCTL_MODELIST:
 
 601                 strcpy(hi->data.modename, "ser12");
 
 602                 if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
 
 606         case HDLCDRVCTL_MODEMPARMASK:
 
 607                 return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
 
 611         if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
 
 618         case BAYCOMCTL_GETDEBUG:
 
 619                 bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
 
 620                 bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
 
 621                 bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
 
 623 #endif /* BAYCOM_DEBUG */
 
 626         if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
 
 632 /* --------------------------------------------------------------------- */
 
 635  * command line settable parameters
 
 637 static char *mode[NR_PORTS] = { "ser12*", };
 
 638 static int iobase[NR_PORTS] = { 0x3f8, };
 
 639 static int irq[NR_PORTS] = { 4, };
 
 641 module_param_array(mode, charp, NULL, 0);
 
 642 MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
 
 643 module_param_array(iobase, int, NULL, 0);
 
 644 MODULE_PARM_DESC(iobase, "baycom io base address");
 
 645 module_param_array(irq, int, NULL, 0);
 
 646 MODULE_PARM_DESC(irq, "baycom irq number");
 
 648 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 
 649 MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
 
 650 MODULE_LICENSE("GPL");
 
 652 /* --------------------------------------------------------------------- */
 
 654 static int __init init_baycomserhdx(void)
 
 661          * register net devices
 
 663         for (i = 0; i < NR_PORTS; i++) {
 
 664                 struct net_device *dev;
 
 665                 struct baycom_state *bc;
 
 666                 char ifname[IFNAMSIZ];
 
 668                 sprintf(ifname, "bcsh%d", i);
 
 673                         iobase[i] = irq[i] = 0;
 
 675                 dev = hdlcdrv_register(&ser12_ops, 
 
 676                                        sizeof(struct baycom_state),
 
 677                                        ifname, iobase[i], irq[i], 0);
 
 681                 bc = netdev_priv(dev);
 
 682                 if (set_hw && baycom_setmode(bc, mode[i]))
 
 685                 baycom_device[i] = dev;
 
 693 static void __exit cleanup_baycomserhdx(void)
 
 697         for(i = 0; i < NR_PORTS; i++) {
 
 698                 struct net_device *dev = baycom_device[i];
 
 701                         hdlcdrv_unregister(dev);
 
 705 module_init(init_baycomserhdx);
 
 706 module_exit(cleanup_baycomserhdx);
 
 708 /* --------------------------------------------------------------------- */
 
 713  * format: baycom_ser_hdx=io,irq,mode
 
 714  * mode: ser12    hardware DCD
 
 715  *       ser12*   software DCD
 
 716  *       ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
 
 717  *                mutes audio input to the modem
 
 718  *       ser12+   hardware DCD, inverted signal at DCD pin
 
 721 static int __init baycom_ser_hdx_setup(char *str)
 
 723         static unsigned nr_dev;
 
 726         if (nr_dev >= NR_PORTS)
 
 728         str = get_options(str, 3, ints);
 
 732         iobase[nr_dev] = ints[1];
 
 733         irq[nr_dev] = ints[2];
 
 738 __setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
 
 741 /* --------------------------------------------------------------------- */