cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

baycom_ser_hdx.c (20330B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*****************************************************************************/
      3
      4/*
      5 *	baycom_ser_hdx.c  -- baycom ser12 halfduplex 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 communications
     11 *  authority of your country.
     12 *
     13 *  Supported modems
     14 *
     15 *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
     16 *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
     17 *          is responsible for regenerating the receiver bit clock, as well as
     18 *          for handling the HDLC protocol. The modem connects to a serial port,
     19 *          hence the name. Since the serial port is not used as an async serial
     20 *          port, the kernel driver for serial ports cannot be used, and this
     21 *          driver only supports standard serial hardware (8250, 16450, 16550A)
     22 *
     23 *  Command line options (insmod command line)
     24 *
     25 *  mode     ser12    hardware DCD
     26 *           ser12*   software DCD
     27 *           ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
     28 *                    mutes audio input to the modem
     29 *           ser12+   hardware DCD, inverted signal at DCD pin
     30 *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
     31 *  irq      interrupt line of the port; common values are 4,3
     32 *
     33 *  History:
     34 *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
     35 *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
     36 *   0.3  26.04.1997  init code/data tagged
     37 *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
     38 *   0.5  11.11.1997  ser12/par96 split into separate files
     39 *   0.6  14.04.1998  cleanups
     40 *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
     41 *   0.8  10.08.1999  use module_init/module_exit
     42 *   0.9  12.02.2000  adapted to softnet driver interface
     43 *   0.10 03.07.2000  fix interface name handling
     44 */
     45
     46/*****************************************************************************/
     47
     48#include <linux/capability.h>
     49#include <linux/module.h>
     50#include <linux/ioport.h>
     51#include <linux/string.h>
     52#include <linux/init.h>
     53#include <linux/interrupt.h>
     54#include <linux/uaccess.h>
     55#include <asm/io.h>
     56#include <linux/hdlcdrv.h>
     57#include <linux/baycom.h>
     58#include <linux/jiffies.h>
     59
     60/* --------------------------------------------------------------------- */
     61
     62#define BAYCOM_DEBUG
     63
     64/* --------------------------------------------------------------------- */
     65
     66static const char bc_drvname[] = "baycom_ser_hdx";
     67static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
     68"baycom_ser_hdx: version 0.10\n";
     69
     70/* --------------------------------------------------------------------- */
     71
     72#define NR_PORTS 4
     73
     74static struct net_device *baycom_device[NR_PORTS];
     75
     76/* --------------------------------------------------------------------- */
     77
     78#define RBR(iobase) (iobase+0)
     79#define THR(iobase) (iobase+0)
     80#define IER(iobase) (iobase+1)
     81#define IIR(iobase) (iobase+2)
     82#define FCR(iobase) (iobase+2)
     83#define LCR(iobase) (iobase+3)
     84#define MCR(iobase) (iobase+4)
     85#define LSR(iobase) (iobase+5)
     86#define MSR(iobase) (iobase+6)
     87#define SCR(iobase) (iobase+7)
     88#define DLL(iobase) (iobase+0)
     89#define DLM(iobase) (iobase+1)
     90
     91#define SER12_EXTENT 8
     92
     93/* ---------------------------------------------------------------------- */
     94/*
     95 * Information that need to be kept for each board.
     96 */
     97
     98struct baycom_state {
     99	struct hdlcdrv_state hdrv;
    100
    101	int opt_dcd;
    102
    103	struct modem_state {
    104		short arb_divider;
    105		unsigned char flags;
    106		unsigned int shreg;
    107		struct modem_state_ser12 {
    108			unsigned char tx_bit;
    109			int dcd_sum0, dcd_sum1, dcd_sum2;
    110			unsigned char last_sample;
    111			unsigned char last_rxbit;
    112			unsigned int dcd_shreg;
    113			unsigned int dcd_time;
    114			unsigned int bit_pll;
    115			unsigned char interm_sample;
    116		} ser12;
    117	} modem;
    118
    119#ifdef BAYCOM_DEBUG
    120	struct debug_vals {
    121		unsigned long last_jiffies;
    122		unsigned cur_intcnt;
    123		unsigned last_intcnt;
    124		int cur_pllcorr;
    125		int last_pllcorr;
    126	} debug_vals;
    127#endif /* BAYCOM_DEBUG */
    128};
    129
    130/* --------------------------------------------------------------------- */
    131
    132static inline void baycom_int_freq(struct baycom_state *bc)
    133{
    134#ifdef BAYCOM_DEBUG
    135	unsigned long cur_jiffies = jiffies;
    136	/*
    137	 * measure the interrupt frequency
    138	 */
    139	bc->debug_vals.cur_intcnt++;
    140	if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
    141		bc->debug_vals.last_jiffies = cur_jiffies;
    142		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
    143		bc->debug_vals.cur_intcnt = 0;
    144		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
    145		bc->debug_vals.cur_pllcorr = 0;
    146	}
    147#endif /* BAYCOM_DEBUG */
    148}
    149
    150/* --------------------------------------------------------------------- */
    151/*
    152 * ===================== SER12 specific routines =========================
    153 */
    154
    155static inline void ser12_set_divisor(struct net_device *dev,
    156				     unsigned char divisor)
    157{
    158	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
    159	outb(divisor, DLL(dev->base_addr));
    160	outb(0, DLM(dev->base_addr));
    161	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
    162	/*
    163	 * make sure the next interrupt is generated;
    164	 * 0 must be used to power the modem; the modem draws its
    165	 * power from the TxD line
    166	 */
    167	outb(0x00, THR(dev->base_addr));
    168	/*
    169	 * it is important not to set the divider while transmitting;
    170	 * this reportedly makes some UARTs generating interrupts
    171	 * in the hundredthousands per second region
    172	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
    173	 */
    174}
    175
    176/* --------------------------------------------------------------------- */
    177
    178/*
    179 * must call the TX arbitrator every 10ms
    180 */
    181#define SER12_ARB_DIVIDER(bc)  (bc->opt_dcd ? 24 : 36)
    182			       
    183#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
    184
    185static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
    186{
    187	/* one interrupt per channel bit */
    188	ser12_set_divisor(dev, 12);
    189	/*
    190	 * first output the last bit (!) then call HDLC transmitter,
    191	 * since this may take quite long
    192	 */
    193	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
    194	if (bc->modem.shreg <= 1)
    195		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
    196	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
    197				   (bc->modem.shreg & 1));
    198	bc->modem.shreg >>= 1;
    199}
    200
    201/* --------------------------------------------------------------------- */
    202
    203static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
    204{
    205	unsigned char cur_s;
    206	/*
    207	 * do demodulator
    208	 */
    209	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
    210	hdlcdrv_channelbit(&bc->hdrv, cur_s);
    211	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
    212		(cur_s != bc->modem.ser12.last_sample);
    213	bc->modem.ser12.last_sample = cur_s;
    214	if(bc->modem.ser12.dcd_shreg & 1) {
    215		if (!bc->opt_dcd) {
    216			unsigned int dcdspos, dcdsneg;
    217
    218			dcdspos = dcdsneg = 0;
    219			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
    220			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
    221				dcdspos += 2;
    222			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
    223			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
    224			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
    225
    226			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
    227		} else
    228			bc->modem.ser12.dcd_sum0--;
    229	}
    230	if(!bc->modem.ser12.dcd_time) {
    231		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
    232					   bc->modem.ser12.dcd_sum1 +
    233					   bc->modem.ser12.dcd_sum2) < 0);
    234		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
    235		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
    236		/* offset to ensure DCD off on silent input */
    237		bc->modem.ser12.dcd_sum0 = 2;
    238		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
    239	}
    240	bc->modem.ser12.dcd_time--;
    241	if (!bc->opt_dcd) {
    242		/*
    243		 * PLL code for the improved software DCD algorithm
    244		 */
    245		if (bc->modem.ser12.interm_sample) {
    246			/*
    247			 * intermediate sample; set timing correction to normal
    248			 */
    249			ser12_set_divisor(dev, 4);
    250		} else {
    251			/*
    252			 * do PLL correction and call HDLC receiver
    253			 */
    254			switch (bc->modem.ser12.dcd_shreg & 7) {
    255			case 1: /* transition too late */
    256				ser12_set_divisor(dev, 5);
    257#ifdef BAYCOM_DEBUG
    258				bc->debug_vals.cur_pllcorr++;
    259#endif /* BAYCOM_DEBUG */
    260				break;
    261			case 4:	/* transition too early */
    262				ser12_set_divisor(dev, 3);
    263#ifdef BAYCOM_DEBUG
    264				bc->debug_vals.cur_pllcorr--;
    265#endif /* BAYCOM_DEBUG */
    266				break;
    267			default:
    268				ser12_set_divisor(dev, 4);
    269				break;
    270			}
    271			bc->modem.shreg >>= 1;
    272			if (bc->modem.ser12.last_sample ==
    273			    bc->modem.ser12.last_rxbit)
    274				bc->modem.shreg |= 0x10000;
    275			bc->modem.ser12.last_rxbit =
    276				bc->modem.ser12.last_sample;
    277		}
    278		if (++bc->modem.ser12.interm_sample >= 3)
    279			bc->modem.ser12.interm_sample = 0;
    280		/*
    281		 * DCD stuff
    282		 */
    283		if (bc->modem.ser12.dcd_shreg & 1) {
    284			unsigned int dcdspos, dcdsneg;
    285
    286			dcdspos = dcdsneg = 0;
    287			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
    288			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
    289				<< 1;
    290			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
    291			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
    292			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
    293
    294			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
    295		}
    296	} else {
    297		/*
    298		 * PLL algorithm for the hardware squelch DCD algorithm
    299		 */
    300		if (bc->modem.ser12.interm_sample) {
    301			/*
    302			 * intermediate sample; set timing correction to normal
    303			 */
    304			ser12_set_divisor(dev, 6);
    305		} else {
    306			/*
    307			 * do PLL correction and call HDLC receiver
    308			 */
    309			switch (bc->modem.ser12.dcd_shreg & 3) {
    310			case 1: /* transition too late */
    311				ser12_set_divisor(dev, 7);
    312#ifdef BAYCOM_DEBUG
    313				bc->debug_vals.cur_pllcorr++;
    314#endif /* BAYCOM_DEBUG */
    315				break;
    316			case 2:	/* transition too early */
    317				ser12_set_divisor(dev, 5);
    318#ifdef BAYCOM_DEBUG
    319				bc->debug_vals.cur_pllcorr--;
    320#endif /* BAYCOM_DEBUG */
    321				break;
    322			default:
    323				ser12_set_divisor(dev, 6);
    324				break;
    325			}
    326			bc->modem.shreg >>= 1;
    327			if (bc->modem.ser12.last_sample ==
    328			    bc->modem.ser12.last_rxbit)
    329				bc->modem.shreg |= 0x10000;
    330			bc->modem.ser12.last_rxbit =
    331				bc->modem.ser12.last_sample;
    332		}
    333		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
    334		/*
    335		 * DCD stuff
    336		 */
    337		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
    338	}
    339	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
    340	if (bc->modem.shreg & 1) {
    341		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
    342		bc->modem.shreg = 0x10000;
    343	}
    344	if(!bc->modem.ser12.dcd_time) {
    345		if (bc->opt_dcd & 1) 
    346			hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
    347		else
    348			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
    349						   bc->modem.ser12.dcd_sum1 +
    350						   bc->modem.ser12.dcd_sum2) < 0);
    351		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
    352		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
    353		/* offset to ensure DCD off on silent input */
    354		bc->modem.ser12.dcd_sum0 = 2;
    355		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
    356	}
    357	bc->modem.ser12.dcd_time--;
    358}
    359
    360/* --------------------------------------------------------------------- */
    361
    362static irqreturn_t ser12_interrupt(int irq, void *dev_id)
    363{
    364	struct net_device *dev = (struct net_device *)dev_id;
    365	struct baycom_state *bc = netdev_priv(dev);
    366	unsigned char iir;
    367
    368	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
    369		return IRQ_NONE;
    370	/* fast way out */
    371	if ((iir = inb(IIR(dev->base_addr))) & 1)
    372		return IRQ_NONE;
    373	baycom_int_freq(bc);
    374	do {
    375		switch (iir & 6) {
    376		case 6:
    377			inb(LSR(dev->base_addr));
    378			break;
    379			
    380		case 4:
    381			inb(RBR(dev->base_addr));
    382			break;
    383			
    384		case 2:
    385			/*
    386			 * check if transmitter active
    387			 */
    388			if (hdlcdrv_ptt(&bc->hdrv))
    389				ser12_tx(dev, bc);
    390			else {
    391				ser12_rx(dev, bc);
    392				bc->modem.arb_divider--;
    393			}
    394			outb(0x00, THR(dev->base_addr));
    395			break;
    396			
    397		default:
    398			inb(MSR(dev->base_addr));
    399			break;
    400		}
    401		iir = inb(IIR(dev->base_addr));
    402	} while (!(iir & 1));
    403	if (bc->modem.arb_divider <= 0) {
    404		bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
    405		local_irq_enable();
    406		hdlcdrv_arbitrate(dev, &bc->hdrv);
    407	}
    408	local_irq_enable();
    409	hdlcdrv_transmitter(dev, &bc->hdrv);
    410	hdlcdrv_receiver(dev, &bc->hdrv);
    411	local_irq_disable();
    412	return IRQ_HANDLED;
    413}
    414
    415/* --------------------------------------------------------------------- */
    416
    417enum uart { c_uart_unknown, c_uart_8250,
    418	    c_uart_16450, c_uart_16550, c_uart_16550A};
    419static const char *uart_str[] = { 
    420	"unknown", "8250", "16450", "16550", "16550A" 
    421};
    422
    423static enum uart ser12_check_uart(unsigned int iobase)
    424{
    425	unsigned char b1,b2,b3;
    426	enum uart u;
    427	enum uart uart_tab[] =
    428		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
    429
    430	b1 = inb(MCR(iobase));
    431	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
    432	b2 = inb(MSR(iobase));
    433	outb(0x1a, MCR(iobase));
    434	b3 = inb(MSR(iobase)) & 0xf0;
    435	outb(b1, MCR(iobase));			/* restore old values */
    436	outb(b2, MSR(iobase));
    437	if (b3 != 0x90)
    438		return c_uart_unknown;
    439	inb(RBR(iobase));
    440	inb(RBR(iobase));
    441	outb(0x01, FCR(iobase));		/* enable FIFOs */
    442	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
    443	if (u == c_uart_16450) {
    444		outb(0x5a, SCR(iobase));
    445		b1 = inb(SCR(iobase));
    446		outb(0xa5, SCR(iobase));
    447		b2 = inb(SCR(iobase));
    448		if ((b1 != 0x5a) || (b2 != 0xa5))
    449			u = c_uart_8250;
    450	}
    451	return u;
    452}
    453
    454/* --------------------------------------------------------------------- */
    455
    456static int ser12_open(struct net_device *dev)
    457{
    458	struct baycom_state *bc = netdev_priv(dev);
    459	enum uart u;
    460
    461	if (!dev || !bc)
    462		return -ENXIO;
    463	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
    464	    dev->irq < 2 || dev->irq > 15)
    465		return -ENXIO;
    466	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
    467		return -EACCES;
    468	memset(&bc->modem, 0, sizeof(bc->modem));
    469	bc->hdrv.par.bitrate = 1200;
    470	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
    471		release_region(dev->base_addr, SER12_EXTENT);       
    472		return -EIO;
    473	}
    474	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
    475	outb(0x0d, MCR(dev->base_addr));
    476	outb(0, IER(dev->base_addr));
    477	if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
    478			"baycom_ser12", dev)) {
    479		release_region(dev->base_addr, SER12_EXTENT);       
    480		return -EBUSY;
    481	}
    482	/*
    483	 * enable transmitter empty interrupt
    484	 */
    485	outb(2, IER(dev->base_addr));
    486	/*
    487	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
    488	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
    489	 * depending on the usage of the software DCD routine
    490	 */
    491	ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
    492	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", 
    493	       bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
    494	return 0;
    495}
    496
    497/* --------------------------------------------------------------------- */
    498
    499static int ser12_close(struct net_device *dev)
    500{
    501	struct baycom_state *bc = netdev_priv(dev);
    502
    503	if (!dev || !bc)
    504		return -EINVAL;
    505	/*
    506	 * disable interrupts
    507	 */
    508	outb(0, IER(dev->base_addr));
    509	outb(1, MCR(dev->base_addr));
    510	free_irq(dev->irq, dev);
    511	release_region(dev->base_addr, SER12_EXTENT);
    512	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
    513	       bc_drvname, dev->base_addr, dev->irq);
    514	return 0;
    515}
    516
    517/* --------------------------------------------------------------------- */
    518/*
    519 * ===================== hdlcdrv driver interface =========================
    520 */
    521
    522/* --------------------------------------------------------------------- */
    523
    524static int baycom_ioctl(struct net_device *dev, void __user *data,
    525			struct hdlcdrv_ioctl *hi, int cmd);
    526
    527/* --------------------------------------------------------------------- */
    528
    529static const struct hdlcdrv_ops ser12_ops = {
    530	.drvname = bc_drvname,
    531	.drvinfo = bc_drvinfo,
    532	.open    = ser12_open,
    533	.close   = ser12_close,
    534	.ioctl   = baycom_ioctl,
    535};
    536
    537/* --------------------------------------------------------------------- */
    538
    539static int baycom_setmode(struct baycom_state *bc, const char *modestr)
    540{
    541	if (strchr(modestr, '*'))
    542		bc->opt_dcd = 0;
    543	else if (strchr(modestr, '+'))
    544		bc->opt_dcd = -1;
    545	else if (strchr(modestr, '@'))
    546		bc->opt_dcd = -2;
    547	else
    548		bc->opt_dcd = 1;
    549	return 0;
    550}
    551
    552/* --------------------------------------------------------------------- */
    553
    554static int baycom_ioctl(struct net_device *dev, void __user *data,
    555			struct hdlcdrv_ioctl *hi, int cmd)
    556{
    557	struct baycom_state *bc;
    558	struct baycom_ioctl bi;
    559
    560	if (!dev)
    561		return -EINVAL;
    562
    563	bc = netdev_priv(dev);
    564	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
    565
    566	if (cmd != SIOCDEVPRIVATE)
    567		return -ENOIOCTLCMD;
    568	switch (hi->cmd) {
    569	default:
    570		break;
    571
    572	case HDLCDRVCTL_GETMODE:
    573		strcpy(hi->data.modename, "ser12");
    574		if (bc->opt_dcd <= 0)
    575			strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
    576		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
    577			return -EFAULT;
    578		return 0;
    579
    580	case HDLCDRVCTL_SETMODE:
    581		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
    582			return -EACCES;
    583		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
    584		return baycom_setmode(bc, hi->data.modename);
    585
    586	case HDLCDRVCTL_MODELIST:
    587		strcpy(hi->data.modename, "ser12");
    588		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
    589			return -EFAULT;
    590		return 0;
    591
    592	case HDLCDRVCTL_MODEMPARMASK:
    593		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
    594
    595	}
    596
    597	if (copy_from_user(&bi, data, sizeof(bi)))
    598		return -EFAULT;
    599	switch (bi.cmd) {
    600	default:
    601		return -ENOIOCTLCMD;
    602
    603#ifdef BAYCOM_DEBUG
    604	case BAYCOMCTL_GETDEBUG:
    605		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
    606		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
    607		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
    608		break;
    609#endif /* BAYCOM_DEBUG */
    610
    611	}
    612	if (copy_to_user(data, &bi, sizeof(bi)))
    613		return -EFAULT;
    614	return 0;
    615
    616}
    617
    618/* --------------------------------------------------------------------- */
    619
    620/*
    621 * command line settable parameters
    622 */
    623static char *mode[NR_PORTS] = { "ser12*", };
    624static int iobase[NR_PORTS] = { 0x3f8, };
    625static int irq[NR_PORTS] = { 4, };
    626
    627module_param_array(mode, charp, NULL, 0);
    628MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
    629module_param_hw_array(iobase, int, ioport, NULL, 0);
    630MODULE_PARM_DESC(iobase, "baycom io base address");
    631module_param_hw_array(irq, int, irq, NULL, 0);
    632MODULE_PARM_DESC(irq, "baycom irq number");
    633
    634MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
    635MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
    636MODULE_LICENSE("GPL");
    637
    638/* --------------------------------------------------------------------- */
    639
    640static int __init init_baycomserhdx(void)
    641{
    642	int i, found = 0;
    643	char set_hw = 1;
    644
    645	printk(bc_drvinfo);
    646	/*
    647	 * register net devices
    648	 */
    649	for (i = 0; i < NR_PORTS; i++) {
    650		struct net_device *dev;
    651		struct baycom_state *bc;
    652		char ifname[IFNAMSIZ];
    653
    654		sprintf(ifname, "bcsh%d", i);
    655
    656		if (!mode[i])
    657			set_hw = 0;
    658		if (!set_hw)
    659			iobase[i] = irq[i] = 0;
    660
    661		dev = hdlcdrv_register(&ser12_ops, 
    662				       sizeof(struct baycom_state),
    663				       ifname, iobase[i], irq[i], 0);
    664		if (IS_ERR(dev)) 
    665			break;
    666
    667		bc = netdev_priv(dev);
    668		if (set_hw && baycom_setmode(bc, mode[i]))
    669			set_hw = 0;
    670		found++;
    671		baycom_device[i] = dev;
    672	}
    673
    674	if (!found)
    675		return -ENXIO;
    676	return 0;
    677}
    678
    679static void __exit cleanup_baycomserhdx(void)
    680{
    681	int i;
    682
    683	for(i = 0; i < NR_PORTS; i++) {
    684		struct net_device *dev = baycom_device[i];
    685
    686		if (dev)
    687			hdlcdrv_unregister(dev);
    688	}
    689}
    690
    691module_init(init_baycomserhdx);
    692module_exit(cleanup_baycomserhdx);
    693
    694/* --------------------------------------------------------------------- */
    695
    696#ifndef MODULE
    697
    698/*
    699 * format: baycom_ser_hdx=io,irq,mode
    700 * mode: ser12    hardware DCD
    701 *       ser12*   software DCD
    702 *       ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
    703 *                mutes audio input to the modem
    704 *       ser12+   hardware DCD, inverted signal at DCD pin
    705 */
    706
    707static int __init baycom_ser_hdx_setup(char *str)
    708{
    709        static unsigned nr_dev;
    710	int ints[3];
    711
    712        if (nr_dev >= NR_PORTS)
    713                return 0;
    714	str = get_options(str, 3, ints);
    715	if (ints[0] < 2)
    716		return 0;
    717	mode[nr_dev] = str;
    718	iobase[nr_dev] = ints[1];
    719	irq[nr_dev] = ints[2];
    720	nr_dev++;
    721	return 1;
    722}
    723
    724__setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
    725
    726#endif /* MODULE */
    727/* --------------------------------------------------------------------- */