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_fdx.c (19751B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*****************************************************************************/
      3
      4/*
      5 *	baycom_ser_fdx.c  -- baycom ser12 fullduplex 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 *          This modem usually draws its supply current out of the otherwise unused
     24 *          TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes
     25 *          is transmitted to achieve a positive supply voltage.
     26 *
     27 *  hsk:    This is a 4800 baud FSK modem, designed for TNC use. It works fine
     28 *          in 'baycom-mode' :-)  In contrast to the TCM3105 modem, power is
     29 *          externally supplied. So there's no need to provide the 0x00-byte-stream
     30 *          when receiving or idle, which drastically reduces interrupt load.
     31 *
     32 *  Command line options (insmod command line)
     33 *
     34 *  mode     ser#    hardware DCD
     35 *           ser#*   software DCD
     36 *           ser#+   hardware DCD, inverted signal at DCD pin
     37 *           '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
     38 *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
     39 *  baud     baud rate (between 300 and 4800)
     40 *  irq      interrupt line of the port; common values are 4,3
     41 *
     42 *  History:
     43 *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
     44 *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
     45 *   0.3  26.04.1997  init code/data tagged
     46 *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
     47 *   0.5  11.11.1997  ser12/par96 split into separate files
     48 *   0.6  24.01.1998  Thorsten Kranzkowski, dl8bcu and Thomas Sailer:
     49 *                    reduced interrupt load in transmit case
     50 *                    reworked receiver
     51 *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
     52 *   0.8  10.08.1999  use module_init/module_exit
     53 *   0.9  12.02.2000  adapted to softnet driver interface
     54 *   0.10 03.07.2000  fix interface name handling
     55 */
     56
     57/*****************************************************************************/
     58
     59#include <linux/capability.h>
     60#include <linux/module.h>
     61#include <linux/ioport.h>
     62#include <linux/string.h>
     63#include <linux/init.h>
     64#include <linux/interrupt.h>
     65#include <linux/hdlcdrv.h>
     66#include <linux/baycom.h>
     67#include <linux/jiffies.h>
     68#include <linux/time64.h>
     69
     70#include <linux/uaccess.h>
     71#include <asm/io.h>
     72#include <asm/irq.h>
     73
     74/* --------------------------------------------------------------------- */
     75
     76#define BAYCOM_DEBUG
     77
     78/* --------------------------------------------------------------------- */
     79
     80static const char bc_drvname[] = "baycom_ser_fdx";
     81static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
     82"baycom_ser_fdx: version 0.10\n";
     83
     84/* --------------------------------------------------------------------- */
     85
     86#define NR_PORTS 4
     87
     88static struct net_device *baycom_device[NR_PORTS];
     89
     90/* --------------------------------------------------------------------- */
     91
     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)
    104
    105#define SER12_EXTENT 8
    106
    107/* ---------------------------------------------------------------------- */
    108/*
    109 * Information that need to be kept for each board.
    110 */
    111
    112struct baycom_state {
    113	struct hdlcdrv_state hdrv;
    114
    115	unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout;
    116	int opt_dcd;
    117
    118	struct modem_state {
    119		unsigned char flags;
    120		unsigned char ptt;
    121		unsigned int shreg;
    122		struct modem_state_ser12 {
    123			unsigned char tx_bit;
    124			unsigned char last_rxbit;
    125			int dcd_sum0, dcd_sum1, dcd_sum2;
    126			int dcd_time;
    127			unsigned int pll_time;
    128			unsigned int txshreg;
    129		} ser12;
    130	} modem;
    131
    132#ifdef BAYCOM_DEBUG
    133	struct debug_vals {
    134		unsigned long last_jiffies;
    135		unsigned cur_intcnt;
    136		unsigned last_intcnt;
    137		int cur_pllcorr;
    138		int last_pllcorr;
    139	} debug_vals;
    140#endif /* BAYCOM_DEBUG */
    141};
    142
    143/* --------------------------------------------------------------------- */
    144
    145static inline void baycom_int_freq(struct baycom_state *bc)
    146{
    147#ifdef BAYCOM_DEBUG
    148	unsigned long cur_jiffies = jiffies;
    149	/*
    150	 * measure the interrupt frequency
    151	 */
    152	bc->debug_vals.cur_intcnt++;
    153	if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
    154		bc->debug_vals.last_jiffies = cur_jiffies;
    155		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
    156		bc->debug_vals.cur_intcnt = 0;
    157		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
    158		bc->debug_vals.cur_pllcorr = 0;
    159	}
    160#endif /* BAYCOM_DEBUG */
    161}
    162
    163/* --------------------------------------------------------------------- */
    164/*
    165 * ===================== SER12 specific routines =========================
    166 */
    167
    168/* --------------------------------------------------------------------- */
    169
    170static inline void ser12_set_divisor(struct net_device *dev,
    171                                     unsigned int divisor)
    172{
    173        outb(0x81, LCR(dev->base_addr));        /* DLAB = 1 */
    174        outb(divisor, DLL(dev->base_addr));
    175        outb(divisor >> 8, DLM(dev->base_addr));
    176        outb(0x01, LCR(dev->base_addr));        /* word length = 6 */
    177        /*
    178         * make sure the next interrupt is generated;
    179         * 0 must be used to power the modem; the modem draws its
    180         * power from the TxD line
    181         */
    182        outb(0x00, THR(dev->base_addr));
    183        /*
    184         * it is important not to set the divider while transmitting;
    185         * this reportedly makes some UARTs generating interrupts
    186         * in the hundredthousands per second region
    187         * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
    188         */
    189}
    190
    191static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timespec64 *ts, unsigned char curs)
    192{
    193	int timediff;
    194	int bdus8 = bc->baud_us >> 3;
    195	int bdus4 = bc->baud_us >> 2;
    196	int bdus2 = bc->baud_us >> 1;
    197
    198	timediff = 1000000 + ts->tv_nsec / NSEC_PER_USEC -
    199					bc->modem.ser12.pll_time;
    200	while (timediff >= 500000)
    201		timediff -= 1000000;
    202	while (timediff >= bdus2) {
    203		timediff -= bc->baud_us;
    204		bc->modem.ser12.pll_time += bc->baud_us;
    205		bc->modem.ser12.dcd_time--;
    206		/* first check if there is room to add a bit */
    207		if (bc->modem.shreg & 1) {
    208			hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff);
    209			bc->modem.shreg = 0x10000;
    210		}
    211		/* add a one bit */
    212		bc->modem.shreg >>= 1;
    213	}
    214	if (bc->modem.ser12.dcd_time <= 0) {
    215		if (!bc->opt_dcd)
    216			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
    217						   bc->modem.ser12.dcd_sum1 + 
    218						   bc->modem.ser12.dcd_sum2) < 0);
    219		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
    220		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
    221		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
    222		bc->modem.ser12.dcd_time += 120;
    223	}
    224	if (bc->modem.ser12.last_rxbit != curs) {
    225		bc->modem.ser12.last_rxbit = curs;
    226		bc->modem.shreg |= 0x10000;
    227		/* adjust the PLL */
    228		if (timediff > 0)
    229			bc->modem.ser12.pll_time += bdus8;
    230		else
    231			bc->modem.ser12.pll_time += 1000000 - bdus8;
    232		/* update DCD */
    233		if (abs(timediff) > bdus4)
    234			bc->modem.ser12.dcd_sum0 += 4;
    235		else
    236			bc->modem.ser12.dcd_sum0--;
    237#ifdef BAYCOM_DEBUG
    238		bc->debug_vals.cur_pllcorr = timediff;
    239#endif /* BAYCOM_DEBUG */
    240	}
    241	while (bc->modem.ser12.pll_time >= 1000000)
    242		bc->modem.ser12.pll_time -= 1000000;
    243}
    244
    245/* --------------------------------------------------------------------- */
    246
    247static irqreturn_t ser12_interrupt(int irq, void *dev_id)
    248{
    249	struct net_device *dev = (struct net_device *)dev_id;
    250	struct baycom_state *bc = netdev_priv(dev);
    251	struct timespec64 ts;
    252	unsigned char iir, msr;
    253	unsigned int txcount = 0;
    254
    255	if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC)
    256		return IRQ_NONE;
    257	/* fast way out for shared irq */
    258	if ((iir = inb(IIR(dev->base_addr))) & 1) 	
    259		return IRQ_NONE;
    260	/* get current time */
    261	ktime_get_ts64(&ts);
    262	msr = inb(MSR(dev->base_addr));
    263	/* delta DCD */
    264	if ((msr & 8) && bc->opt_dcd)
    265		hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
    266	do {
    267		switch (iir & 6) {
    268		case 6:
    269			inb(LSR(dev->base_addr));
    270			break;
    271			
    272		case 4:
    273			inb(RBR(dev->base_addr));
    274			break;
    275			
    276		case 2:
    277			/*
    278			 * make sure the next interrupt is generated;
    279			 * 0 must be used to power the modem; the modem draws its
    280			 * power from the TxD line
    281			 */
    282			outb(0x00, THR(dev->base_addr));
    283			baycom_int_freq(bc);
    284			txcount++;
    285			/*
    286			 * first output the last bit (!) then call HDLC transmitter,
    287			 * since this may take quite long
    288			 */
    289			if (bc->modem.ptt)
    290				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
    291			else
    292				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
    293			break;
    294			
    295		default:
    296			msr = inb(MSR(dev->base_addr));
    297			/* delta DCD */
    298			if ((msr & 8) && bc->opt_dcd) 
    299				hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
    300			break;
    301		}
    302		iir = inb(IIR(dev->base_addr));
    303	} while (!(iir & 1));
    304	ser12_rx(dev, bc, &ts, msr & 0x10); /* CTS */
    305	if (bc->modem.ptt && txcount) {
    306		if (bc->modem.ser12.txshreg <= 1) {
    307			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
    308			if (!hdlcdrv_ptt(&bc->hdrv)) {
    309				ser12_set_divisor(dev, 115200/100/8);
    310				bc->modem.ptt = 0;
    311				goto end_transmit;
    312			}
    313		}
    314		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
    315		bc->modem.ser12.txshreg >>= 1;
    316	}
    317 end_transmit:
    318	local_irq_enable();
    319	if (!bc->modem.ptt && txcount) {
    320		hdlcdrv_arbitrate(dev, &bc->hdrv);
    321		if (hdlcdrv_ptt(&bc->hdrv)) {
    322			ser12_set_divisor(dev, bc->baud_uartdiv);
    323			bc->modem.ser12.txshreg = 1;
    324			bc->modem.ptt = 1;
    325		}
    326	}
    327	hdlcdrv_transmitter(dev, &bc->hdrv);
    328	hdlcdrv_receiver(dev, &bc->hdrv);
    329	local_irq_disable();
    330	return IRQ_HANDLED;
    331}
    332
    333/* --------------------------------------------------------------------- */
    334
    335enum uart { c_uart_unknown, c_uart_8250,
    336	    c_uart_16450, c_uart_16550, c_uart_16550A};
    337static const char *uart_str[] = { 
    338	"unknown", "8250", "16450", "16550", "16550A" 
    339};
    340
    341static enum uart ser12_check_uart(unsigned int iobase)
    342{
    343	unsigned char b1,b2,b3;
    344	enum uart u;
    345	enum uart uart_tab[] =
    346		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
    347
    348	b1 = inb(MCR(iobase));
    349	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
    350	b2 = inb(MSR(iobase));
    351	outb(0x1a, MCR(iobase));
    352	b3 = inb(MSR(iobase)) & 0xf0;
    353	outb(b1, MCR(iobase));			/* restore old values */
    354	outb(b2, MSR(iobase));
    355	if (b3 != 0x90)
    356		return c_uart_unknown;
    357	inb(RBR(iobase));
    358	inb(RBR(iobase));
    359	outb(0x01, FCR(iobase));		/* enable FIFOs */
    360	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
    361	if (u == c_uart_16450) {
    362		outb(0x5a, SCR(iobase));
    363		b1 = inb(SCR(iobase));
    364		outb(0xa5, SCR(iobase));
    365		b2 = inb(SCR(iobase));
    366		if ((b1 != 0x5a) || (b2 != 0xa5))
    367			u = c_uart_8250;
    368	}
    369	return u;
    370}
    371
    372/* --------------------------------------------------------------------- */
    373
    374static int ser12_open(struct net_device *dev)
    375{
    376	struct baycom_state *bc = netdev_priv(dev);
    377	enum uart u;
    378
    379	if (!dev || !bc)
    380		return -ENXIO;
    381	if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT ||
    382	    dev->irq < 2 || dev->irq > nr_irqs) {
    383		printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) "
    384				"or irq (2 <= irq <= %d)\n",
    385				0xffff-SER12_EXTENT, nr_irqs);
    386		return -ENXIO;
    387	}
    388	if (bc->baud < 300 || bc->baud > 4800) {
    389		printk(KERN_INFO "baycom_ser_fdx: invalid baudrate "
    390				"(300...4800)\n");
    391		return -EINVAL;
    392	}
    393	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) {
    394		printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy\n",
    395		       dev->base_addr);
    396		return -EACCES;
    397	}
    398	memset(&bc->modem, 0, sizeof(bc->modem));
    399	bc->hdrv.par.bitrate = bc->baud;
    400	bc->baud_us = 1000000/bc->baud;
    401	bc->baud_uartdiv = (115200/8)/bc->baud;
    402	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){
    403		release_region(dev->base_addr, SER12_EXTENT);
    404		return -EIO;
    405	}
    406	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
    407	outb(0x0d, MCR(dev->base_addr));
    408	outb(0, IER(dev->base_addr));
    409	if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
    410			"baycom_ser_fdx", dev)) {
    411		release_region(dev->base_addr, SER12_EXTENT);
    412		return -EBUSY;
    413	}
    414	/*
    415	 * set the SIO to 6 Bits/character; during receive,
    416	 * the baud rate is set to produce 100 ints/sec
    417	 * to feed the channel arbitration process,
    418	 * during transmit to baud ints/sec to run
    419	 * the transmitter
    420	 */
    421	ser12_set_divisor(dev, 115200/100/8);
    422	/*
    423	 * enable transmitter empty interrupt and modem status interrupt
    424	 */
    425	outb(0x0a, IER(dev->base_addr));
    426	/*
    427	 * make sure the next interrupt is generated;
    428	 * 0 must be used to power the modem; the modem draws its
    429	 * power from the TxD line
    430	 */
    431	outb(0x00, THR(dev->base_addr));
    432	hdlcdrv_setdcd(&bc->hdrv, 0);
    433	printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n",
    434	       bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]);
    435	return 0;
    436}
    437
    438/* --------------------------------------------------------------------- */
    439
    440static int ser12_close(struct net_device *dev)
    441{
    442	struct baycom_state *bc = netdev_priv(dev);
    443
    444	if (!dev || !bc)
    445		return -EINVAL;
    446	/*
    447	 * disable interrupts
    448	 */
    449	outb(0, IER(dev->base_addr));
    450	outb(1, MCR(dev->base_addr));
    451	free_irq(dev->irq, dev);
    452	release_region(dev->base_addr, SER12_EXTENT);
    453	printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
    454	       bc_drvname, dev->base_addr, dev->irq);
    455	return 0;
    456}
    457
    458/* --------------------------------------------------------------------- */
    459/*
    460 * ===================== hdlcdrv driver interface =========================
    461 */
    462
    463/* --------------------------------------------------------------------- */
    464
    465static int baycom_ioctl(struct net_device *dev, void __user *data,
    466			struct hdlcdrv_ioctl *hi, int cmd);
    467
    468/* --------------------------------------------------------------------- */
    469
    470static const struct hdlcdrv_ops ser12_ops = {
    471	.drvname = bc_drvname,
    472	.drvinfo = bc_drvinfo,
    473	.open    = ser12_open,
    474	.close   = ser12_close,
    475	.ioctl   = baycom_ioctl,
    476};
    477
    478/* --------------------------------------------------------------------- */
    479
    480static int baycom_setmode(struct baycom_state *bc, const char *modestr)
    481{
    482	unsigned int baud;
    483
    484	if (!strncmp(modestr, "ser", 3)) {
    485		baud = simple_strtoul(modestr+3, NULL, 10);
    486		if (baud >= 3 && baud <= 48)
    487			bc->baud = baud*100;
    488	}
    489	if (strchr(modestr, '*'))
    490		bc->opt_dcd = 0;
    491	else if (strchr(modestr, '+'))
    492		bc->opt_dcd = -1;
    493	else
    494		bc->opt_dcd = 1;
    495	return 0;
    496}
    497
    498/* --------------------------------------------------------------------- */
    499
    500static int baycom_ioctl(struct net_device *dev, void __user *data,
    501			struct hdlcdrv_ioctl *hi, int cmd)
    502{
    503	struct baycom_state *bc;
    504	struct baycom_ioctl bi;
    505
    506	if (!dev)
    507		return -EINVAL;
    508
    509	bc = netdev_priv(dev);
    510	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
    511
    512	if (cmd != SIOCDEVPRIVATE)
    513		return -ENOIOCTLCMD;
    514	switch (hi->cmd) {
    515	default:
    516		break;
    517
    518	case HDLCDRVCTL_GETMODE:
    519		sprintf(hi->data.modename, "ser%u", bc->baud / 100);
    520		if (bc->opt_dcd <= 0)
    521			strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+");
    522		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
    523			return -EFAULT;
    524		return 0;
    525
    526	case HDLCDRVCTL_SETMODE:
    527		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
    528			return -EACCES;
    529		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
    530		return baycom_setmode(bc, hi->data.modename);
    531
    532	case HDLCDRVCTL_MODELIST:
    533		strcpy(hi->data.modename, "ser12,ser3,ser24");
    534		if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl)))
    535			return -EFAULT;
    536		return 0;
    537
    538	case HDLCDRVCTL_MODEMPARMASK:
    539		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
    540
    541	}
    542
    543	if (copy_from_user(&bi, data, sizeof(bi)))
    544		return -EFAULT;
    545	switch (bi.cmd) {
    546	default:
    547		return -ENOIOCTLCMD;
    548
    549#ifdef BAYCOM_DEBUG
    550	case BAYCOMCTL_GETDEBUG:
    551		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
    552		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
    553		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
    554		break;
    555#endif /* BAYCOM_DEBUG */
    556
    557	}
    558	if (copy_to_user(data, &bi, sizeof(bi)))
    559		return -EFAULT;
    560	return 0;
    561
    562}
    563
    564/* --------------------------------------------------------------------- */
    565
    566/*
    567 * command line settable parameters
    568 */
    569static char *mode[NR_PORTS] = { "ser12*", };
    570static int iobase[NR_PORTS] = { 0x3f8, };
    571static int irq[NR_PORTS] = { 4, };
    572static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
    573
    574module_param_array(mode, charp, NULL, 0);
    575MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
    576module_param_hw_array(iobase, int, ioport, NULL, 0);
    577MODULE_PARM_DESC(iobase, "baycom io base address");
    578module_param_hw_array(irq, int, irq, NULL, 0);
    579MODULE_PARM_DESC(irq, "baycom irq number");
    580module_param_array(baud, int, NULL, 0);
    581MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
    582
    583MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
    584MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
    585MODULE_LICENSE("GPL");
    586
    587/* --------------------------------------------------------------------- */
    588
    589static int __init init_baycomserfdx(void)
    590{
    591	int i, found = 0;
    592	char set_hw = 1;
    593
    594	printk(bc_drvinfo);
    595	/*
    596	 * register net devices
    597	 */
    598	for (i = 0; i < NR_PORTS; i++) {
    599		struct net_device *dev;
    600		struct baycom_state *bc;
    601		char ifname[IFNAMSIZ];
    602
    603		sprintf(ifname, "bcsf%d", i);
    604
    605		if (!mode[i])
    606			set_hw = 0;
    607		if (!set_hw)
    608			iobase[i] = irq[i] = 0;
    609
    610		dev = hdlcdrv_register(&ser12_ops, 
    611				       sizeof(struct baycom_state),
    612				       ifname, iobase[i], irq[i], 0);
    613		if (IS_ERR(dev)) 
    614			break;
    615
    616		bc = netdev_priv(dev);
    617		if (set_hw && baycom_setmode(bc, mode[i]))
    618			set_hw = 0;
    619		bc->baud = baud[i];
    620		found++;
    621		baycom_device[i] = dev;
    622	}
    623
    624	if (!found)
    625		return -ENXIO;
    626	return 0;
    627}
    628
    629static void __exit cleanup_baycomserfdx(void)
    630{
    631	int i;
    632
    633	for(i = 0; i < NR_PORTS; i++) {
    634		struct net_device *dev = baycom_device[i];
    635		if (dev) 
    636			hdlcdrv_unregister(dev);
    637	}
    638}
    639
    640module_init(init_baycomserfdx);
    641module_exit(cleanup_baycomserfdx);
    642
    643/* --------------------------------------------------------------------- */
    644
    645#ifndef MODULE
    646
    647/*
    648 * format: baycom_ser_fdx=io,irq,mode
    649 * mode: ser#    hardware DCD
    650 *       ser#*   software DCD
    651 *       ser#+   hardware DCD, inverted signal at DCD pin
    652 * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
    653 */
    654
    655static int __init baycom_ser_fdx_setup(char *str)
    656{
    657        static unsigned nr_dev;
    658        int ints[4];
    659
    660        if (nr_dev >= NR_PORTS)
    661                return 0;
    662        str = get_options(str, 4, ints);
    663        if (ints[0] < 2)
    664                return 0;
    665        mode[nr_dev] = str;
    666        iobase[nr_dev] = ints[1];
    667        irq[nr_dev] = ints[2];
    668	if (ints[0] >= 3)
    669		baud[nr_dev] = ints[3];
    670	nr_dev++;
    671	return 1;
    672}
    673
    674__setup("baycom_ser_fdx=", baycom_ser_fdx_setup);
    675
    676#endif /* MODULE */
    677/* --------------------------------------------------------------------- */