📄 wcfxs.c
字号:
wc->span.ioctl = wcfxs_ioctl; wc->span.watchdog = wcfxs_watchdog; init_waitqueue_head(&wc->span.maintq); wc->span.pvt = wc; if (zt_register(&wc->span, 0)) { printk("Unable to register span with zaptel\n"); return -1; } return 0;}static void wcfxs_post_initialize(struct wcfxs *wc){ int x; /* Finalize signalling */ for (x=0;x<wc->cards;x++) { if (wc->cardflag & (1 << x)) { if (wc->modtype[x] == MOD_TYPE_FXO) wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF; else wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM; } }}static int wcfxs_hardware_init(struct wcfxs *wc){ /* Hardware stuff */ unsigned char ver; unsigned char x,y; int failed; /* Signal Reset */ outb(0x01, wc->ioaddr + WC_CNTL); /* Check Freshmaker chip */ x=inb(wc->ioaddr + WC_CNTL); ver = __wcfxs_getcreg(wc, WC_VER); failed = 0; if (ver != 0x59) { printk("Freshmaker version: %02x\n", ver); for (x=0;x<255;x++) { /* Test registers */ if (ver >= 0x70) { __wcfxs_setcreg(wc, WC_CS, x); y = __wcfxs_getcreg(wc, WC_CS); } else { __wcfxs_setcreg(wc, WC_TEST, x); y = __wcfxs_getcreg(wc, WC_TEST); } if (x != y) { printk("%02x != %02x\n", x, y); failed++; } } if (!failed) { printk("Freshmaker passed register test\n"); } else { printk("Freshmaker failed register test\n"); return -1; } /* Go to half-duty FSYNC */ __wcfxs_setcreg(wc, WC_SYNC, 0x01); y = __wcfxs_getcreg(wc, WC_SYNC); } else { printk("No freshmaker chip\n"); } /* Reset PCI Interface chip and registers (and serial) */ outb(0x06, wc->ioaddr + WC_CNTL); /* Setup our proper outputs for when we switch for our "serial" port */ wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; outb(wc->ios, wc->ioaddr + WC_AUXD); /* Set all to outputs except AUX 5, which is an input */ outb(0xdf, wc->ioaddr + WC_AUXC); /* Select alternate function for AUX0 */ outb(0x4, wc->ioaddr + WC_AUXFUNC); /* Wait 1/4 of a sec */ wait_just_a_bit(HZ/4); /* Back to normal, with automatic DMA wrap around */ outb(0x30 | 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 */ outb(0xc1, wc->ioaddr + WC_SERCTL); /* Delay FSC by 0 so it's properly aligned */ outb(0x0, wc->ioaddr + WC_FSCDELAY); /* Setup DMA Addresses */ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ outl(wc->writedma + ZT_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ outl(wc->writedma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWE); /* End */ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ outl(wc->readdma + ZT_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ outl(wc->readdma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARE); /* End */ /* Clear interrupts */ outb(0xff, wc->ioaddr + WC_INTSTAT); /* Wait 1/4 of a second more */ wait_just_a_bit(HZ/4); for (x=0;x<wc->cards;x++) { int sane=0,ret=0,readi=0;#if 1 /* Init with Auto Calibration */ if (!(ret=wcfxs_init_proslic(wc, x, 0, 0, sane))) { wc->cardflag |= (1 << x); if (debug) { readi = wcfxs_getreg(wc,x,LOOP_I_LIMIT); printk("Proslic module %d loop current is %dmA\n",x, ((readi*3)+20)); } printk("Module %d: Installed -- AUTO FXS/DPO\n",x); } else { if(ret!=-2) { sane=1; /* Init with Manual Calibration */ if (!wcfxs_init_proslic(wc, x, 0, 1, sane)) { wc->cardflag |= (1 << x); if (debug) { readi = wcfxs_getreg(wc,x,LOOP_I_LIMIT); printk("Proslic module %d loop current is %dmA\n",x, ((readi*3)+20)); } printk("Module %d: Installed -- MANUAL FXS\n",x); } else { printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); } } else if (!(ret = wcfxs_init_voicedaa(wc, x, 0, 0, sane))) { wc->cardflag |= (1 << x); printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); } else printk("Module %d: Not installed\n", x); }#endif } /* Return error if nothing initialized okay. */ if (!wc->cardflag && !timingonly) return -1; __wcfxs_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); return 0;}static void wcfxs_enable_interrupts(struct wcfxs *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 wcfxs_restart_dma(struct wcfxs *wc){ /* Reset Master and TDM */ outb(0x01, wc->ioaddr + WC_CNTL); outb(0x01, wc->ioaddr + WC_OPER);}static void wcfxs_start_dma(struct wcfxs *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 wcfxs_stop_dma(struct wcfxs *wc){ outb(0x00, wc->ioaddr + WC_OPER);}static void wcfxs_reset_tdm(struct wcfxs *wc){ /* Reset TDM */ outb(0x0f, wc->ioaddr + WC_CNTL);}static void wcfxs_disable_interrupts(struct wcfxs *wc) { outb(0x00, wc->ioaddr + WC_MASK0); outb(0x00, wc->ioaddr + WC_MASK1);}static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ int res; struct wcfxs *wc; struct wcfxs_desc *d = (struct wcfxs_desc *)ent->driver_data; int x; static int initd_ifaces=0; if(initd_ifaces){ memset((void *)ifaces,0,(sizeof(struct wcfxs *))*WC_MAX_IFACES); initd_ifaces=1; } 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 wcfxs), GFP_KERNEL); if (wc) { ifaces[x] = wc; memset(wc, 0, sizeof(struct wcfxs)); spin_lock_init(&wc->lock); wc->curcard = -1; wc->cards = 4; 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, "wcfxs")) 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("wcfxs: Unable to allocate DMA-able memory\n"); if (wc->freeregion) release_region(wc->ioaddr, 0xff); return -ENOMEM; } wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * 2; /* in doublewords */ wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * 8; /* in bytes */ if (wcfxs_initialize(wc)) { printk("wcfxs: Unable to intialize FXS\n"); /* Set Reset Low */ x=inb(wc->ioaddr + WC_CNTL); outb((~0x1)&x, wc->ioaddr + WC_CNTL); /* Free Resources */ free_irq(pdev->irq, wc); if (wc->freeregion) release_region(wc->ioaddr, 0xff); pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); 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, wcfxs_interrupt, SA_SHIRQ, "wctdm", wc)) { printk("wcfxs: Unable to request IRQ %d\n", pdev->irq); if (wc->freeregion) release_region(wc->ioaddr, 0xff); pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); pci_set_drvdata(pdev, NULL); kfree(wc); return -EIO; } if (wcfxs_hardware_init(wc)) { unsigned char x; /* Set Reset Low */ x=inb(wc->ioaddr + WC_CNTL); outb((~0x1)&x, wc->ioaddr + WC_CNTL); /* Free Resources */ free_irq(pdev->irq, wc); if (wc->freeregion) release_region(wc->ioaddr, 0xff); pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); pci_set_drvdata(pdev, NULL); zt_unregister(&wc->span); kfree(wc); return -EIO; } wcfxs_post_initialize(wc); /* Enable interrupts */ wcfxs_enable_interrupts(wc); /* Initialize Write/Buffers to all blank data */ memset((void *)wc->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 4); /* Start DMA */ wcfxs_start_dma(wc); printk("Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->cards); res = 0; } else res = -ENOMEM; } return res;}static void wcfxs_release(struct wcfxs *wc){ zt_unregister(&wc->span); if (wc->freeregion) release_region(wc->ioaddr, 0xff); kfree(wc); printk("Freed a Wildcard\n");}static void __devexit wcfxs_remove_one(struct pci_dev *pdev){ struct wcfxs *wc = pci_get_drvdata(pdev); if (wc) { /* Stop any DMA */ wcfxs_stop_dma(wc); wcfxs_reset_tdm(wc); /* In case hardware is still there */ wcfxs_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) wcfxs_release(wc); else wc->dead = 1; }}static struct pci_device_id wcfxs_pci_tbl[] = { { 0xe159, 0x0001, 0xa159, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxs }, { 0xe159, 0x0001, 0xe159, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxs }, { 0xe159, 0x0001, 0xb100, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxse }, { 0xe159, 0x0001, 0xa9fd, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh }, { 0xe159, 0x0001, 0xa8fd, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh }, { 0xe159, 0x0001, 0xa800, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh }, { 0xe159, 0x0001, 0xa801, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh }, { 0xe159, 0x0001, 0xa908, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh }, { 0xe159, 0x0001, 0xa901, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh },#ifdef TDM_REVH_MATCHALL { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxsh },#endif { 0 }};MODULE_DEVICE_TABLE(pci, wcfxs_pci_tbl);static struct pci_driver wcfxs_driver = { name: "wcfxs", probe: wcfxs_init_one,#ifdef LINUX26 remove: __devexit_p(wcfxs_remove_one),#else remove: wcfxs_remove_one,#endif suspend: NULL, resume: NULL, id_table: wcfxs_pci_tbl,};static int __init wcfxs_init(void){ int res; int x; for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { if (!strcmp(fxo_modes[x].name, opermode)) break; } if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { _opermode = x; } else { printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++) printk(" %s\n", fxo_modes[x].name); printk("Note this option is CASE SENSITIVE!\n"); return -ENODEV; } res = pci_module_init(&wcfxs_driver); if (res) return -ENODEV; return 0;}static void __exit wcfxs_cleanup(void){ pci_unregister_driver(&wcfxs_driver);}#ifdef LINUX26module_param(debug, int, 0600);module_param(loopcurrent, int, 0600);module_param(robust, int, 0600);module_param(_opermode, int, 0600);module_param(opermode, charp, 0600);module_param(timingonly, int, 0600);module_param(lowpower, int, 0600);module_param(boostringer, int, 0600);module_param(fxshonormode, int, 0600);#elseMODULE_PARM(debug, "i");MODULE_PARM(loopcurrent, "i");MODULE_PARM(robust, "i");MODULE_PARM(_opermode, "i");MODULE_PARM(opermode, "s");MODULE_PARM(timingonly, "i");MODULE_PARM(lowpower, "i");MODULE_PARM(boostringer, "i");MODULE_PARM(fxshonormode, "i");#endifMODULE_DESCRIPTION("Wildcard TDM400P Zaptel Driver");MODULE_AUTHOR("Mark Spencer <markster@digium.com>");#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endifmodule_init(wcfxs_init);module_exit(wcfxs_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -