Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*****************************************************************************/
0003 
0004 /*
0005  *  baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
0006  *
0007  *  Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
0008  *
0009  *  Please note that the GPL allows you to use the driver, NOT the radio.
0010  *  In order to use the radio, you need a license from the communications
0011  *  authority of your country.
0012  *
0013  *  Supported modems
0014  *
0015  *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
0016  *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
0017  *          is responsible for regenerating the receiver bit clock, as well as
0018  *          for handling the HDLC protocol. The modem connects to a serial port,
0019  *          hence the name. Since the serial port is not used as an async serial
0020  *          port, the kernel driver for serial ports cannot be used, and this
0021  *          driver only supports standard serial hardware (8250, 16450, 16550A)
0022  *
0023  *  Command line options (insmod command line)
0024  *
0025  *  mode     ser12    hardware DCD
0026  *           ser12*   software DCD
0027  *           ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
0028  *                    mutes audio input to the modem
0029  *           ser12+   hardware DCD, inverted signal at DCD pin
0030  *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
0031  *  irq      interrupt line of the port; common values are 4,3
0032  *
0033  *  History:
0034  *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
0035  *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
0036  *   0.3  26.04.1997  init code/data tagged
0037  *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
0038  *   0.5  11.11.1997  ser12/par96 split into separate files
0039  *   0.6  14.04.1998  cleanups
0040  *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
0041  *   0.8  10.08.1999  use module_init/module_exit
0042  *   0.9  12.02.2000  adapted to softnet driver interface
0043  *   0.10 03.07.2000  fix interface name handling
0044  */
0045 
0046 /*****************************************************************************/
0047 
0048 #include <linux/capability.h>
0049 #include <linux/module.h>
0050 #include <linux/ioport.h>
0051 #include <linux/string.h>
0052 #include <linux/init.h>
0053 #include <linux/interrupt.h>
0054 #include <linux/uaccess.h>
0055 #include <asm/io.h>
0056 #include <linux/hdlcdrv.h>
0057 #include <linux/baycom.h>
0058 #include <linux/jiffies.h>
0059 
0060 /* --------------------------------------------------------------------- */
0061 
0062 #define BAYCOM_DEBUG
0063 
0064 /* --------------------------------------------------------------------- */
0065 
0066 static const char bc_drvname[] = "baycom_ser_hdx";
0067 static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
0068 "baycom_ser_hdx: version 0.10\n";
0069 
0070 /* --------------------------------------------------------------------- */
0071 
0072 #define NR_PORTS 4
0073 
0074 static struct net_device *baycom_device[NR_PORTS];
0075 
0076 /* --------------------------------------------------------------------- */
0077 
0078 #define RBR(iobase) (iobase+0)
0079 #define THR(iobase) (iobase+0)
0080 #define IER(iobase) (iobase+1)
0081 #define IIR(iobase) (iobase+2)
0082 #define FCR(iobase) (iobase+2)
0083 #define LCR(iobase) (iobase+3)
0084 #define MCR(iobase) (iobase+4)
0085 #define LSR(iobase) (iobase+5)
0086 #define MSR(iobase) (iobase+6)
0087 #define SCR(iobase) (iobase+7)
0088 #define DLL(iobase) (iobase+0)
0089 #define DLM(iobase) (iobase+1)
0090 
0091 #define SER12_EXTENT 8
0092 
0093 /* ---------------------------------------------------------------------- */
0094 /*
0095  * Information that need to be kept for each board.
0096  */
0097 
0098 struct baycom_state {
0099     struct hdlcdrv_state hdrv;
0100 
0101     int opt_dcd;
0102 
0103     struct modem_state {
0104         short arb_divider;
0105         unsigned char flags;
0106         unsigned int shreg;
0107         struct modem_state_ser12 {
0108             unsigned char tx_bit;
0109             int dcd_sum0, dcd_sum1, dcd_sum2;
0110             unsigned char last_sample;
0111             unsigned char last_rxbit;
0112             unsigned int dcd_shreg;
0113             unsigned int dcd_time;
0114             unsigned int bit_pll;
0115             unsigned char interm_sample;
0116         } ser12;
0117     } modem;
0118 
0119 #ifdef BAYCOM_DEBUG
0120     struct debug_vals {
0121         unsigned long last_jiffies;
0122         unsigned cur_intcnt;
0123         unsigned last_intcnt;
0124         int cur_pllcorr;
0125         int last_pllcorr;
0126     } debug_vals;
0127 #endif /* BAYCOM_DEBUG */
0128 };
0129 
0130 /* --------------------------------------------------------------------- */
0131 
0132 static inline void baycom_int_freq(struct baycom_state *bc)
0133 {
0134 #ifdef BAYCOM_DEBUG
0135     unsigned long cur_jiffies = jiffies;
0136     /*
0137      * measure the interrupt frequency
0138      */
0139     bc->debug_vals.cur_intcnt++;
0140     if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
0141         bc->debug_vals.last_jiffies = cur_jiffies;
0142         bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
0143         bc->debug_vals.cur_intcnt = 0;
0144         bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
0145         bc->debug_vals.cur_pllcorr = 0;
0146     }
0147 #endif /* BAYCOM_DEBUG */
0148 }
0149 
0150 /* --------------------------------------------------------------------- */
0151 /*
0152  * ===================== SER12 specific routines =========================
0153  */
0154 
0155 static inline void ser12_set_divisor(struct net_device *dev,
0156                      unsigned char divisor)
0157 {
0158     outb(0x81, LCR(dev->base_addr));    /* DLAB = 1 */
0159     outb(divisor, DLL(dev->base_addr));
0160     outb(0, DLM(dev->base_addr));
0161     outb(0x01, LCR(dev->base_addr));    /* word length = 6 */
0162     /*
0163      * make sure the next interrupt is generated;
0164      * 0 must be used to power the modem; the modem draws its
0165      * power from the TxD line
0166      */
0167     outb(0x00, THR(dev->base_addr));
0168     /*
0169      * it is important not to set the divider while transmitting;
0170      * this reportedly makes some UARTs generating interrupts
0171      * in the hundredthousands per second region
0172      * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
0173      */
0174 }
0175 
0176 /* --------------------------------------------------------------------- */
0177 
0178 /*
0179  * must call the TX arbitrator every 10ms
0180  */
0181 #define SER12_ARB_DIVIDER(bc)  (bc->opt_dcd ? 24 : 36)
0182                    
0183 #define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
0184 
0185 static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
0186 {
0187     /* one interrupt per channel bit */
0188     ser12_set_divisor(dev, 12);
0189     /*
0190      * first output the last bit (!) then call HDLC transmitter,
0191      * since this may take quite long
0192      */
0193     outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
0194     if (bc->modem.shreg <= 1)
0195         bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
0196     bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
0197                    (bc->modem.shreg & 1));
0198     bc->modem.shreg >>= 1;
0199 }
0200 
0201 /* --------------------------------------------------------------------- */
0202 
0203 static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
0204 {
0205     unsigned char cur_s;
0206     /*
0207      * do demodulator
0208      */
0209     cur_s = inb(MSR(dev->base_addr)) & 0x10;    /* the CTS line */
0210     hdlcdrv_channelbit(&bc->hdrv, cur_s);
0211     bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
0212         (cur_s != bc->modem.ser12.last_sample);
0213     bc->modem.ser12.last_sample = cur_s;
0214     if(bc->modem.ser12.dcd_shreg & 1) {
0215         if (!bc->opt_dcd) {
0216             unsigned int dcdspos, dcdsneg;
0217 
0218             dcdspos = dcdsneg = 0;
0219             dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
0220             if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
0221                 dcdspos += 2;
0222             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
0223             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
0224             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
0225 
0226             bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
0227         } else
0228             bc->modem.ser12.dcd_sum0--;
0229     }
0230     if(!bc->modem.ser12.dcd_time) {
0231         hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
0232                        bc->modem.ser12.dcd_sum1 +
0233                        bc->modem.ser12.dcd_sum2) < 0);
0234         bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
0235         bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
0236         /* offset to ensure DCD off on silent input */
0237         bc->modem.ser12.dcd_sum0 = 2;
0238         bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
0239     }
0240     bc->modem.ser12.dcd_time--;
0241     if (!bc->opt_dcd) {
0242         /*
0243          * PLL code for the improved software DCD algorithm
0244          */
0245         if (bc->modem.ser12.interm_sample) {
0246             /*
0247              * intermediate sample; set timing correction to normal
0248              */
0249             ser12_set_divisor(dev, 4);
0250         } else {
0251             /*
0252              * do PLL correction and call HDLC receiver
0253              */
0254             switch (bc->modem.ser12.dcd_shreg & 7) {
0255             case 1: /* transition too late */
0256                 ser12_set_divisor(dev, 5);
0257 #ifdef BAYCOM_DEBUG
0258                 bc->debug_vals.cur_pllcorr++;
0259 #endif /* BAYCOM_DEBUG */
0260                 break;
0261             case 4: /* transition too early */
0262                 ser12_set_divisor(dev, 3);
0263 #ifdef BAYCOM_DEBUG
0264                 bc->debug_vals.cur_pllcorr--;
0265 #endif /* BAYCOM_DEBUG */
0266                 break;
0267             default:
0268                 ser12_set_divisor(dev, 4);
0269                 break;
0270             }
0271             bc->modem.shreg >>= 1;
0272             if (bc->modem.ser12.last_sample ==
0273                 bc->modem.ser12.last_rxbit)
0274                 bc->modem.shreg |= 0x10000;
0275             bc->modem.ser12.last_rxbit =
0276                 bc->modem.ser12.last_sample;
0277         }
0278         if (++bc->modem.ser12.interm_sample >= 3)
0279             bc->modem.ser12.interm_sample = 0;
0280         /*
0281          * DCD stuff
0282          */
0283         if (bc->modem.ser12.dcd_shreg & 1) {
0284             unsigned int dcdspos, dcdsneg;
0285 
0286             dcdspos = dcdsneg = 0;
0287             dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
0288             dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
0289                 << 1;
0290             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
0291             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
0292             dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
0293 
0294             bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
0295         }
0296     } else {
0297         /*
0298          * PLL algorithm for the hardware squelch DCD algorithm
0299          */
0300         if (bc->modem.ser12.interm_sample) {
0301             /*
0302              * intermediate sample; set timing correction to normal
0303              */
0304             ser12_set_divisor(dev, 6);
0305         } else {
0306             /*
0307              * do PLL correction and call HDLC receiver
0308              */
0309             switch (bc->modem.ser12.dcd_shreg & 3) {
0310             case 1: /* transition too late */
0311                 ser12_set_divisor(dev, 7);
0312 #ifdef BAYCOM_DEBUG
0313                 bc->debug_vals.cur_pllcorr++;
0314 #endif /* BAYCOM_DEBUG */
0315                 break;
0316             case 2: /* transition too early */
0317                 ser12_set_divisor(dev, 5);
0318 #ifdef BAYCOM_DEBUG
0319                 bc->debug_vals.cur_pllcorr--;
0320 #endif /* BAYCOM_DEBUG */
0321                 break;
0322             default:
0323                 ser12_set_divisor(dev, 6);
0324                 break;
0325             }
0326             bc->modem.shreg >>= 1;
0327             if (bc->modem.ser12.last_sample ==
0328                 bc->modem.ser12.last_rxbit)
0329                 bc->modem.shreg |= 0x10000;
0330             bc->modem.ser12.last_rxbit =
0331                 bc->modem.ser12.last_sample;
0332         }
0333         bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
0334         /*
0335          * DCD stuff
0336          */
0337         bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
0338     }
0339     outb(0x0d, MCR(dev->base_addr));        /* transmitter off */
0340     if (bc->modem.shreg & 1) {
0341         hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
0342         bc->modem.shreg = 0x10000;
0343     }
0344     if(!bc->modem.ser12.dcd_time) {
0345         if (bc->opt_dcd & 1) 
0346             hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
0347         else
0348             hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
0349                            bc->modem.ser12.dcd_sum1 +
0350                            bc->modem.ser12.dcd_sum2) < 0);
0351         bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
0352         bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
0353         /* offset to ensure DCD off on silent input */
0354         bc->modem.ser12.dcd_sum0 = 2;
0355         bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
0356     }
0357     bc->modem.ser12.dcd_time--;
0358 }
0359 
0360 /* --------------------------------------------------------------------- */
0361 
0362 static irqreturn_t ser12_interrupt(int irq, void *dev_id)
0363 {
0364     struct net_device *dev = (struct net_device *)dev_id;
0365     struct baycom_state *bc = netdev_priv(dev);
0366     unsigned char iir;
0367 
0368     if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
0369         return IRQ_NONE;
0370     /* fast way out */
0371     if ((iir = inb(IIR(dev->base_addr))) & 1)
0372         return IRQ_NONE;
0373     baycom_int_freq(bc);
0374     do {
0375         switch (iir & 6) {
0376         case 6:
0377             inb(LSR(dev->base_addr));
0378             break;
0379             
0380         case 4:
0381             inb(RBR(dev->base_addr));
0382             break;
0383             
0384         case 2:
0385             /*
0386              * check if transmitter active
0387              */
0388             if (hdlcdrv_ptt(&bc->hdrv))
0389                 ser12_tx(dev, bc);
0390             else {
0391                 ser12_rx(dev, bc);
0392                 bc->modem.arb_divider--;
0393             }
0394             outb(0x00, THR(dev->base_addr));
0395             break;
0396             
0397         default:
0398             inb(MSR(dev->base_addr));
0399             break;
0400         }
0401         iir = inb(IIR(dev->base_addr));
0402     } while (!(iir & 1));
0403     if (bc->modem.arb_divider <= 0) {
0404         bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
0405         local_irq_enable();
0406         hdlcdrv_arbitrate(dev, &bc->hdrv);
0407     }
0408     local_irq_enable();
0409     hdlcdrv_transmitter(dev, &bc->hdrv);
0410     hdlcdrv_receiver(dev, &bc->hdrv);
0411     local_irq_disable();
0412     return IRQ_HANDLED;
0413 }
0414 
0415 /* --------------------------------------------------------------------- */
0416 
0417 enum uart { c_uart_unknown, c_uart_8250,
0418         c_uart_16450, c_uart_16550, c_uart_16550A};
0419 static const char *uart_str[] = { 
0420     "unknown", "8250", "16450", "16550", "16550A" 
0421 };
0422 
0423 static enum uart ser12_check_uart(unsigned int iobase)
0424 {
0425     unsigned char b1,b2,b3;
0426     enum uart u;
0427     enum uart uart_tab[] =
0428         { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
0429 
0430     b1 = inb(MCR(iobase));
0431     outb(b1 | 0x10, MCR(iobase));   /* loopback mode */
0432     b2 = inb(MSR(iobase));
0433     outb(0x1a, MCR(iobase));
0434     b3 = inb(MSR(iobase)) & 0xf0;
0435     outb(b1, MCR(iobase));          /* restore old values */
0436     outb(b2, MSR(iobase));
0437     if (b3 != 0x90)
0438         return c_uart_unknown;
0439     inb(RBR(iobase));
0440     inb(RBR(iobase));
0441     outb(0x01, FCR(iobase));        /* enable FIFOs */
0442     u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
0443     if (u == c_uart_16450) {
0444         outb(0x5a, SCR(iobase));
0445         b1 = inb(SCR(iobase));
0446         outb(0xa5, SCR(iobase));
0447         b2 = inb(SCR(iobase));
0448         if ((b1 != 0x5a) || (b2 != 0xa5))
0449             u = c_uart_8250;
0450     }
0451     return u;
0452 }
0453 
0454 /* --------------------------------------------------------------------- */
0455 
0456 static int ser12_open(struct net_device *dev)
0457 {
0458     struct baycom_state *bc = netdev_priv(dev);
0459     enum uart u;
0460 
0461     if (!dev || !bc)
0462         return -ENXIO;
0463     if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
0464         dev->irq < 2 || dev->irq > 15)
0465         return -ENXIO;
0466     if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
0467         return -EACCES;
0468     memset(&bc->modem, 0, sizeof(bc->modem));
0469     bc->hdrv.par.bitrate = 1200;
0470     if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
0471         release_region(dev->base_addr, SER12_EXTENT);       
0472         return -EIO;
0473     }
0474     outb(0, FCR(dev->base_addr));  /* disable FIFOs */
0475     outb(0x0d, MCR(dev->base_addr));
0476     outb(0, IER(dev->base_addr));
0477     if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
0478             "baycom_ser12", dev)) {
0479         release_region(dev->base_addr, SER12_EXTENT);       
0480         return -EBUSY;
0481     }
0482     /*
0483      * enable transmitter empty interrupt
0484      */
0485     outb(2, IER(dev->base_addr));
0486     /*
0487      * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
0488      * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
0489      * depending on the usage of the software DCD routine
0490      */
0491     ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
0492     printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", 
0493            bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
0494     return 0;
0495 }
0496 
0497 /* --------------------------------------------------------------------- */
0498 
0499 static int ser12_close(struct net_device *dev)
0500 {
0501     struct baycom_state *bc = netdev_priv(dev);
0502 
0503     if (!dev || !bc)
0504         return -EINVAL;
0505     /*
0506      * disable interrupts
0507      */
0508     outb(0, IER(dev->base_addr));
0509     outb(1, MCR(dev->base_addr));
0510     free_irq(dev->irq, dev);
0511     release_region(dev->base_addr, SER12_EXTENT);
0512     printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
0513            bc_drvname, dev->base_addr, dev->irq);
0514     return 0;
0515 }
0516 
0517 /* --------------------------------------------------------------------- */
0518 /*
0519  * ===================== hdlcdrv driver interface =========================
0520  */
0521 
0522 /* --------------------------------------------------------------------- */
0523 
0524 static int baycom_ioctl(struct net_device *dev, void __user *data,
0525             struct hdlcdrv_ioctl *hi, int cmd);
0526 
0527 /* --------------------------------------------------------------------- */
0528 
0529 static const struct hdlcdrv_ops ser12_ops = {
0530     .drvname = bc_drvname,
0531     .drvinfo = bc_drvinfo,
0532     .open    = ser12_open,
0533     .close   = ser12_close,
0534     .ioctl   = baycom_ioctl,
0535 };
0536 
0537 /* --------------------------------------------------------------------- */
0538 
0539 static int baycom_setmode(struct baycom_state *bc, const char *modestr)
0540 {
0541     if (strchr(modestr, '*'))
0542         bc->opt_dcd = 0;
0543     else if (strchr(modestr, '+'))
0544         bc->opt_dcd = -1;
0545     else if (strchr(modestr, '@'))
0546         bc->opt_dcd = -2;
0547     else
0548         bc->opt_dcd = 1;
0549     return 0;
0550 }
0551 
0552 /* --------------------------------------------------------------------- */
0553 
0554 static int baycom_ioctl(struct net_device *dev, void __user *data,
0555             struct hdlcdrv_ioctl *hi, int cmd)
0556 {
0557     struct baycom_state *bc;
0558     struct baycom_ioctl bi;
0559 
0560     if (!dev)
0561         return -EINVAL;
0562 
0563     bc = netdev_priv(dev);
0564     BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
0565 
0566     if (cmd != SIOCDEVPRIVATE)
0567         return -ENOIOCTLCMD;
0568     switch (hi->cmd) {
0569     default:
0570         break;
0571 
0572     case HDLCDRVCTL_GETMODE:
0573         strcpy(hi->data.modename, "ser12");
0574         if (bc->opt_dcd <= 0)
0575             strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
0576         if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
0577             return -EFAULT;
0578         return 0;
0579 
0580     case HDLCDRVCTL_SETMODE:
0581         if (netif_running(dev) || !capable(CAP_NET_ADMIN))
0582             return -EACCES;
0583         hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
0584         return baycom_setmode(bc, hi->data.modename);
0585 
0586     case HDLCDRVCTL_MODELIST:
0587         strcpy(hi->data.modename, "ser12");
0588         if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
0589             return -EFAULT;
0590         return 0;
0591 
0592     case HDLCDRVCTL_MODEMPARMASK:
0593         return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
0594 
0595     }
0596 
0597     if (copy_from_user(&bi, data, sizeof(bi)))
0598         return -EFAULT;
0599     switch (bi.cmd) {
0600     default:
0601         return -ENOIOCTLCMD;
0602 
0603 #ifdef BAYCOM_DEBUG
0604     case BAYCOMCTL_GETDEBUG:
0605         bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
0606         bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
0607         bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
0608         break;
0609 #endif /* BAYCOM_DEBUG */
0610 
0611     }
0612     if (copy_to_user(data, &bi, sizeof(bi)))
0613         return -EFAULT;
0614     return 0;
0615 
0616 }
0617 
0618 /* --------------------------------------------------------------------- */
0619 
0620 /*
0621  * command line settable parameters
0622  */
0623 static char *mode[NR_PORTS] = { "ser12*", };
0624 static int iobase[NR_PORTS] = { 0x3f8, };
0625 static int irq[NR_PORTS] = { 4, };
0626 
0627 module_param_array(mode, charp, NULL, 0);
0628 MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
0629 module_param_hw_array(iobase, int, ioport, NULL, 0);
0630 MODULE_PARM_DESC(iobase, "baycom io base address");
0631 module_param_hw_array(irq, int, irq, NULL, 0);
0632 MODULE_PARM_DESC(irq, "baycom irq number");
0633 
0634 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
0635 MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
0636 MODULE_LICENSE("GPL");
0637 
0638 /* --------------------------------------------------------------------- */
0639 
0640 static int __init init_baycomserhdx(void)
0641 {
0642     int i, found = 0;
0643     char set_hw = 1;
0644 
0645     printk(bc_drvinfo);
0646     /*
0647      * register net devices
0648      */
0649     for (i = 0; i < NR_PORTS; i++) {
0650         struct net_device *dev;
0651         struct baycom_state *bc;
0652         char ifname[IFNAMSIZ];
0653 
0654         sprintf(ifname, "bcsh%d", i);
0655 
0656         if (!mode[i])
0657             set_hw = 0;
0658         if (!set_hw)
0659             iobase[i] = irq[i] = 0;
0660 
0661         dev = hdlcdrv_register(&ser12_ops, 
0662                        sizeof(struct baycom_state),
0663                        ifname, iobase[i], irq[i], 0);
0664         if (IS_ERR(dev)) 
0665             break;
0666 
0667         bc = netdev_priv(dev);
0668         if (set_hw && baycom_setmode(bc, mode[i]))
0669             set_hw = 0;
0670         found++;
0671         baycom_device[i] = dev;
0672     }
0673 
0674     if (!found)
0675         return -ENXIO;
0676     return 0;
0677 }
0678 
0679 static void __exit cleanup_baycomserhdx(void)
0680 {
0681     int i;
0682 
0683     for(i = 0; i < NR_PORTS; i++) {
0684         struct net_device *dev = baycom_device[i];
0685 
0686         if (dev)
0687             hdlcdrv_unregister(dev);
0688     }
0689 }
0690 
0691 module_init(init_baycomserhdx);
0692 module_exit(cleanup_baycomserhdx);
0693 
0694 /* --------------------------------------------------------------------- */
0695 
0696 #ifndef MODULE
0697 
0698 /*
0699  * format: baycom_ser_hdx=io,irq,mode
0700  * mode: ser12    hardware DCD
0701  *       ser12*   software DCD
0702  *       ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
0703  *                mutes audio input to the modem
0704  *       ser12+   hardware DCD, inverted signal at DCD pin
0705  */
0706 
0707 static int __init baycom_ser_hdx_setup(char *str)
0708 {
0709         static unsigned nr_dev;
0710     int ints[3];
0711 
0712         if (nr_dev >= NR_PORTS)
0713                 return 0;
0714     str = get_options(str, 3, ints);
0715     if (ints[0] < 2)
0716         return 0;
0717     mode[nr_dev] = str;
0718     iobase[nr_dev] = ints[1];
0719     irq[nr_dev] = ints[2];
0720     nr_dev++;
0721     return 1;
0722 }
0723 
0724 __setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
0725 
0726 #endif /* MODULE */
0727 /* --------------------------------------------------------------------- */