baycom_ser_hdx.c (20330B)
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/* --------------------------------------------------------------------- */