File: /usr/src/linux/drivers/net/hamradio/baycom_ser_fdx.c

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