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/* --------------------------------------------------------------------- */
728