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