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