📄 wcfxo.c
字号:
if (wc->battdebounce) wc->battdebounce--;#ifdef JAPAN if (wc->ohdebounce) wc->ohdebounce--;#endif }#ifdef LINUX26 return IRQ_RETVAL(1);#endif }static int wcfxo_setreg(struct wcfxo *wc, unsigned char reg, unsigned char value){ int x; if (wc->wregcount < ZT_CHUNKSIZE) { x = wc->wregcount; wc->regs[x].reg = reg; wc->regs[x].value = value; wc->regs[x].flags = FLAG_WRITE; wc->wregcount++; return 0; } printk("wcfxo: Out of space to write register %02x with %02x\n", reg, value); return -1;}static int wcfxo_open(struct zt_chan *chan){ struct wcfxo *wc = chan->pvt; if (wc->dead) return -ENODEV; wc->usecount++;#ifndef LINUX26 MOD_INC_USE_COUNT;#endif return 0;}static int wcfxo_watchdog(struct zt_span *span, int event){ printk("FXO: Restarting DMA\n"); wcfxo_restart_dma(span->pvt); return 0;}static int wcfxo_close(struct zt_chan *chan){ struct wcfxo *wc = chan->pvt; wc->usecount--;#ifndef LINUX26 MOD_DEC_USE_COUNT;#endif /* If we're dead, release us now */ if (!wc->usecount && wc->dead) wcfxo_release(wc); return 0;}static int wcfxo_hooksig(struct zt_chan *chan, zt_txsig_t txsig){ struct wcfxo *wc = chan->pvt; int reg=0; switch(txsig) { case ZT_TXSIG_START: case ZT_TXSIG_OFFHOOK: /* Take off hook and enable normal mode reception. This must be done in two steps because of a hardware bug. */ reg = wc->readregs[0x5] & ~0x08; wcfxo_setreg(wc, 0x5, reg); reg = reg | 0x1; wcfxo_setreg(wc, 0x5, reg); wc->offhook = 1;#ifdef JAPAN wc->battery = 1; wc->battdebounce = BATT_DEBOUNCE; wc->ohdebounce = OH_DEBOUNCE;#endif break; case ZT_TXSIG_ONHOOK: /* Put on hook and enable on hook line monitor */ reg = wc->readregs[0x5] & 0xfe; wcfxo_setreg(wc, 0x5, reg); reg = reg | 0x08; wcfxo_setreg(wc, 0x5, reg); wc->offhook = 0; /* Don't accept a ring for another 1000 ms */ wc->ringdebounce = 1000;#ifdef JAPAN wc->ohdebounce = OH_DEBOUNCE;#endif break; default: printk("wcfxo: Can't set tx state to %d\n", txsig); } if (debug) printk("Setting hook state to %d (%02x)\n", txsig, reg); return 0;}static int wcfxo_initialize(struct wcfxo *wc){ /* Zapata stuff */ sprintf(wc->span.name, "WCFXO/%d", wc->pos); sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1); sprintf(wc->chan.name, "WCFXO/%d/%d", wc->pos, 0); wc->chan.sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF; wc->chan.chanpos = 1; wc->span.chans = &wc->chan; wc->span.channels = 1; wc->span.hooksig = wcfxo_hooksig; wc->span.open = wcfxo_open; wc->span.close = wcfxo_close; wc->span.flags = ZT_FLAG_RBS; wc->span.deflaw = ZT_LAW_MULAW; wc->span.watchdog = wcfxo_watchdog;#ifdef ENABLE_TASKLETS tasklet_init(&wc->wcfxo_tlet, wcfxo_tasklet, (unsigned long)wc);#endif init_waitqueue_head(&wc->span.maintq); wc->span.pvt = wc; wc->chan.pvt = wc; if (zt_register(&wc->span, 0)) { printk("Unable to register span with zaptel\n"); return -1; } return 0;}static int wcfxo_hardware_init(struct wcfxo *wc){ /* Hardware stuff */ /* Reset PCI Interface chip and registers */ outb(0x0e, wc->ioaddr + WC_CNTL); if (wc->flags & FLAG_RESET_ON_AUX5) { /* Set hook state to on hook for when we switch. Make sure reset is high */ outb(0x34, wc->ioaddr + WC_AUXD); } else { /* Set hook state to on hook for when we switch */ outb(0x24, wc->ioaddr + WC_AUXD); } /* Set all to outputs except AUX 4, which is an input */ outb(0xef, wc->ioaddr + WC_AUXC); /* Back to normal, with automatic DMA wrap around */ outb(0x01, wc->ioaddr + WC_CNTL); /* Make sure serial port and DMA are out of reset */ outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL); /* Configure serial port for MSB->LSB operation */ if (wc->flags & FLAG_DOUBLE_CLOCK) outb(0xc1, wc->ioaddr + WC_SERCTL); else outb(0xc0, wc->ioaddr + WC_SERCTL); if (wc->flags & FLAG_USE_XTAL) { /* Use the crystal oscillator */ outb(0x04, wc->ioaddr + WC_AUXFUNC); } /* Delay FSC by 2 so it's properly aligned */ outb(0x2, wc->ioaddr + WC_FSCDELAY); /* Setup DMA Addresses */ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ outl(wc->writedma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ outl(wc->writedma + ZT_CHUNKSIZE * 16 - 4, wc->ioaddr + WC_DMAWE); /* End */ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ outl(wc->readdma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ outl(wc->readdma + ZT_CHUNKSIZE * 16 - 4, wc->ioaddr + WC_DMARE); /* End */ /* Clear interrupts */ outb(0xff, wc->ioaddr + WC_INTSTAT); return 0;}static void wcfxo_enable_interrupts(struct wcfxo *wc){ /* Enable interrupts (we care about all of them) */ outb(0x3f, wc->ioaddr + WC_MASK0); /* No external interrupts */ outb(0x00, wc->ioaddr + WC_MASK1);}static void wcfxo_start_dma(struct wcfxo *wc){ /* Reset Master and TDM */ outb(0x0f, wc->ioaddr + WC_CNTL); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); outb(0x01, wc->ioaddr + WC_CNTL); outb(0x01, wc->ioaddr + WC_OPER);}static void wcfxo_restart_dma(struct wcfxo *wc){ /* Reset Master and TDM */ outb(0x01, wc->ioaddr + WC_CNTL); outb(0x01, wc->ioaddr + WC_OPER);}static void wcfxo_stop_dma(struct wcfxo *wc){ outb(0x00, wc->ioaddr + WC_OPER);}static void wcfxo_reset_tdm(struct wcfxo *wc){ /* Reset TDM */ outb(0x0f, wc->ioaddr + WC_CNTL);}static void wcfxo_disable_interrupts(struct wcfxo *wc) { outb(0x00, wc->ioaddr + WC_MASK0); outb(0x00, wc->ioaddr + WC_MASK1);}static int wcfxo_set_daa_mode(struct wcfxo *wc){ /* Set country specific parameters (OHS, ACT, DCT, RZ, RT, LIM, VOL) */ int reg16 = ((fxo_modes[opermode].ohs & 0x1) << 6) | ((fxo_modes[opermode].act & 0x1) << 5) | ((fxo_modes[opermode].dct & 0x3) << 2) | ((fxo_modes[opermode].rz & 0x1) << 1) | ((fxo_modes[opermode].rt & 0x1) << 0); int reg17 = ((fxo_modes[opermode].lim & 0x3) << 3); int reg18 = ((fxo_modes[opermode].vol & 0x3) << 3); wcfxo_setreg(wc, 0x16, reg16); wcfxo_setreg(wc, 0x17, reg17); wcfxo_setreg(wc, 0x18, reg18); /* Wait a couple of jiffies for our writes to finish */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1 + (ZT_CHUNKSIZE * HZ) / 800); printk("wcfxo: DAA mode is '%s'\n", fxo_modes[opermode].name); return 0;}static int wcfxo_init_daa(struct wcfxo *wc){ /* This must not be called in an interrupt */ /* We let things settle for a bit */ unsigned char reg15;// set_current_state(TASK_INTERRUPTIBLE);// schedule_timeout(10); /* Soft-reset it */ wcfxo_setreg(wc, 0x1, 0x80); /* Let the reset go */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1 + (ZT_CHUNKSIZE * HZ) / 800); /* We have a clock at 18.432 Mhz, so N1=1, M1=2, CGM=0 */ wcfxo_setreg(wc, 0x7, 0x0); /* This value is N1 - 1 */ wcfxo_setreg(wc, 0x8, 0x1); /* This value is M1 - 1 */ /* We want to sample at 8khz, so N2 = 9, M2 = 10 (N2-1, M2-1) */ wcfxo_setreg(wc, 0x9, 0x89); /* Wait until the PLL's are locked. Time is between 100 uSec and 1 mSec */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1 + HZ/1000 + (ZT_CHUNKSIZE * HZ) / 800); /* No additional ration is applied to the PLL and faster lock times * are possible */ wcfxo_setreg(wc, 0xa, 0x0); /* Enable off hook pin */ wcfxo_setreg(wc, 0x5, 0x0a); if (monitor) { /* Enable ISOcap and external speaker and charge pump if present */ wcfxo_setreg(wc, 0x6, 0x80); } else { /* Enable ISOcap and charge pump if present (leave speaker disabled) */ wcfxo_setreg(wc, 0x6, 0xe0); } /* Wait a couple of jiffies for our writes to finish */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1 + (ZT_CHUNKSIZE * HZ) / 800); reg15 = 0x0; /* Go ahead and attenuate transmit signal by 6 db */ if (quiet) { printk("wcfxo: Attenuating transmit signal for quiet operation\n"); reg15 |= (quiet & 0x3) << 4; } if (boost) { printk("wcfxo: Boosting receive signal\n"); reg15 |= (boost & 0x3); } wcfxo_setreg(wc, 0xf, reg15); /* Didn't get it right. Register 9 is still garbage */ if (wc->readregs[0x9] != 0x89) return -1;#if 0 { int x; int y; for (y=0;y<100;y++) { printk(" reg dump ====== %d ======\n", y); for (x=0;x<sizeof(wecareregs) / sizeof(wecareregs[0]);x++) { printk("daa: Reg %d: %02x\n", wecareregs[x], wc->readregs[wecareregs[x]]); } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(100); } }#endif return 0;}static int __devinit wcfxo_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ int res; struct wcfxo *wc; struct wcfxo_desc *d = (struct wcfxo_desc *)ent->driver_data; int x; for (x=0;x<WC_MAX_IFACES;x++) if (!ifaces[x]) break; if (x >= WC_MAX_IFACES) { printk("Too many interfaces\n"); return -EIO; } if (pci_enable_device(pdev)) { res = -EIO; } else { wc = kmalloc(sizeof(struct wcfxo), GFP_KERNEL); if (wc) { ifaces[x] = wc; memset(wc, 0, sizeof(struct wcfxo)); wc->ioaddr = pci_resource_start(pdev, 0); wc->dev = pdev; wc->pos = x; wc->variety = d->name; wc->flags = d->flags; /* Keep track of whether we need to free the region */ if (request_region(wc->ioaddr, 0xff, "wcfxo")) wc->freeregion = 1; /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses 32 bits. Allocate an extra set just for control too */ wc->writechunk = (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &wc->writedma); if (!wc->writechunk) { printk("wcfxo: Unable to allocate DMA-able memory\n"); if (wc->freeregion) release_region(wc->ioaddr, 0xff); return -ENOMEM; } wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * 4; /* in doublewords */ wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * 16; /* in bytes */ if (wcfxo_initialize(wc)) { printk("wcfxo: Unable to intialize modem\n"); if (wc->freeregion) release_region(wc->ioaddr, 0xff); kfree(wc); return -EIO; } /* Enable bus mastering */ pci_set_master(pdev); /* Keep track of which device we are */ pci_set_drvdata(pdev, wc); if (request_irq(pdev->irq, wcfxo_interrupt, SA_SHIRQ, "wcfxo", wc)) { printk("wcfxo: Unable to request IRQ %d\n", pdev->irq); if (wc->freeregion) release_region(wc->ioaddr, 0xff); kfree(wc); return -EIO; } wcfxo_hardware_init(wc); /* Enable interrupts */ wcfxo_enable_interrupts(wc); /* Initialize Write/Buffers to all blank data */ memset((void *)wc->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4); /* Start DMA */ wcfxo_start_dma(wc); /* Initialize DAA (after it's started) */ if (wcfxo_init_daa(wc)) { printk("Failed to initailize DAA, giving up...\n"); wcfxo_stop_dma(wc); wcfxo_disable_interrupts(wc); zt_unregister(&wc->span); free_irq(pdev->irq, wc); /* Reset PCI chip and registers */ outb(0x0e, wc->ioaddr + WC_CNTL); if (wc->freeregion) release_region(wc->ioaddr, 0xff); kfree(wc); return -EIO; } wcfxo_set_daa_mode(wc); printk("Found a Wildcard FXO: %s\n", wc->variety); res = 0; } else res = -ENOMEM; } return res;}static void wcfxo_release(struct wcfxo *wc){ zt_unregister(&wc->span); if (wc->freeregion) release_region(wc->ioaddr, 0xff); kfree(wc); printk("Freed a Wildcard\n");}static void __devexit wcfxo_remove_one(struct pci_dev *pdev){ struct wcfxo *wc = pci_get_drvdata(pdev); if (wc) { /* Stop any DMA */ wcfxo_stop_dma(wc); wcfxo_reset_tdm(wc); /* In case hardware is still there */ wcfxo_disable_interrupts(wc); /* Immediately free resources */ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); free_irq(pdev->irq, wc); /* Reset PCI chip and registers */ outb(0x0e, wc->ioaddr + WC_CNTL); /* Release span, possibly delayed */ if (!wc->usecount) wcfxo_release(wc); else wc->dead = 1; }}static struct pci_device_id wcfxo_pci_tbl[] = { { 0xe159, 0x0001, 0x8085, PCI_ANY_ID, 0, 0, (unsigned long) &wcx101p }, { 0xe159, 0x0001, 0x8086, PCI_ANY_ID, 0, 0, (unsigned long) &generic }, { 0xe159, 0x0001, 0x8087, PCI_ANY_ID, 0, 0, (unsigned long) &generic }, { 0x1057, 0x5608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcx100p }, { 0 }};MODULE_DEVICE_TABLE (pci, wcfxo_pci_tbl);static struct pci_driver wcfxo_driver = { name: "wcfxo", probe: wcfxo_init_one,#ifdef LINUX26 remove: __devexit_p(wcfxo_remove_one),#else remove: wcfxo_remove_one,#endif id_table: wcfxo_pci_tbl,};static int __init wcfxo_init(void){ int res; int x; if ((opermode >= sizeof(fxo_modes) / sizeof(fxo_modes[0])) || (opermode < 0)) { printk("Invalid/unknown operating mode specified. Please choose one of:\n"); for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++) printk("%d: %s\n", x, fxo_modes[x].name); return -ENODEV; } res = pci_module_init(&wcfxo_driver); if (res) return -ENODEV; return 0;}static void __exit wcfxo_cleanup(void){ pci_unregister_driver(&wcfxo_driver);}#ifdef LINUX26module_param(debug, int, 0600);module_param(quiet, int, 0600);module_param(boost, int, 0600);module_param(monitor, int, 0600);module_param(opermode, int, 0600);#elseMODULE_PARM(debug, "i");MODULE_PARM(quiet, "i");MODULE_PARM(boost, "i");MODULE_PARM(monitor, "i");MODULE_PARM(opermode, "i");#endifMODULE_DESCRIPTION("Wildcard X100P Zaptel Driver");MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endifmodule_init(wcfxo_init);module_exit(wcfxo_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -