--- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/zaphfc.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/zaphfc.c @@ -0,0 +1,1128 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * kernel module inspired by HFC PCI ISDN4Linux and Zaptel drivers + * + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include +#include +#ifdef RTAITIMING +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include "zaphfc.h" + +#include + +#if CONFIG_PCI + +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +typedef struct { + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; +} PCI_ENTRY; + +static const PCI_ENTRY id_list[] = +{ + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"}, + {0x182d, 0x3069,"Sitecom","Isdn 128 PCI"}, + {0, 0, NULL, NULL}, +}; + +static struct hfc_card *hfc_dev_list = NULL; +static int hfc_dev_count = 0; +static int modes = 0; // all TE +static int debug = 0; +static struct pci_dev *multi_hfc = NULL; +static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; + +void hfc_shutdownCard(struct hfc_card *hfctmp) { + unsigned long flags; + + if (hfctmp == NULL) { + return; + } + + if (hfctmp->pci_io == NULL) { + return; + } + + spin_lock_irqsave(&hfctmp->lock,flags); + + printk(KERN_INFO "zaphfc: shutting down card at %p.\n",hfctmp->pci_io); + + /* Clear interrupt mask */ + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Reset pending interrupts */ + hfc_inb(hfctmp, hfc_INT_S1); + + /* Wait for interrupts that might still be pending */ + spin_unlock_irqrestore(&hfctmp->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + + /* Remove interrupt handler */ + if (hfctmp->irq) { + free_irq(hfctmp->irq, hfctmp); + } + + /* Soft-reset the card */ + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on + + spin_unlock_irqrestore(&hfctmp->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + + hfc_outb(hfctmp,hfc_CIRM,0); // softreset off + + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, 0); // disable memio and bustmaster + + if (hfctmp->fifomem != NULL) { + kfree(hfctmp->fifomem); + } + iounmap((void *) hfctmp->pci_io); + hfctmp->pci_io = NULL; + if (hfctmp->pcidev != NULL) { + pci_disable_device(hfctmp->pcidev); + } + spin_unlock_irqrestore(&hfctmp->lock,flags); + if (hfctmp->ztdev != NULL) { + dahdi_unregister(&hfctmp->ztdev->span); + kfree(hfctmp->ztdev); + printk(KERN_INFO "unregistered from DAHDI.\n"); + } +} + +void hfc_resetCard(struct hfc_card *hfctmp) { + unsigned long flags; + + spin_lock_irqsave(&hfctmp->lock,flags); + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY); // enable memio + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + +// printk(KERN_INFO "zaphfc: resetting card.\n"); + pci_set_master(hfctmp->pcidev); + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on + spin_unlock_irqrestore(&hfctmp->lock, flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + hfc_outb(hfctmp, hfc_CIRM, 0); // softreset off + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); // wait 20 ms + if (hfc_inb(hfctmp,hfc_STATUS) & hfc_STATUS_PCI_PROC) { + printk(KERN_WARNING "zaphfc: hfc busy.\n"); + } + +// hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; +// hfctmp->regs.fifo_en = hfc_FIFOEN_D; /* only D fifos enabled */ + hfctmp->regs.fifo_en = 0; /* no fifos enabled */ + hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + + hfctmp->regs.trm = 2; + hfc_outb(hfctmp, hfc_TRM, hfctmp->regs.trm); + + if (hfctmp->regs.nt_mode == 1) { + hfc_outb(hfctmp, hfc_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */ + } else { + hfc_outb(hfctmp, hfc_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */ + } + hfctmp->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; + hfc_outb(hfctmp, hfc_SCTRL_E, hfctmp->regs.sctrl_e); /* S/T Auto awake */ + hfctmp->regs.bswapped = 0; /* no exchange */ + + hfctmp->regs.ctmt = hfc_CTMT_TRANSB1 | hfc_CTMT_TRANSB2; // all bchans are transparent , no freaking hdlc + hfc_outb(hfctmp, hfc_CTMT, hfctmp->regs.ctmt); + + hfctmp->regs.int_m1 = 0; + hfc_outb(hfctmp, hfc_INT_M1, hfctmp->regs.int_m1); + +#ifdef RTAITIMING + hfctmp->regs.int_m2 = 0; +#else + hfctmp->regs.int_m2 = hfc_M2_PROC_TRANS; +#endif + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Clear already pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); + + if (hfctmp->regs.nt_mode == 1) { + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_NT; /* set tx_lo mode, error in datasheet ! */ + } else { + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_TE; /* set tx_lo mode, error in datasheet ! */ + } + + hfctmp->regs.mst_mode = hfc_MST_MODE_MASTER; /* HFC Master Mode */ + hfc_outb(hfctmp, hfc_MST_MODE, hfctmp->regs.mst_mode); + + hfc_outb(hfctmp, hfc_SCTRL, hfctmp->regs.sctrl); + hfctmp->regs.sctrl_r = 3; + hfc_outb(hfctmp, hfc_SCTRL_R, hfctmp->regs.sctrl_r); + + hfctmp->regs.connect = 0; + hfc_outb(hfctmp, hfc_CONNECT, hfctmp->regs.connect); + + hfc_outb(hfctmp, hfc_CIRM, 0x80 | 0x40); // bit order + + /* Finally enable IRQ output */ +#ifndef RTAITIMING + hfctmp->regs.int_m2 |= hfc_M2_IRQ_ENABLE; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); +#endif + + /* clear pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); + hfc_inb(hfctmp, hfc_INT_S2); +} + +void hfc_registerCard(struct hfc_card *hfccard) { + spin_lock(®isterlock); + if (hfccard != NULL) { + hfccard->cardno = hfc_dev_count++; + hfccard->next = hfc_dev_list; + hfc_dev_list = hfccard; + } + spin_unlock(®isterlock); +} + +static void hfc_btrans(struct hfc_card *hfctmp, char whichB) { + // we are called with irqs disabled from the irq handler + int count, maxlen, total; + unsigned char *f1, *f2; + unsigned short *z1, *z2, newz1; + int freebytes; + + if (whichB == 1) { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z2 + (*f1 * 4)); + } else { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z2 + (*f1 * 4)); + } + + freebytes = *z2 - *z1; + if (freebytes <= 0) { + freebytes += hfc_B_FIFO_SIZE; + } + count = DAHDI_CHUNKSIZE; + + total = count; + if (freebytes < count) { + hfctmp->clicks++; + /* only spit out this warning once per second to not make things worse! */ + if (hfctmp->clicks > 100) { + printk(KERN_CRIT "zaphfc: bchan tx fifo full, dropping audio! (z1=%d, z2=%d)\n",*z1,*z2); + hfctmp->clicks = 0; + } + return; + } + + maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z1; + if (maxlen > count) { + maxlen = count; + } + newz1 = *z1 + total; + if (newz1 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { newz1 -= hfc_B_FIFO_SIZE; } + + if (whichB == 1) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + *z1),hfctmp->ztdev->chans[0].writechunk, maxlen); + } else { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + *z1),hfctmp->ztdev->chans[1].writechunk, maxlen); + } + + count -= maxlen; + if (count > 0) { + // Buffer wrap + if (whichB == 1) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[0].writechunk+maxlen, count); + } else { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[1].writechunk+maxlen, count); + } + } + + *z1 = newz1; /* send it now */ + +// if (count > 0) printk(KERN_CRIT "zaphfc: bchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); + return; +} + +static void hfc_brec(struct hfc_card *hfctmp, char whichB) { + // we are called with irqs disabled from the irq handler + int count, maxlen, drop; + volatile unsigned char *f1, *f2; + volatile unsigned short *z1, *z2, newz2; + int bytes = 0; + + if (whichB == 1) { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); + } else { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); + } + + bytes = *z1 - *z2; + if (bytes < 0) { + bytes += hfc_B_FIFO_SIZE; + } + count = DAHDI_CHUNKSIZE; + + if (bytes < DAHDI_CHUNKSIZE) { +#ifndef RTAITIMING + printk(KERN_CRIT "zaphfc: bchan rx fifo not enough bytes to receive! (z1=%d, z2=%d, wanted %d got %d), probably a buffer overrun.\n",*z1,*z2,DAHDI_CHUNKSIZE,bytes); +#endif + return; + } + + /* allowing the buffering of hfc_BCHAN_BUFFER bytes of audio data works around irq jitter */ + if (bytes > hfc_BCHAN_BUFFER + DAHDI_CHUNKSIZE) { + /* if the system is too slow to handle it, we will have to drop it all (except 1 DAHDI chunk) */ + drop = bytes - DAHDI_CHUNKSIZE; + hfctmp->clicks++; + /* only spit out this warning once per second to not make things worse! */ + if (hfctmp->clicks > 100) { + printk(KERN_CRIT "zaphfc: dropped audio (z1=%d, z2=%d, wanted %d got %d, dropped %d).\n",*z1,*z2,count,bytes,drop); + hfctmp->clicks = 0; + } + /* hm, we are processing the b chan data tooooo slowly... let's drop the lost audio */ + newz2 = *z2 + drop; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + *z2 = newz2; + } + + + maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z2; + if (maxlen > count) { + maxlen = count; + } + if (whichB == 1) { + memcpy(hfctmp->ztdev->chans[0].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + *z2), maxlen); + } else { + memcpy(hfctmp->ztdev->chans[1].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + *z2), maxlen); + } + newz2 = *z2 + count; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + *z2 = newz2; + + count -= maxlen; + if (count > 0) { + // Buffer wrap + if (whichB == 1) { + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); + memcpy(hfctmp->ztdev->chans[0].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + hfc_B_SUB_VAL), count); + } else { + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); + memcpy(hfctmp->ztdev->chans[1].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + hfc_B_SUB_VAL), count); + } + newz2 = *z2 + count; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + } + + + if (whichB == 1) { + dahdi_ec_chunk(&hfctmp->ztdev->chans[0], hfctmp->ztdev->chans[0].readchunk, hfctmp->ztdev->chans[0].writechunk); + } else { + dahdi_ec_chunk(&hfctmp->ztdev->chans[1], hfctmp->ztdev->chans[1].readchunk, hfctmp->ztdev->chans[1].writechunk); + } + return; +} + + +static void hfc_dtrans(struct hfc_card *hfctmp) { + // we are called with irqs disabled from the irq handler + int x; + int count, maxlen, total; + unsigned char *f1, *f2, newf1; + unsigned short *z1, *z2, newz1; + int frames, freebytes; + + if (hfctmp->ztdev->chans[2].bytes2transmit == 0) { + return; + } + + f1 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z2 + (*f1 * 4)); + + frames = (*f1 - *f2) & hfc_FMASK; + if (frames < 0) { + frames += hfc_MAX_DFRAMES + 1; + } + + if (frames >= hfc_MAX_DFRAMES) { + printk(KERN_CRIT "zaphfc: dchan tx fifo total number of frames exceeded!\n"); + return; + } + + freebytes = *z2 - *z1; + if (freebytes <= 0) { + freebytes += hfc_D_FIFO_SIZE; + } + count = hfctmp->ztdev->chans[2].bytes2transmit; + + total = count; + if (freebytes < count) { + printk(KERN_CRIT "zaphfc: dchan tx fifo not enough free bytes! (z1=%d, z2=%d)\n",*z1,*z2); + return; + } + + newz1 = (*z1 + count) & hfc_ZMASK; + newf1 = ((*f1 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); // next frame + + if (count > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d TX [ ", hfctmp->cardno); + for (x=0; xdtransbuf[x]); + } + if (hfctmp->ztdev->chans[2].eoftx == 1) { + printk("] %d bytes\n", count); + } else { + printk("..] %d bytes\n", count); + } + } + maxlen = hfc_D_FIFO_SIZE - *z1; + if (maxlen > count) { + maxlen = count; + } + memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF + *z1),hfctmp->ztdev->chans[2].writechunk, maxlen); + count -= maxlen; + if (count > 0) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF),(char *)(hfctmp->ztdev->chans[2].writechunk + maxlen), count); + } + } + + *z1 = newz1; + + if (hfctmp->ztdev->chans[2].eoftx == 1) { + *f1 = newf1; + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); + *z1 = newz1; + hfctmp->ztdev->chans[2].eoftx = 0; + } +// printk(KERN_CRIT "zaphfc: dchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); + return; +} + +/* receive a complete hdlc frame, skip broken or short frames */ +static void hfc_drec(struct hfc_card *hfctmp) { + int count=0, maxlen=0, framelen=0; + unsigned char *f1, *f2, *crcstat; + unsigned short *z1, *z2, oldz2, newz2; + + hfctmp->ztdev->chans[2].bytes2receive=0; + hfctmp->ztdev->chans[2].eofrx = 0; + + /* put the received data into the DAHDI buffer + we'll call dahdi_receive() later when the timer fires. */ + f1 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F2); + + if (*f1 == *f2) return; /* nothing received, strange eh? */ + + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z1 + (*f2 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + + /* calculate length of frame, including 2 bytes CRC and 1 byte STAT */ + count = *z1 - *z2; + + if (count < 0) { + count += hfc_D_FIFO_SIZE; /* ring buffer wrapped */ + } + count++; + framelen = count; + + crcstat = (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z1); + + if ((framelen < 4) || (*crcstat != 0x0)) { + /* the frame is too short for a valid HDLC frame or the CRC is borked */ + printk(KERN_CRIT "zaphfc: empty HDLC frame or bad CRC received (framelen = %d, stat = %#x, card = %d).\n", framelen, *crcstat, hfctmp->cardno); + oldz2 = *z2; + *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ + // recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + *z2 = (oldz2 + framelen) & hfc_ZMASK; + hfctmp->drecinframe = 0; + hfctmp->regs.int_drec--; + /* skip short or broken frames */ + hfctmp->ztdev->chans[2].bytes2receive = 0; + return; + } + + count -= 1; /* strip STAT */ + hfctmp->ztdev->chans[2].eofrx = 1; + + if (count + *z2 <= hfc_D_FIFO_SIZE) { + maxlen = count; + } else { + maxlen = hfc_D_FIFO_SIZE - *z2; + } + + /* copy first part */ + memcpy(hfctmp->drecbuf, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z2), maxlen); + hfctmp->ztdev->chans[2].bytes2receive += maxlen; + + count -= maxlen; + if (count > 0) { + /* ring buffer wrapped, copy rest from start of d fifo */ + memcpy(hfctmp->drecbuf + maxlen, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF), count); + hfctmp->ztdev->chans[2].bytes2receive += count; + } + + /* frame read */ + oldz2 = *z2; + newz2 = (oldz2 + framelen) & hfc_ZMASK; + *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ + /* recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! */ + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + *z2 = newz2; + hfctmp->drecinframe = 0; + hfctmp->regs.int_drec--; +} + +#ifndef RTAITIMING +DAHDI_IRQ_HANDLER(hfc_interrupt) { + struct hfc_card *hfctmp = dev_id; + unsigned long flags = 0; + unsigned char stat; +#else +static void hfc_service(struct hfc_card *hfctmp) { +#endif + struct dahdi_hfc *zthfc; + unsigned char s1, s2, l1state; + int x; + + if (!hfctmp) { +#ifndef RTAITIMING + return IRQ_NONE; +#else + /* rtai */ + return; +#endif + } + + if (!hfctmp->pci_io) { + printk(KERN_WARNING "%s: IO-mem disabled, cannot handle interrupt\n", + __FUNCTION__); +#ifndef RTAITIMING + return IRQ_NONE; +#else + /* rtai */ + return; +#endif + } + + /* we assume a few things in this irq handler: + - the hfc-pci will only generate "timer" irqs (proc/non-proc) + - we need to use every 8th IRQ (to generate 1khz timing) + OR + - if we use rtai for timing the hfc-pci will not generate ANY irq, + instead rtai will call this "fake" irq with a 1khz realtime timer. :) + - rtai will directly service the card, not like it used to by triggering + the linux irq + */ + +#ifndef RTAITIMING + spin_lock_irqsave(&hfctmp->lock, flags); + stat = hfc_inb(hfctmp, hfc_STATUS); + + if ((stat & hfc_STATUS_ANYINT) == 0) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&hfctmp->lock,flags); + return IRQ_NONE; + } +#endif + + s1 = hfc_inb(hfctmp, hfc_INT_S1); + s2 = hfc_inb(hfctmp, hfc_INT_S2); + if (s1 != 0) { + if (s1 & hfc_INTS_TIMER) { + // timer (bit 7) + // printk(KERN_CRIT "timer %d %d %d.\n", stat, s1, s2); + } + if (s1 & hfc_INTS_L1STATE) { + // state machine (bit 6) + // printk(KERN_CRIT "zaphfc: layer 1 state machine interrupt\n"); + zthfc = hfctmp->ztdev; + l1state = hfc_inb(hfctmp,hfc_STATES) & hfc_STATES_STATE_MASK; + if (hfctmp->regs.nt_mode == 1) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d layer 1 state = G%d\n", hfctmp->cardno, l1state); + } + switch (l1state) { + case 3: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d)", hfctmp->cardno, l1state); +#endif + break; + default: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d)", hfctmp->cardno, l1state); +#endif + } + if (l1state == 2) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_ACTIVATE | hfc_STATES_DO_ACTION | hfc_STATES_NT_G2_G3); + } else if (l1state == 3) { + // fix to G3 state (see specs) + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_LOAD_STATE | 3); + } + } else { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d layer 1 state = F%d\n", hfctmp->cardno, l1state); + } + switch (l1state) { + case 7: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d)", hfctmp->cardno, l1state); +#endif + break; + default: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d)", hfctmp->cardno, l1state); +#endif + } + if (l1state == 3) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); + } + } + + } + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) + hfctmp->regs.int_drec++; + // mr. zapata there is something for you! + // printk(KERN_CRIT "d chan rx\n"); + } + if (s1 & hfc_INTS_B2REC) { + // B2 chan RX (bit 4) + } + if (s1 & hfc_INTS_B1REC) { + // B1 chan RX (bit 3) + } + if (s1 & hfc_INTS_DTRANS) { + // D chan TX (bit 2) +// printk(KERN_CRIT "zaphfc: dchan frame transmitted.\n"); + } + if (s1 & hfc_INTS_B2TRANS) { + // B2 chan TX (bit 1) + } + if (s1 & hfc_INTS_B1TRANS) { + // B1 chan TX (bit 0) + } + } +#ifdef RTAITIMING + /* fake an irq */ + s2 |= hfc_M2_PROC_TRANS; +#endif + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) + printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); + } + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) + } + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } + if (s2 & hfc_M2_PROC_TRANS) { + // processing/non-processing transition (bit 0) + hfctmp->ticks++; +#ifndef RTAITIMING + if (hfctmp->ticks > 7) { + // welcome to DAHDI timing :) +#endif + hfctmp->ticks = 0; + + if (hfctmp->ztdev->span.flags & DAHDI_FLAG_RUNNING) { + // clear dchan buffer + hfctmp->ztdev->chans[2].bytes2transmit = 0; + hfctmp->ztdev->chans[2].maxbytes2transmit = hfc_D_FIFO_SIZE; + + dahdi_transmit(&(hfctmp->ztdev->span)); + + hfc_btrans(hfctmp,1); + hfc_btrans(hfctmp,2); + hfc_dtrans(hfctmp); + } + + hfc_brec(hfctmp,1); + hfc_brec(hfctmp,2); + if (hfctmp->regs.int_drec > 0) { + // dchan data to read + hfc_drec(hfctmp); + if (hfctmp->ztdev->chans[2].bytes2receive > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d RX [ ", hfctmp->cardno); + if (hfctmp->ztdev->chans[2].eofrx) { + /* dont output CRC == less user confusion */ + for (x=0; x < hfctmp->ztdev->chans[2].bytes2receive - 2; x++) { + printk("%#2x ", hfctmp->drecbuf[x]); + } + printk("] %d bytes\n", hfctmp->ztdev->chans[2].bytes2receive - 2); + } else { + for (x=0; x < hfctmp->ztdev->chans[2].bytes2receive; x++) { + printk("%#2x ", hfctmp->drecbuf[x]); + } + printk("..] %d bytes\n", hfctmp->ztdev->chans[2].bytes2receive); + } + } + } + } else { + // hmm....ok, let DAHDI receive nothing + hfctmp->ztdev->chans[2].bytes2receive = 0; + } + if (hfctmp->ztdev->span.flags & DAHDI_FLAG_RUNNING) { + dahdi_receive(&(hfctmp->ztdev->span)); + } + +#ifndef RTAITIMING + } +#endif + } + + } +#ifndef RTAITIMING + spin_unlock_irqrestore(&hfctmp->lock,flags); + return IRQ_RETVAL(1); +#endif +} + + +static int zthfc_open(struct dahdi_chan *chan) { + struct dahdi_hfc *zthfc = chan->pvt; + struct hfc_card *hfctmp = zthfc->card; + + if (!hfctmp) { + return 0; + } + try_module_get(THIS_MODULE); + return 0; +} + +static int zthfc_close(struct dahdi_chan *chan) { + struct dahdi_hfc *zthfc = chan->pvt; + struct hfc_card *hfctmp = zthfc->card; + + if (!hfctmp) { + return 0; + } + + module_put(THIS_MODULE); + return 0; +} + +static int zthfc_rbsbits(struct dahdi_chan *chan, int bits) { + return 0; +} + +static int zthfc_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int zthfc_startup(struct dahdi_span *span) { + struct dahdi_hfc *zthfc = span->pvt; + struct hfc_card *hfctmp = zthfc->card; + int alreadyrunning; + + if (hfctmp == NULL) { + printk(KERN_INFO "zaphfc: no card for span at startup!\n"); + } + alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; + + if (!alreadyrunning) { + span->chans[2]->flags &= ~DAHDI_FLAG_HDLC; + span->chans[2]->flags |= DAHDI_FLAG_BRIDCHAN; + + span->flags |= DAHDI_FLAG_RUNNING; + + hfctmp->ticks = -2; + hfctmp->clicks = 0; + hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; + hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + } else { + return 0; + } + + // drivers, start engines! + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); + return 0; +} + +static int zthfc_shutdown(struct dahdi_span *span) { + return 0; +} + +static int zthfc_maint(struct dahdi_span *span, int cmd) { + return 0; +} + +static int zthfc_chanconfig(struct dahdi_chan *chan, int sigtype) { +// printk(KERN_CRIT "chan_config sigtype=%d\n", sigtype); + return 0; +} + +static int zthfc_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc) { + span->lineconfig = lc->lineconfig; + return 0; +} + +static int zthfc_initialize(struct dahdi_hfc *zthfc) { + struct hfc_card *hfctmp = zthfc->card; + int i; + + memset(&zthfc->span, 0x0, sizeof(struct dahdi_span)); // you never can tell... + + sprintf(zthfc->span.name, "ZTHFC%d", hfc_dev_count + 1); + if (hfctmp->regs.nt_mode == 1) { +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] [realtime]", hfc_dev_count + 1); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT]", hfc_dev_count + 1); +#endif + } else { +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] [realtime]", hfc_dev_count + 1); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE]", hfc_dev_count + 1); +#endif + } + + zthfc->span.spanconfig = zthfc_spanconfig; + zthfc->span.chanconfig = zthfc_chanconfig; + zthfc->span.startup = zthfc_startup; + zthfc->span.shutdown = zthfc_shutdown; + zthfc->span.maint = zthfc_maint; + zthfc->span.rbsbits = zthfc_rbsbits; + zthfc->span.open = zthfc_open; + zthfc->span.close = zthfc_close; + zthfc->span.ioctl = zthfc_ioctl; + + zthfc->span.channels = 3; + zthfc->span.chans = zthfc->_chans; + for (i = 0; i < zthfc->span.channels; i++) + zthfc->_chans[i] = &zthfc->chans[i]; + zthfc->span.deflaw = DAHDI_LAW_ALAW; + zthfc->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; // <--- this is really BS + zthfc->span.offset = 0; + init_waitqueue_head(&zthfc->span.maintq); + zthfc->span.pvt = zthfc; + + for (i = 0; i < zthfc->span.channels; i++) { + memset(&(zthfc->chans[i]), 0x0, sizeof(struct dahdi_chan)); + sprintf(zthfc->chans[i].name, "ZTHFC%d/%d/%d", hfc_dev_count + 1,0,i + 1); + zthfc->chans[i].pvt = zthfc; + zthfc->chans[i].sigcap = DAHDI_SIG_EM | DAHDI_SIG_CLEAR | DAHDI_SIG_FXSLS | DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_SF; + zthfc->chans[i].chanpos = i + 1; + } + + if (dahdi_register(&zthfc->span,0)) { + printk(KERN_CRIT "unable to register DAHDI device!\n"); + return -1; + } +// printk(KERN_CRIT "zaphfc: registered DAHDI device!\n"); + return 0; +} + +#ifdef RTAITIMING +#define TICK_PERIOD 1000000 +#define TICK_PERIOD2 1000000000 +#define TASK_PRIORITY 1 +#define STACK_SIZE 10000 + +static RT_TASK rt_task; +static struct hfc_card *rtai_hfc_list[hfc_MAX_CARDS]; +static unsigned char rtai_hfc_counter = 0; + +static void rtai_register_hfc(struct hfc_card *hfctmp) { + rtai_hfc_list[rtai_hfc_counter++] = hfctmp; +} + +static void rtai_loop(int t) { + int i=0; + for (;;) { + for (i=0; i < rtai_hfc_counter; i++) { + if (rtai_hfc_list[i] != NULL) + hfc_service(rtai_hfc_list[i]); + } + rt_task_wait_period(); + } +} +#endif + +int hfc_findCards(int pcivendor, int pcidevice, char *vendor_name, char *card_name) { + struct pci_dev *tmp; + struct hfc_card *hfctmp = NULL; + struct dahdi_hfc *zthfc = NULL; + + tmp = pci_get_device(pcivendor, pcidevice, multi_hfc); + while (tmp != NULL) { + multi_hfc = tmp; // skip this next time. + + if (pci_enable_device(tmp)) { + multi_hfc = NULL; + return -1; + } + pci_set_master(tmp); + + hfctmp = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); + if (!hfctmp) { + printk(KERN_WARNING "zaphfc: unable to kmalloc!\n"); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } + memset(hfctmp, 0x0, sizeof(struct hfc_card)); + spin_lock_init(&hfctmp->lock); + + hfctmp->pcidev = tmp; + hfctmp->pcibus = tmp->bus->number; + hfctmp->pcidevfn = tmp->devfn; + + if (!tmp->irq) { + printk(KERN_WARNING "zaphfc: no irq!\n"); + } else { + hfctmp->irq = tmp->irq; + } + + hfctmp->pci_io = (char *) tmp->resource[1].start; + if (!hfctmp->pci_io) { + printk(KERN_WARNING "zaphfc: no iomem!\n"); + kfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -1; + } + + hfctmp->fifomem = kmalloc(65536, GFP_KERNEL); + if (!hfctmp->fifomem) { + printk(KERN_WARNING "zaphfc: unable to kmalloc fifomem!\n"); + kfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } else { + memset(hfctmp->fifomem, 0x0, 65536); + hfctmp->fifos = (void *)(((ulong) hfctmp->fifomem) & ~0x7FFF) + 0x8000; + pci_write_config_dword(hfctmp->pcidev, 0x80, (u_int) virt_to_bus(hfctmp->fifos)); + hfctmp->pci_io = ioremap((ulong) hfctmp->pci_io, 256); + } + +#ifdef RTAITIMING + /* we need no stinking irq */ + hfctmp->irq = 0; +#else + if (request_irq(hfctmp->irq, &hfc_interrupt, DAHDI_IRQ_SHARED, "zaphfc", hfctmp)) { + printk(KERN_WARNING "zaphfc: unable to register irq\n"); + kfree(hfctmp->fifomem); + kfree(hfctmp); + iounmap((void *) hfctmp->pci_io); + pci_disable_device(tmp); + multi_hfc = NULL; + return -EIO; + } +#endif + +#ifdef RTAITIMING + rtai_register_hfc(hfctmp); +#endif + printk(KERN_INFO + "zaphfc: %s %s configured at mem %lx fifo %lx(%#x) IRQ %d HZ %d\n", + vendor_name, card_name, + (unsigned long) hfctmp->pci_io, + (unsigned long) hfctmp->fifos, + (u_int) virt_to_bus(hfctmp->fifos), + hfctmp->irq, HZ); + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY); // enable memio + hfctmp->regs.int_m1 = 0; // no ints + hfctmp->regs.int_m2 = 0; // not at all + hfc_outb(hfctmp,hfc_INT_M1,hfctmp->regs.int_m1); + hfc_outb(hfctmp,hfc_INT_M2,hfctmp->regs.int_m2); + + if ((modes & (1 << hfc_dev_count)) != 0) { + printk(KERN_INFO "zaphfc: Card %d configured for NT mode\n",hfc_dev_count); + hfctmp->regs.nt_mode = 1; + } else { + printk(KERN_INFO "zaphfc: Card %d configured for TE mode\n",hfc_dev_count); + hfctmp->regs.nt_mode = 0; + } + + zthfc = kmalloc(sizeof(struct dahdi_hfc),GFP_KERNEL); + if (!zthfc) { + printk(KERN_CRIT "zaphfc: unable to kmalloc!\n"); + hfc_shutdownCard(hfctmp); + kfree(hfctmp); + multi_hfc = NULL; + return -ENOMEM; + } + memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); + + zthfc->card = hfctmp; + zthfc_initialize(zthfc); + hfctmp->ztdev = zthfc; + + memset(hfctmp->drecbuf, 0x0, sizeof(hfctmp->drecbuf)); + hfctmp->ztdev->chans[2].readchunk = hfctmp->drecbuf; + + memset(hfctmp->dtransbuf, 0x0, sizeof(hfctmp->dtransbuf)); + hfctmp->ztdev->chans[2].writechunk = hfctmp->dtransbuf; + + memset(hfctmp->brecbuf[0], 0x0, sizeof(hfctmp->brecbuf[0])); + hfctmp->ztdev->chans[0].readchunk = hfctmp->brecbuf[0]; + memset(hfctmp->btransbuf[0], 0x0, sizeof(hfctmp->btransbuf[0])); + hfctmp->ztdev->chans[0].writechunk = hfctmp->btransbuf[0]; + + memset(hfctmp->brecbuf[1], 0x0, sizeof(hfctmp->brecbuf[1])); + hfctmp->ztdev->chans[1].readchunk = hfctmp->brecbuf[1]; + memset(hfctmp->btransbuf[1], 0x0, sizeof(hfctmp->btransbuf[1])); + hfctmp->ztdev->chans[1].writechunk = hfctmp->btransbuf[1]; + + + hfc_registerCard(hfctmp); + hfc_resetCard(hfctmp); + tmp = pci_get_device(pcivendor, pcidevice, multi_hfc); + } + return 0; +} + + + +int init_module(void) { + int i = 0; +#ifdef RTAITIMING + RTIME tick_period; + for (i=0; i < hfc_MAX_CARDS; i++) { + rtai_hfc_list[i] = NULL; + } + rt_set_periodic_mode(); +#endif + i = 0; + while (id_list[i].vendor_id) { + multi_hfc = NULL; + hfc_findCards(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_name, id_list[i].card_name); + i++; + } +#ifdef RTAITIMING + for (i=0; i < hfc_MAX_CARDS; i++) { + if (rtai_hfc_list[i]) { + printk(KERN_INFO + "zaphfc: configured %d at mem %#x fifo %#x(%#x) for realtime servicing\n", + rtai_hfc_list[i]->cardno, + (u_int) rtai_hfc_list[i]->pci_io, + (u_int) rtai_hfc_list[i]->fifos, + (u_int) virt_to_bus(rtai_hfc_list[i]->fifos)); + + } + } + rt_task_init(&rt_task, rtai_loop, 1, STACK_SIZE, TASK_PRIORITY, 0, 0); + tick_period = start_rt_timer(nano2count(TICK_PERIOD)); + rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); +#endif + printk(KERN_INFO "zaphfc: %d hfc-pci card(s) in this box.\n", hfc_dev_count); + return 0; +} + +void cleanup_module(void) { + struct hfc_card *tmpcard; +#ifdef RTAITIMING + stop_rt_timer(); + rt_task_delete(&rt_task); +#endif + printk(KERN_INFO "zaphfc: stop\n"); +// spin_lock(®isterlock); + while (hfc_dev_list != NULL) { + if (hfc_dev_list == NULL) break; + hfc_shutdownCard(hfc_dev_list); + tmpcard = hfc_dev_list; + hfc_dev_list = hfc_dev_list->next; + if (tmpcard != NULL) { + kfree(tmpcard); + tmpcard = NULL; + printk(KERN_INFO "zaphfc: freed one card.\n"); + } + } +// spin_unlock(®isterlock); +} +#endif + + +module_param(modes, int, 0600); +module_param(debug, int, 0600); + +MODULE_DESCRIPTION("HFC-S PCI A Zaptel Driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/opvxa1200.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/opvxa1200.c @@ -0,0 +1,3009 @@ +/* + * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface + * + * Modify from wctdm.c by MiaoLin + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Rev histroy + * + * Rev 0.10 initial version + * Rev 0.11 + * fixed the led light on/off bug. + * modify some wctdm print to opvxa1200 + * support firmware version 1.2, faster i/o operation, and better LED control. + * + * Rev 0.12 patched to support new pci id 0x8519 + * Rev 0.13 patched to remove the warning during compile under kernel 2.6.22 + * Rev 0.14 patched to remove the bug for ZAP_IRQ_SHARED , 3/9/2007 + * Rev 0.15 patched to support new pci ID 0X9532 by james.zhu, 23/10/2007 + * Rev 0.16 support new pci id 0x9559 by Miao Lin 21/3/2008 + * Rev 0.17 + * patched a few bugs, + * add hwgain support. + * fixed A800P version check + * Rev 1.4.9.2 + * Only generate 8 channels for A800P + * Version number synced to zaptel distribution. + * Rev 1.4.9.2.a + * Fixed freeregion. + * + * Rev 1.4.9.2.b + * Add cid before first ring support. + * New Paremeters: + * cidbeforering : set to 1 will cause the card enable cidbeforering function. default 0 + * cidbuflen : length of cid buffer, in msec, default 3000 msec. + * cidtimeout : time out of a ring, default 6000msec + * User must set cidstart=polarity in zapata.conf to use with this feature + * cidsignalling = signalling format send before 1st ring. most likely dtmf. + * + * Rev 1.4.9.2.c + * add driver parameter cidtimeout. + * + * Rev 1.4.9.2.d + * add debug stuff to test fxs power alarm + * + * Rev 1.4.11 + * Support enhanced full scale tx/rx for FXO required by europe standard (Register 30, acim) (module parm fxofullscale) + * + * Rev 1.4.12 2008/10/17 + * Fixed bug cause FXS module report fake power alarm. + * Power alarm debug stuff removed. + * + * Rev 2.0 DAHDI 2008/10/17 + * + * Rev 2.0.1 add new pci id 0x9599 + * Re 2.0.2 12/01/2009 + add fixedtimepolarity: set time(ms) when send polarity after 1st ring happen. + * Sometimes the dtmf cid is sent just after first ring off, and the system do not have + * enough time to start detect 1st dtmf. + * 0 means send polarity at the end of 1st ring. + * x means send ploarity after x ms of 1st ring begin. + * + * Rev 2.0.3 12/01/2009 + * Add touch_softlockup_watchdog() in wctdm_hardware_init, to avoid cpu softlockup system message for FXS. + * + * + * Rev 1.4.12.4 17/04/2009 James.zhu + * Changed wctdm_voicedaa_check_hook() to detect FXO battery and solved the problem with dial(dahdi/go/XXXXXXXXXX) + * add alarm detection for FXO + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proslic.h" + +/* MiaoLin debug start */ +#include +#include /* get_fs(), set_fs(), KERNEL_DS */ +#include /* fput() */ +/* MiaoLin debug end */ + + +/* + * Define for audio vs. register based ring detection + * + */ +/* #define AUDIO_RINGCHECK */ + +/* + Experimental max loop current limit for the proslic + Loop current limit is from 20 mA to 41 mA in steps of 3 + (according to datasheet) + So set the value below to: + 0x00 : 20mA (default) + 0x01 : 23mA + 0x02 : 26mA + 0x03 : 29mA + 0x04 : 32mA + 0x05 : 35mA + 0x06 : 37mA + 0x07 : 41mA +*/ +static int loopcurrent = 20; + +static int reversepolarity = 0; + +static alpha indirect_regs[] = +{ +{0,255,"DTMF_ROW_0_PEAK",0x55C2}, +{1,255,"DTMF_ROW_1_PEAK",0x51E6}, +{2,255,"DTMF_ROW2_PEAK",0x4B85}, +{3,255,"DTMF_ROW3_PEAK",0x4937}, +{4,255,"DTMF_COL1_PEAK",0x3333}, +{5,255,"DTMF_FWD_TWIST",0x0202}, +{6,255,"DTMF_RVS_TWIST",0x0202}, +{7,255,"DTMF_ROW_RATIO_TRES",0x0198}, +{8,255,"DTMF_COL_RATIO_TRES",0x0198}, +{9,255,"DTMF_ROW_2ND_ARM",0x0611}, +{10,255,"DTMF_COL_2ND_ARM",0x0202}, +{11,255,"DTMF_PWR_MIN_TRES",0x00E5}, +{12,255,"DTMF_OT_LIM_TRES",0x0A1C}, +{13,0,"OSC1_COEF",0x7B30}, +{14,1,"OSC1X",0x0063}, +{15,2,"OSC1Y",0x0000}, +{16,3,"OSC2_COEF",0x7870}, +{17,4,"OSC2X",0x007D}, +{18,5,"OSC2Y",0x0000}, +{19,6,"RING_V_OFF",0x0000}, +{20,7,"RING_OSC",0x7EF0}, +{21,8,"RING_X",0x0160}, +{22,9,"RING_Y",0x0000}, +{23,255,"PULSE_ENVEL",0x2000}, +{24,255,"PULSE_X",0x2000}, +{25,255,"PULSE_Y",0x0000}, +//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower +{26,13,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower +{27,14,"XMIT_DIGITAL_GAIN",0x4000}, +//{27,14,"XMIT_DIGITAL_GAIN",0x2000}, +{28,15,"LOOP_CLOSE_TRES",0x1000}, +{29,16,"RING_TRIP_TRES",0x3600}, +{30,17,"COMMON_MIN_TRES",0x1000}, +{31,18,"COMMON_MAX_TRES",0x0200}, +{32,19,"PWR_ALARM_Q1Q2",0x07C0}, +{33,20,"PWR_ALARM_Q3Q4",0x2600}, +{34,21,"PWR_ALARM_Q5Q6",0x1B80}, +{35,22,"LOOP_CLOSURE_FILTER",0x8000}, +{36,23,"RING_TRIP_FILTER",0x0320}, +{37,24,"TERM_LP_POLE_Q1Q2",0x008C}, +{38,25,"TERM_LP_POLE_Q3Q4",0x0100}, +{39,26,"TERM_LP_POLE_Q5Q6",0x0010}, +{40,27,"CM_BIAS_RINGING",0x0C00}, +{41,64,"DCDC_MIN_V",0x0C00}, +{42,255,"DCDC_XTRA",0x1000}, +{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, +}; + + +#include +#include + +#include "fxo_modes.h" + +#define NUM_FXO_REGS 60 + +#define WC_MAX_IFACES 128 + +#define WC_OFFSET 4 /* Offset between transmit and receive, in bytes. */ +#define WC_SYNCFLAG 0xca1ef1ac + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 +#define WC_AUXR 0x07 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 + +#define WC_AUXFUNC 0x2b +#define WC_SERCTL 0x2d +#define WC_FSCDELAY 0x2f + +#define WC_REGBASE 0xc0 + +#define WC_VER 0x0 +#define WC_CS 0x1 +#define WC_SPICTRL 0x2 +#define WC_SPIDATA 0x3 + +#define BIT_SPI_BYHW (1 << 0) +#define BIT_SPI_BUSY (1 << 1) // 0=can read/write spi, 1=spi working. +#define BIT_SPI_START (1 << 2) + + +#define BIT_LED_CLK (1 << 0) // MiaoLin add to control the led. +#define BIT_LED_DATA (1 << 1) // MiaoLin add to control the led. + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define FLAG_EMPTY 0 +#define FLAG_WRITE 1 +#define FLAG_READ 2 +#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ +#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ +#define OHT_TIMER 6000 /* How long after RING to retain OHT */ + +#define FLAG_3215 (1 << 0) +#define FLAG_A800 (1 << 7) + +#define MAX_NUM_CARDS 12 +#define NUM_CARDS 12 +#define NUM_FLAG 4 /* number of flag channels. */ + + +enum cid_hook_state { + CID_STATE_IDLE = 0, + CID_STATE_RING_ON, + CID_STATE_RING_OFF, + CID_STATE_WAIT_RING_FINISH +}; + +/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ +/* #define TEST_LOG_INCOME_VOICE */ +#define voc_buffer_size (8000*8) + + +#define MAX_ALARMS 10 + +#define MOD_TYPE_FXS 0 +#define MOD_TYPE_FXO 1 + +#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ +#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ +#define PEGCOUNT 5 /* 5 cycles of pegging means RING */ + +#define NUM_CAL_REGS 12 + +struct calregs { + unsigned char vals[NUM_CAL_REGS]; +}; + +enum proslic_power_warn { + PROSLIC_POWER_UNKNOWN = 0, + PROSLIC_POWER_ON, + PROSLIC_POWER_WARNED, +}; + +enum battery_state { + BATTERY_UNKNOWN = 0, + BATTERY_PRESENT, + BATTERY_LOST, +}; +struct wctdm { + struct pci_dev *dev; + char *variety; + struct dahdi_span span; + unsigned char ios; + int usecount; + unsigned int intcount; + int dead; + int pos; + int flags[MAX_NUM_CARDS]; + int freeregion; + int alt; + int curcard; + int cardflag; /* Bit-map of present cards */ + enum proslic_power_warn proslic_power; + spinlock_t lock; + + union { + struct fxo { +#ifdef AUDIO_RINGCHECK + unsigned int pegtimer; + int pegcount; + int peg; + int ring; +#else + int wasringing; + int lastrdtx; +#endif + int ringdebounce; + int offhook; + unsigned int battdebounce; + unsigned int battalarm; + enum battery_state battery; + int lastpol; + int polarity; + int polaritydebounce; + } fxo; + struct fxs { + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + int palarms; + struct calregs calregs; + } fxs; + } mod[MAX_NUM_CARDS]; + + /* Receive hook state and debouncing */ + int modtype[MAX_NUM_CARDS]; + unsigned char reg0shadow[MAX_NUM_CARDS]; + unsigned char reg1shadow[MAX_NUM_CARDS]; + + unsigned long ioaddr; + unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ + unsigned long mem_len; /* Length of 32 bit region */ + volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ + + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned char *writechunk; /* Double-word aligned write memory */ + volatile unsigned char *readchunk; /* Double-word aligned read memory */ + /*struct dahdi_chan chans[MAX_NUM_CARDS];*/ + struct dahdi_chan _chans[NUM_CARDS]; + struct dahdi_chan *chans[NUM_CARDS]; + + +#ifdef TEST_LOG_INCOME_VOICE + char * voc_buf[MAX_NUM_CARDS + NUM_FLAG]; + int voc_ptr[MAX_NUM_CARDS + NUM_FLAG]; +#endif + int lastchan; + unsigned short ledstate; + unsigned char fwversion; + int max_cards; + char *card_name; + + char *cid_history_buf[MAX_NUM_CARDS]; + int cid_history_ptr[MAX_NUM_CARDS]; + int cid_history_clone_cnt[MAX_NUM_CARDS]; + enum cid_hook_state cid_state[MAX_NUM_CARDS]; + int cid_ring_on_time[MAX_NUM_CARDS]; +}; + +char* A1200P_Name = "A1200P"; +char* A800P_Name = "A800P"; + +struct wctdm_desc { + char *name; + int flags; +}; + +static struct wctdm_desc wctdme = { "OpenVox A1200P/A800P", 0 }; +static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; + +static struct wctdm *ifaces[WC_MAX_IFACES]; + +static void wctdm_release(struct wctdm *wc); + +static unsigned int battdebounce; +static unsigned int battalarm; +static unsigned int battthresh; +static int ringdebounce = DEFAULT_RING_DEBOUNCE; +static int fwringdetect = 0; +static int debug = 0; +static int robust = 0; +static int timingonly = 0; +static int lowpower = 0; +static int boostringer = 0; +static int fastringer = 0; +static int _opermode = 0; +static char *opermode = "FCC"; +static int fxshonormode = 0; +static int alawoverride = 0; +static int fastpickup = 0; +static int fxotxgain = 0; +static int fxorxgain = 0; +static int fxstxgain = 0; +static int fxsrxgain = 0; +/* special h/w control command */ +static int spibyhw = 1; +static int usememio = 1; +static int cidbeforering = 0; +static int cidbuflen = 3000; /* in msec, default 3000 */ +static int cidtimeout = 6*1000; /* in msec, default 6000 */ +static int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */ +static int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/ + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); + +static void wctdm_set_led(struct wctdm* wc, int card, int onoff) +{ + int i; + unsigned char c; + + wc->ledstate &= ~(0x01<ledstate |= (onoff<ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA; + outb( c, wc->ioaddr + WC_AUXD); + for(i=MAX_NUM_CARDS-1; i>=0; i--) + { + if(wc->ledstate & (0x0001<fwversion == 0x11) + c &= ~BIT_LED_DATA; + else + c |= BIT_LED_DATA; + else + if(wc->fwversion == 0x11) + c |= BIT_LED_DATA; + else + c &= ~BIT_LED_DATA; + + outb( c, wc->ioaddr + WC_AUXD); + outb( c|BIT_LED_CLK, wc->ioaddr + WC_AUXD); + outb( (c&~BIT_LED_CLK)|BIT_LED_DATA, wc->ioaddr + WC_AUXD); + } +} + + +static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) +{ + int x, y, chan_offset, pos; + volatile unsigned char *txbuf; + + if (ints & /*0x01*/ 0x04) + /* Write is at interrupt address. Start writing from normal offset */ + txbuf = wc->writechunk; + else + txbuf = wc->writechunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); + + /* Calculate Transmission */ + dahdi_transmit(&wc->span); + + if(wc->lastchan == -1) // not in sync. + return; + + chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); + + for (y=0;ymax_cards/*MAX_NUM_CARDS*/) + txbuf[pos] = wc->chans[x]->writechunk[y]; + else + txbuf[pos] = 0; + } +#endif + } +} + + +#ifdef AUDIO_RINGCHECK +static inline void ring_check(struct wctdm *wc, int card) +{ + int x; + short sample; + if (wc->modtype[card] != MOD_TYPE_FXO) + return; + wc->mod[card].fxo.pegtimer += DAHDI_CHUNKSIZE; + for (x=0;xchans[card].readchunk[x], (&(wc->chans[card]))); + if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { + if (debug > 1) printk(KERN_DEBUG "High peg!\n"); + if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME)) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = 1; + } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) { + if (debug > 1) printk(KERN_DEBUG "Low peg!\n"); + if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = -1; + } + } + if (wc->mod[card].fxo.pegtimer > PEGTIME) { + /* Reset pegcount if our timer expires */ + wc->mod[card].fxo.pegcount = 0; + } + /* Decrement debouncer if appropriate */ + if (wc->mod[card].fxo.ringdebounce) + wc->mod[card].fxo.ringdebounce--; + if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) { + if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) { + /* It's ringing */ + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + if (!wc->mod[card].fxo.offhook) + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_RING); + wc->mod[card].fxo.ring = 1; + } + if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) { + /* No more ring */ + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); + wc->mod[card].fxo.ring = 0; + } + } +} +#endif + + +static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints) +{ + volatile unsigned char *rxbuf; + int x, y, chan_offset; + + + if (ints & 0x08/*0x04*/) + /* Read is at interrupt address. Valid data is available at normal offset */ + rxbuf = wc->readchunk; + else + rxbuf = wc->readchunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); + + for(x=0; x<4; x++) + if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) + break; + if(x==4) + { + printk("buffer sync misseed!\n"); + wc->lastchan = -1; + return; + } + else if(wc->lastchan != x) + { + printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x); + wc->lastchan = x; + } + chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); + + for (x=0;xmax_cards/*MAX_NUM_CARDS*/;y++) { + if (wc->cardflag & (1 << y)) + wc->chans[y]->readchunk[x] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; +#ifdef TEST_LOG_INCOME_VOICE + wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)]; + wc->voc_ptr[y]++; + if(wc->voc_ptr[y] >= voc_buffer_size) + wc->voc_ptr[y] = 0; +#endif + } +#endif + } + + if(cidbeforering) + { + for(x=0; xmax_cards; x++) + { + if (wc->modtype[wc->chans[x]->chanpos - 1] == MOD_TYPE_FXO) + if(wc->mod[wc->chans[x]->chanpos - 1].fxo.offhook == 0) + { + /*unsigned int *p_readchunk, *p_cid_history; + + p_readchunk = (unsigned int*)wc->chans[x].readchunk; + p_cid_history = (unsigned int*)(wc->cid_history_buf[x] + wc->cid_history_ptr[x]);*/ + + if(wc->cid_state[x] == CID_STATE_IDLE) /* we need copy data to the cid voice buffer */ + { + memcpy(wc->cid_history_buf[x] + wc->cid_history_ptr[x], wc->chans[x]->readchunk, DAHDI_CHUNKSIZE); + wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + else if (wc->cid_state[x] == CID_STATE_RING_ON) + wc->cid_history_clone_cnt[x] = cidbuflen; + else if (wc->cid_state[x] == CID_STATE_RING_OFF) + { + if(wc->cid_history_clone_cnt[x]) + { + memcpy(wc->chans[x]->readchunk, wc->cid_history_buf[x] + wc->cid_history_ptr[x], DAHDI_MAX_CHUNKSIZE); + wc->cid_history_clone_cnt[x]--; + wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_MAX_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + else + { + wc->cid_state[x] = CID_STATE_WAIT_RING_FINISH; + wc->cid_history_clone_cnt[x] = cidtimeout; /* wait 6 sec, if no ring, return to idle */ + } + } + else if(wc->cid_state[x] == CID_STATE_WAIT_RING_FINISH) + { + if(wc->cid_history_clone_cnt[x] > 0) + wc->cid_history_clone_cnt[x]--; + else + { + wc->cid_state[x] = CID_STATE_IDLE; + wc->cid_history_ptr[x] = 0; + wc->cid_history_clone_cnt[x] = 0; + } + } + } + } + } + +#ifdef AUDIO_RINGCHECK + for (x=0;xmax_cards;x++) + ring_check(wc, x); +#endif + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) + dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + } + dahdi_receive(&wc->span); +} + +static void wctdm_stop_dma(struct wctdm *wc); +static void wctdm_reset_tdm(struct wctdm *wc); +static void wctdm_restart_dma(struct wctdm *wc); + + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg); +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val); + + +static inline void __write_8bits(struct wctdm *wc, unsigned char bits) +{ + if(spibyhw == 0) + { + int x; + /* Drop chip select */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + /* Send out each bit, MSB first, drop SCLK as we do so */ + if (bits & 0x80) + wc->ios |= BIT_SDI; + else + wc->ios &= ~BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + bits <<= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPIDATA, bits); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } +} + + +static inline void __reset_spi(struct wctdm *wc) +{ + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* Drop chip select and clock once and raise and clock once */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios |= BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Clock again */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + + __wctdm_setcreg(wc, WC_SPICTRL, spibyhw); + +} + +static inline unsigned char __read_8bits(struct wctdm *wc) +{ + unsigned char res=0, c; + int x; + if(spibyhw == 0) + { + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Drop chip select */ + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + res <<= 1; + /* Get SCLK */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Read back the value */ + c = inb(wc->ioaddr + WC_AUXR); + if (c & BIT_SDO) + res |= 1; + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + res = __wctdm_getcreg(wc, WC_SPIDATA); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } + + /* And return our result */ + return res; +} + +static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + *p = val; +} + +static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + return (*p)&0x00ff; +} + + +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + if(usememio) + __wctdm_setcreg_mem(wc, reg, val); + else + outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) +{ + if(usememio) + return __wctdm_getcreg_mem(wc, reg); + else + return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static inline void __wctdm_setcard(struct wctdm *wc, int card) +{ + if (wc->curcard != card) { + __wctdm_setcreg(wc, WC_CS, card); + wc->curcard = card; + //printk("Select card %d\n", card); + } +} + +static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x20); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg & 0x7f); + } + __write_8bits(wc, value); +} + +static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setreg(wc, card, reg, value); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x60); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg | 0x80); + } + return __read_8bits(wc); +} + +static inline void reset_spi(struct wctdm *wc, int card) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setcard(wc, card); + __reset_spi(wc); + __reset_spi(wc); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + spin_lock_irqsave(&wc->lock, flags); + res = __wctdm_getreg(wc, card, reg); + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int __wait_access(struct wctdm *wc, int card) +{ + unsigned char data = 0; + long origjiffies; + int count = 0; + + #define MAX 6000 /* attempts */ + + + origjiffies = jiffies; + /* Wait for indirect access */ + while (count++ < MAX) + { + data = __wctdm_getreg(wc, card, I_STATUS); + + if (!data) + return 0; + + } + + if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data); + + return 0; +} + +static unsigned char translate_3215(unsigned char address) +{ + int x; + for (x=0;xflags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if(!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); + __wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); + __wctdm_setreg(wc, card, IAA,address); + res = 0; + }; + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) +{ + unsigned long flags; + int res = -1; + char *p=NULL; + /* Translate 3215 addresses */ + if (wc->flags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if (!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IAA, address); + if (!__wait_access(wc, card)) { + unsigned char data1, data2; + data1 = __wctdm_getreg(wc, card, IDA_LO); + data2 = __wctdm_getreg(wc, card, IDA_HI); + res = data1 | (data2 << 8); + } else + p = "Failed to wait inside\n"; + } else + p = "failed to wait\n"; + spin_unlock_irqrestore(&wc->lock, flags); + if (p) + printk(KERN_NOTICE "%s", p); + return res; +} + +static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) +{ + unsigned char i; + + for (i=0; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) + { + printk(KERN_NOTICE "!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,indirect_regs[i].address,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) + printk(KERN_DEBUG "Init Indirect Registers completed successfully.\n"); + } else { + printk(KERN_NOTICE " !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) +{ + int res; + /* Check loopback */ + res = wc->reg1shadow[card]; + + if (!res && (res != wc->mod[card].fxs.lasttxhook)) // read real state from register By wx + res=wctdm_getreg(wc, card, 64); + + if (!res && (res != wc->mod[card].fxs.lasttxhook)) { + res = wctdm_getreg(wc, card, 8); + if (res) { + printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card); + wctdm_init_proslic(wc, card, 1, 0, 1); + } else { + if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { + printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); + if (wc->mod[card].fxs.lasttxhook == 4) + wc->mod[card].fxs.lasttxhook = 1; + wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); + } else { + if (wc->mod[card].fxs.palarms == MAX_ALARMS) + printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); + } + } + } +} +static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) +{ +#define MS_PER_CHECK_HOOK 16 + +#ifndef AUDIO_RINGCHECK + unsigned char res; +#endif + signed char b; + int errors = 0; + struct fxo *fxo = &wc->mod[card].fxo; + + /* Try to track issues that plague slot one FXO's */ + b = wc->reg0shadow[card]; + if ((b & 0x2) || !(b & 0x8)) { + /* Not good -- don't look at anything else */ + if (debug) + printk(KERN_DEBUG "Error (%02x) on card %d!\n", b, card + 1); + errors++; + } + b &= 0x9b; + if (fxo->offhook) { + if (b != 0x9) + wctdm_setreg(wc, card, 5, 0x9); + } else { + if (b != 0x8) + wctdm_setreg(wc, card, 5, 0x8); + } + if (errors) + return; + if (!fxo->offhook) { + if(fixedtimepolarity) + if ( wc->cid_state[card] == CID_STATE_RING_ON && wc->cid_ring_on_time[card]>0) + if(wc->cid_ring_on_time[card]>=fixedtimepolarity ) + { + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_ring_on_time[card] = -1; /* the polarity already sent */ + } + else + wc->cid_ring_on_time[card] += 16; + if (fwringdetect) { + res = wc->reg0shadow[card] & 0x60; + if (fxo->ringdebounce) { + --fxo->ringdebounce; + if (res && (res != fxo->lastrdtx) && + (fxo->battery == BATTERY_PRESENT)) { + if (!fxo->wasringing) { + fxo->wasringing = 1; + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_IDLE) + { + wc->cid_state[card] = CID_STATE_RING_ON; + wc->cid_ring_on_time[card] = 1; /* check every 16ms */ + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + fxo->lastrdtx = res; + fxo->ringdebounce = 10; + } else if (!res) { + if ((fxo->ringdebounce == 0) && fxo->wasringing) { + fxo->wasringing = 0; + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_RING_ON) + { + if(fixedtimepolarity==0) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_state[card] = CID_STATE_RING_OFF; + } + else + { + if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) + wc->cid_history_clone_cnt[card] = cidtimeout; + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + else + + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + } else if (res && (fxo->battery == BATTERY_PRESENT)) { + fxo->lastrdtx = res; + fxo->ringdebounce = 10; + } + } else { + res = wc->reg0shadow[card]; + if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { + fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); + if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { + if (!fxo->wasringing) { + fxo->wasringing = 1; + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_IDLE) + { + wc->cid_state[card] = CID_STATE_RING_ON; + wc->cid_ring_on_time[card] = 1; /* check every 16ms */ + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + } + fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; + } + } else { + fxo->ringdebounce -= DAHDI_CHUNKSIZE * 4; + if (fxo->ringdebounce <= 0) { + if (fxo->wasringing) { + fxo->wasringing = 0; + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_RING_ON) + { + if(fixedtimepolarity==0) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_state[card] = CID_STATE_RING_OFF; + } + else + { + if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) + wc->cid_history_clone_cnt[card] = cidtimeout; + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + } + fxo->ringdebounce = 0; + } + } + } + } + + b = wc->reg1shadow[card]; + if (abs(b) < battthresh) { + /* possible existing states: + battery lost, no debounce timer + battery lost, debounce timer (going to battery present) + battery present or unknown, no debounce timer + battery present or unknown, debounce timer (going to battery lost) + */ + + if (fxo->battery == BATTERY_LOST) { + if (fxo->battdebounce) { + /* we were going to BATTERY_PRESENT, but battery was lost again, + so clear the debounce timer */ + fxo->battdebounce = 0; + } + } else { + if (fxo->battdebounce) { + /* going to BATTERY_LOST, see if we are there yet */ + if (--fxo->battdebounce == 0) { + fxo->battery = BATTERY_LOST; + if (debug) + printk(KERN_DEBUG "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); +#ifdef JAPAN + if (!wc->ohdebounce && wc->offhook) { + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_ONHOOK); + if (debug) + printk(KERN_DEBUG "Signalled On Hook\n"); +#ifdef ZERO_BATT_RING + wc->onhook++; +#endif + } +#else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + /* set the alarm timer, taking into account that part of its time + period has already passed while debouncing occurred */ + fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; +#endif + } + } else { + /* start the debounce timer to verify that battery has been lost */ + fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; + } + } + } else { + /* possible existing states: + battery lost or unknown, no debounce timer + battery lost or unknown, debounce timer (going to battery present) + battery present, no debounce timer + battery present, debounce timer (going to battery lost) + */ + + if (fxo->battery == BATTERY_PRESENT) { + if (fxo->battdebounce) { + /* we were going to BATTERY_LOST, but battery appeared again, + so clear the debounce timer */ + fxo->battdebounce = 0; + } + } else { + if (fxo->battdebounce) { + /* going to BATTERY_PRESENT, see if we are there yet */ + if (--fxo->battdebounce == 0) { + fxo->battery = BATTERY_PRESENT; + if (debug) + printk(KERN_DEBUG "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, + (b < 0) ? "-" : "+"); +#ifdef ZERO_BATT_RING + if (wc->onhook) { + wc->onhook = 0; + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (debug) + printk(KERN_DEBUG "Signalled Off Hook\n"); + } +#else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); +#endif + /* set the alarm timer, taking into account that part of its time + period has already passed while debouncing occurred */ + fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; + } + } else { + /* start the debounce timer to verify that battery has appeared */ + fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; + } + } + } + + if (fxo->lastpol >= 0) { + if (b < 0) { + fxo->lastpol = -1; + fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; + } + } + if (fxo->lastpol <= 0) { + if (b > 0) { + fxo->lastpol = 1; + fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; + } + } + + if (fxo->battalarm) { + if (--fxo->battalarm == 0) { + /* the alarm timer has expired, so update the battery alarm state + for this channel */ + dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); + } + } + + if (fxo->polaritydebounce) { + if (--fxo->polaritydebounce == 0) { + if (fxo->lastpol != fxo->polarity) { + if (debug) + printk(KERN_DEBUG "%lu Polarity reversed (%d -> %d)\n", jiffies, + fxo->polarity, + fxo->lastpol); + if (fxo->polarity) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + fxo->polarity = fxo->lastpol; + } + } + } +#undef MS_PER_CHECK_HOOK +} + +static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) +{ + char res; + int hook; + + /* For some reason we have to debounce the + hook detector. */ + + res = wc->reg0shadow[card]; + hook = (res & 1); + if (hook != wc->mod[card].fxs.lastrxhook) { + /* Reset the debounce (must be multiple of 4ms) */ + wc->mod[card].fxs.debounce = 8 * (4 * 8); +#if 0 + printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); +#endif + } else { + if (wc->mod[card].fxs.debounce > 0) { + wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE; +#if 0 + printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); +#endif + if (!wc->mod[card].fxs.debounce) { +#if 0 + printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); +#endif + wc->mod[card].fxs.debouncehook = hook; + } + if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { + /* Off hook */ +#if 1 + if (debug) +#endif + printk(KERN_DEBUG "opvxa1200: Card %d Going off hook\n", card); + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (robust) + wctdm_init_proslic(wc, card, 1, 0, 1); + wc->mod[card].fxs.oldrxhook = 1; + + } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { + /* On hook */ +#if 1 + if (debug) +#endif + printk(KERN_DEBUG "opvxa1200: Card %d Going on hook\n", card); + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + wc->mod[card].fxs.oldrxhook = 0; + } + } + } + wc->mod[card].fxs.lastrxhook = hook; +} + +DAHDI_IRQ_HANDLER(wctdm_interrupt) +{ + struct wctdm *wc = dev_id; + unsigned char ints; + int x, y, z; + int mode; + + ints = inb(wc->ioaddr + WC_INTSTAT); + + if (!ints) + return IRQ_NONE; + + outb(ints, wc->ioaddr + WC_INTSTAT); + + if (ints & 0x10) { + /* Stop DMA, wait for watchdog */ + printk(KERN_INFO "TDM PCI Master abort\n"); + wctdm_stop_dma(wc); + return IRQ_RETVAL(1); + } + + if (ints & 0x20) { + printk(KERN_INFO "PCI Target abort\n"); + return IRQ_RETVAL(1); + } + + for (x=0;xmax_cards/*4*3*/;x++) { + if (wc->cardflag & (1 << x) && + (wc->modtype[x] == MOD_TYPE_FXS)) { + if (wc->mod[x].fxs.lasttxhook == 0x4) { + /* RINGing, prepare for OHT */ + wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[x].fxs.idletxhookstate = 0x2; + } else { + if (wc->mod[x].fxs.ohttimer) { + wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; + if (!wc->mod[x].fxs.ohttimer) { + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ + else + wc->mod[x].fxs.idletxhookstate = 0x1; + if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[x].fxs.lasttxhook = 0x5; + else + wc->mod[x].fxs.lasttxhook = 0x1; + wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); + } + } + } + } + } + } + + if (ints & 0x0f) { + wc->intcount++; + z = wc->intcount & 0x3; + mode = wc->intcount & 0xc; + for(y=0; ymax_cards/4/*3*/; y++) + { + x = z + y*4; + if (wc->cardflag & (1 << x ) ) + { + switch(mode) + { + case 0: + /* Rest */ + break; + case 4: + /* Read first shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 68); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 5); + break; + case 8: + /* Read second shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 64); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 29); + break; + case 12: + /* Perform processing */ + if (wc->modtype[x] == MOD_TYPE_FXS) { + wctdm_proslic_check_hook(wc, x); + if (!(wc->intcount & 0xf0)) + wctdm_proslic_recheck_sanity(wc, x); + } else if (wc->modtype[x] == MOD_TYPE_FXO) { + wctdm_voicedaa_check_hook(wc, x); + } + break; + } + } + } + if (!(wc->intcount % 10000)) { + /* Accept an alarm once per 10 seconds */ + for (x=0;xmax_cards/*4*3*/;x++) + if (wc->modtype[x] == MOD_TYPE_FXS) { + if (wc->mod[x].fxs.palarms) + wc->mod[x].fxs.palarms--; + } + } + wctdm_receiveprep(wc, ints); + wctdm_transmitprep(wc, ints); + } + + return IRQ_RETVAL(1); + +} + +static int wctdm_voicedaa_insane(struct wctdm *wc, int card) +{ + int blah; + blah = wctdm_getreg(wc, card, 2); + if (blah != 0x3) + return -2; + blah = wctdm_getreg(wc, card, 11); + if (debug) + printk(KERN_DEBUG "VoiceDAA System: %02x\n", blah & 0xf); + return 0; +} + +static int wctdm_proslic_insane(struct wctdm *wc, int card) +{ + int blah,insane_report; + insane_report=0; + + blah = wctdm_getreg(wc, card, 0); + if (debug) + printk(KERN_DEBUG "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); + +#if 0 + if ((blah & 0x30) >> 4) { + printk(KERN_DEBUG "ProSLIC on module %d is not a 3210.\n", card); + return -1; + } +#endif + if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) { + /* SLIC not loaded */ + return -1; + } + if ((blah & 0xf) < 2) { + printk(KERN_NOTICE "ProSLIC 3210 version %d is too old\n", blah & 0xf); + return -1; + } + if (wctdm_getreg(wc, card, 1) & 0x80) + /* ProSLIC 3215, not a 3210 */ + wc->flags[card] |= FLAG_3215; + + blah = wctdm_getreg(wc, card, 8); + if (blah != 0x2) { + printk(KERN_NOTICE "ProSLIC on module %d insane (1) %d should be 2\n", card, blah); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); + + blah = wctdm_getreg(wc, card, 64); + if (blah != 0x0) { + printk(KERN_NOTICE "ProSLIC on module %d insane (2)\n", card); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); + + blah = wctdm_getreg(wc, card, 11); + if (blah != 0x33) { + printk(KERN_NOTICE "ProSLIC on module %d insane (3)\n", card); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah); + + /* Just be sure it's setup right. */ + wctdm_setreg(wc, card, 30, 0); + + if (debug) + printk(KERN_DEBUG "ProSLIC on module %d seems sane.\n", card); + return 0; +} + +static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + unsigned char vbat; + + /* Turn off linefeed */ + wctdm_setreg(wc, card, 64, 0); + + /* Power down */ + wctdm_setreg(wc, card, 14, 0x10); + + /* Wait for one second */ + origjiffies = jiffies; + + while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { + if ((jiffies - origjiffies) >= (HZ/2)) + break;; + } + + if (vbat < 0x06) { + printk(KERN_NOTICE "Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card, + 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ)); + return -1; + } else if (debug) { + printk(KERN_NOTICE "Post-leakage voltage: %d volts\n", 376 * vbat / 1000); + } + return 0; +} + +static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) +{ + unsigned char vbat; + unsigned long origjiffies; + int lim; + + /* Set period of DC-DC converter to 1/64 khz */ + wctdm_setreg(wc, card, 92, 0xff /* was 0xff */); + + /* Wait for VBat to powerup */ + origjiffies = jiffies; + + /* Disable powerdown */ + wctdm_setreg(wc, card, 14, 0); + + /* If fast, don't bother checking anymore */ + if (fast) + return 0; + + while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { + /* Wait no more than 500ms */ + if ((jiffies - origjiffies) > HZ/2) { + break; + } + } + + if (vbat < 0xc0) { + if (wc->proslic_power == PROSLIC_POWER_UNKNOWN) + printk(KERN_NOTICE "ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n", + card, (int)(((jiffies - origjiffies) * 1000 / HZ)), + vbat * 375); + wc->proslic_power = PROSLIC_POWER_WARNED; + return -1; + } else if (debug) { + printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); + } + wc->proslic_power = PROSLIC_POWER_ON; + + /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ + /* If out of range, just set it to the default value */ + lim = (loopcurrent - 20) / 3; + if ( loopcurrent > 41 ) { + lim = 0; + if (debug) + printk(KERN_DEBUG "Loop current out of range! Setting to default 20mA!\n"); + } + else if (debug) + printk(KERN_DEBUG "Loop current set to %dmA!\n",(lim*3)+20); + wctdm_setreg(wc,card,LOOP_I_LIMIT,lim); + + /* Engage DC-DC converter */ + wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); +#if 0 + origjiffies = jiffies; + while(0x80 & wctdm_getreg(wc, card, 93)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk(KERN_DEBUG "Timeout waiting for DC-DC calibration on module %d\n", card); + return -1; + } + } + +#if 0 + /* Wait a full two seconds */ + while((jiffies - origjiffies) < 2 * HZ); + + /* Just check to be sure */ + vbat = wctdm_getreg(wc, card, 82); + printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); +#endif +#endif + return 0; + +} + +static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){ + unsigned long origjiffies; + unsigned char i; + + wctdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21 + wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 64, 0);//(0) + + wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. + wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM + + origjiffies=jiffies; + while( wctdm_getreg(wc,card,96)!=0 ){ + if((jiffies-origjiffies)>80) + return -1; + } +//Initialized DR 98 and 99 to get consistant results. +// 98 and 99 are the results registers and the search should have same intial conditions. + +/*******************************The following is the manual gain mismatch calibration****************************/ +/*******************************This is also available as a function *******************************************/ + // Delay 10ms + origjiffies=jiffies; + while((jiffies-origjiffies)<1); + wctdm_proslic_setreg_indirect(wc, card, 88,0); + wctdm_proslic_setreg_indirect(wc,card,89,0); + wctdm_proslic_setreg_indirect(wc,card,90,0); + wctdm_proslic_setreg_indirect(wc,card,91,0); + wctdm_proslic_setreg_indirect(wc,card,92,0); + wctdm_proslic_setreg_indirect(wc,card,93,0); + + wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time + wctdm_setreg(wc, card, 99,0x10); + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 98,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,88)) == 0) + break; + } // for + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 99,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,89)) == 0) + break; + }//for + +/*******************************The preceding is the manual gain mismatch calibration****************************/ +/**********************************The following is the longitudinal Balance Cal***********************************/ + wctdm_setreg(wc,card,64,1); + while((jiffies-origjiffies)<10); // Sleep 100? + + wctdm_setreg(wc, card, 64, 0); + wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal + wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration + wctdm_setreg(wc, card, 96,0x40); + + wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ + + wctdm_setreg(wc, card, 21, 0xFF); + wctdm_setreg(wc, card, 22, 0xFF); + wctdm_setreg(wc, card, 23, 0xFF); + + /**The preceding is the longitudinal Balance Cal***/ + return(0); + +} +#if 1 +static int wctdm_proslic_calibrate(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + int x; + /* Perform all calibrations */ + wctdm_setreg(wc, card, 97, 0x1f); + + /* Begin, no speedup */ + wctdm_setreg(wc, card, 96, 0x5f); + + /* Wait for it to finish */ + origjiffies = jiffies; + while(wctdm_getreg(wc, card, 96)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk(KERN_NOTICE "Timeout waiting for calibration of module %d\n", card); + return -1; + } + } + + if (debug) { + /* Print calibration parameters */ + printk(KERN_DEBUG "Calibration Vector Regs 98 - 107: \n"); + for (x=98;x<108;x++) { + printk(KERN_DEBUG "%d: %02x\n", x, wctdm_getreg(wc, card, x)); + } + } + return 0; +} +#endif + +static void wait_just_a_bit(int foo) +{ + long newjiffies; + newjiffies = jiffies + foo; + while(jiffies < newjiffies); +} + +/********************************************************************* + * Set the hwgain on the analog modules + * + * card = the card position for this module (0-23) + * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) + * tx = (0 for rx; 1 for tx) + * + *******************************************************************/ +static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) +{ + if (!(wc->modtype[card] == MOD_TYPE_FXO)) { + printk(KERN_NOTICE "Cannot adjust gain. Unsupported module type!\n"); + return -1; + } + if (tx) { + if (debug) + printk(KERN_DEBUG "setting FXO tx gain for card=%d to %d\n", card, gain); + if (gain >= -150 && gain <= 0) { + wctdm_setreg(wc, card, 38, 16 + (gain/-10)); + wctdm_setreg(wc, card, 40, 16 + (-gain%10)); + } else if (gain <= 120 && gain > 0) { + wctdm_setreg(wc, card, 38, gain/10); + wctdm_setreg(wc, card, 40, (gain%10)); + } else { + printk(KERN_INFO "FXO tx gain is out of range (%d)\n", gain); + return -1; + } + } else { /* rx */ + if (debug) + printk(KERN_DEBUG "setting FXO rx gain for card=%d to %d\n", card, gain); + if (gain >= -150 && gain <= 0) { + wctdm_setreg(wc, card, 39, 16+ (gain/-10)); + wctdm_setreg(wc, card, 41, 16 + (-gain%10)); + } else if (gain <= 120 && gain > 0) { + wctdm_setreg(wc, card, 39, gain/10); + wctdm_setreg(wc, card, 41, (gain%10)); + } else { + printk(KERN_INFO "FXO rx gain is out of range (%d)\n", gain); + return -1; + } + } + + return 0; +} + +static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + unsigned char reg16=0, reg26=0, reg30=0, reg31=0; + long newjiffies; + wc->modtype[card] = MOD_TYPE_FXO; + /* Sanity check the ProSLIC */ + reset_spi(wc, card); + if (!sane && wctdm_voicedaa_insane(wc, card)) + return -2; + + /* Software reset */ + wctdm_setreg(wc, card, 1, 0x80); + + /* Wait just a bit */ + wait_just_a_bit(HZ/10); + + /* Enable PCM, ulaw */ + if (alawoverride) + wctdm_setreg(wc, card, 33, 0x20); + else + wctdm_setreg(wc, card, 33, 0x28); + + /* Set On-hook speed, Ringer impedence, and ringer threshold */ + reg16 |= (fxo_modes[_opermode].ohs << 6); + reg16 |= (fxo_modes[_opermode].rz << 1); + reg16 |= (fxo_modes[_opermode].rt); + wctdm_setreg(wc, card, 16, reg16); + + if(fwringdetect) { + /* Enable ring detector full-wave rectifier mode */ + wctdm_setreg(wc, card, 18, 2); + wctdm_setreg(wc, card, 24, 0); + } else { + /* Set to the device defaults */ + wctdm_setreg(wc, card, 18, 0); + wctdm_setreg(wc, card, 24, 0x19); + } + + /* Set DC Termination: + Tip/Ring voltage adjust, minimum operational current, current limitation */ + reg26 |= (fxo_modes[_opermode].dcv << 6); + reg26 |= (fxo_modes[_opermode].mini << 4); + reg26 |= (fxo_modes[_opermode].ilim << 1); + wctdm_setreg(wc, card, 26, reg26); + + /* Set AC Impedence */ + reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); + wctdm_setreg(wc, card, 30, reg30); + + /* Misc. DAA parameters */ + if (fastpickup) + reg31 = 0xb3; + else + reg31 = 0xa3; + + reg31 |= (fxo_modes[_opermode].ohs2 << 3); + wctdm_setreg(wc, card, 31, reg31); + + /* Set Transmit/Receive timeslot */ + //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 35, 0x00); + wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 37, 0x00); + + /* Enable ISO-Cap */ + wctdm_setreg(wc, card, 6, 0x00); + + if (fastpickup) + wctdm_setreg(wc, card, 17, wctdm_getreg(wc, card, 17) | 0x20); + + /* Wait 1000ms for ISO-cap to come up */ + newjiffies = jiffies; + newjiffies += 2 * HZ; + while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) + wait_just_a_bit(HZ/10); + + if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { + printk(KERN_NOTICE "VoiceDAA did not bring up ISO link properly!\n"); + return -1; + } + if (debug) + printk(KERN_DEBUG "ISO-Cap is now up, line side: %02x rev %02x\n", + wctdm_getreg(wc, card, 11) >> 4, + (wctdm_getreg(wc, card, 13) >> 2) & 0xf); + /* Enable on-hook line monitor */ + wctdm_setreg(wc, card, 5, 0x08); + + /* Take values for fxotxgain and fxorxgain and apply them to module */ + wctdm_set_hwgain(wc, card, fxotxgain, 1); + wctdm_set_hwgain(wc, card, fxorxgain, 0); + + /* NZ -- crank the tx gain up by 7 dB */ + if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { + printk(KERN_INFO "Adjusting gain\n"); + wctdm_set_hwgain(wc, card, 7, 1); + } + + if(debug) + printk(KERN_DEBUG "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16)?-(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16)? -(wctdm_getreg(wc, card, 40) - 16):wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16)? -(wctdm_getreg(wc, card, 39) - 16) : wctdm_getreg(wc, card, 39),(wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16):wctdm_getreg(wc, card, 41)); + + return 0; + +} + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + + unsigned short tmp[5]; + unsigned char r19, r9; + int x; + int fxsmode=0; + + /* Sanity check the ProSLIC */ + if (!sane && wctdm_proslic_insane(wc, card)) + return -2; + + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[card].fxs.idletxhookstate = 5; + else + wc->mod[card].fxs.idletxhookstate = 1; + + if (sane) { + /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ + wctdm_setreg(wc, card, 14, 0x10); + } + + if (wctdm_proslic_init_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); + return -1; + } + + /* Clear scratch pad area */ + wctdm_proslic_setreg_indirect(wc, card, 97,0); + + /* Clear digital loopback */ + wctdm_setreg(wc, card, 8, 0); + + /* Revision C optimization */ + wctdm_setreg(wc, card, 108, 0xeb); + + /* Disable automatic VBat switching for safety to prevent + Q7 from accidently turning on and burning out. */ + wctdm_setreg(wc, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads + change this to 0x17 */ + + /* Turn off Q7 */ + wctdm_setreg(wc, card, 66, 1); + + /* Flush ProSLIC digital filters by setting to clear, while + saving old values */ + for (x=0;x<5;x++) { + tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35); + wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000); + } + + /* Power up the DC-DC converter */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk(KERN_NOTICE "Unable to do INITIAL ProSLIC powerup on module %d\n", card); + return -1; + } + + if (!fast) { + + /* Check for power leaks */ + if (wctdm_proslic_powerleak_test(wc, card)) { + printk(KERN_NOTICE "ProSLIC module %d failed leakage test. Check for short circuit\n", card); + } + /* Power up again */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk(KERN_NOTICE "Unable to do FINAL ProSLIC powerup on module %d\n", card); + return -1; + } +#ifndef NO_CALIBRATION + /* Perform calibration */ + if(manual) { + if (wctdm_proslic_manual_calibrate(wc, card)) { + //printk(KERN_NOTICE "Proslic failed on Manual Calibration\n"); + if (wctdm_proslic_manual_calibrate(wc, card)) { + printk(KERN_NOTICE "Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); + return -1; + } + printk(KERN_NOTICE "Proslic Passed Manual Calibration on Second Attempt\n"); + } + } + else { + if(wctdm_proslic_calibrate(wc, card)) { + //printk(KERN_NOTICE "ProSlic died on Auto Calibration.\n"); + if (wctdm_proslic_calibrate(wc, card)) { + printk(KERN_NOTICE "Proslic Failed on Second Attempt to Auto Calibrate\n"); + return -1; + } + printk(KERN_NOTICE "Proslic Passed Auto Calibration on Second Attempt\n"); + } + } + /* Perform DC-DC calibration */ + wctdm_setreg(wc, card, 93, 0x99); + r19 = wctdm_getreg(wc, card, 107); + if ((r19 < 0x2) || (r19 > 0xd)) { + printk(KERN_NOTICE "DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); + wctdm_setreg(wc, card, 107, 0x8); + } + + /* Save calibration vectors */ + for (x=0;xmod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); +#endif + + } else { + /* Restore calibration registers */ + for (x=0;xmod[card].fxs.calregs.vals[x]); + } + /* Calibration complete, restore original values */ + for (x=0;x<5;x++) { + wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]); + } + + if (wctdm_proslic_verify_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed verification.\n"); + return -1; + } + + +#if 0 + /* Disable Auto Power Alarm Detect and other "features" */ + wctdm_setreg(wc, card, 67, 0x0e); + blah = wctdm_getreg(wc, card, 67); +#endif + +#if 0 + if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix + printk(KERN_INFO "ProSlic IndirectReg Died.\n"); + return -1; + } +#endif + + if (alawoverride) + wctdm_setreg(wc, card, 1, 0x20); + else + wctdm_setreg(wc, card, 1, 0x28); + // U-Law 8-bit interface + wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64); // Tx Start count low byte 0 + wctdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0 + wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64); // Rx Start count low byte 0 + wctdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0 + wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt + wctdm_setreg(wc, card, 19, 0xff); + wctdm_setreg(wc, card, 20, 0xff); + wctdm_setreg(wc, card, 73, 0x04); + if (fxshonormode) { + fxsmode = acim2tiss[fxo_modes[_opermode].acim]; + wctdm_setreg(wc, card, 10, 0x08 | fxsmode); + if (fxo_modes[_opermode].ring_osc) + wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc); + if (fxo_modes[_opermode].ring_x) + wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x); + } + if (lowpower) + wctdm_setreg(wc, card, 72, 0x10); + +#if 0 + wctdm_setreg(wc, card, 21, 0x00); // enable interrupt + wctdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt + wctdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt +#endif + +#if 0 + /* Enable loopback */ + wctdm_setreg(wc, card, 8, 0x2); + wctdm_setreg(wc, card, 14, 0x0); + wctdm_setreg(wc, card, 64, 0x0); + wctdm_setreg(wc, card, 1, 0x08); +#endif + + if (fastringer) { + /* Speed up Ringer */ + wctdm_proslic_setreg_indirect(wc, card, 20, 0x7e6d); + wctdm_proslic_setreg_indirect(wc, card, 21, 0x01b9); + /* Beef up Ringing voltage to 89V */ + if (boostringer) { + wctdm_setreg(wc, card, 74, 0x3f); + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x247)) + return -1; + printk(KERN_INFO "Boosting fast ringer on slot %d (89V peak)\n", card + 1); + } else if (lowpower) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x14b)) + return -1; + printk(KERN_INFO "Reducing fast ring power on slot %d (50V peak)\n", card + 1); + } else + printk(KERN_INFO "Speeding up ringer on slot %d (25Hz)\n", card + 1); + } else { + /* Beef up Ringing voltage to 89V */ + if (boostringer) { + wctdm_setreg(wc, card, 74, 0x3f); + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) + return -1; + printk(KERN_INFO "Boosting ringer on slot %d (89V peak)\n", card + 1); + } else if (lowpower) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) + return -1; + printk(KERN_INFO "Reducing ring power on slot %d (50V peak)\n", card + 1); + } + } + + if(fxstxgain || fxsrxgain) { + r9 = wctdm_getreg(wc, card, 9); + switch (fxstxgain) { + + case 35: + r9+=8; + break; + case -35: + r9+=4; + break; + case 0: + break; + } + + switch (fxsrxgain) { + + case 35: + r9+=2; + break; + case -35: + r9+=1; + break; + case 0: + break; + } + wctdm_setreg(wc,card,9,r9); + } + + if(debug) + printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); + + wctdm_setreg(wc, card, 64, 0x01); + return 0; +} + + +static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct dahdi_hwgain hwgain; + struct wctdm *wc = chan->pvt; + int x; + switch (cmd) { + case DAHDI_ONHOOKTRANSFER: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + if (get_user(x, (__user int *)data)) + return -EFAULT; + wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; + if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + } + break; + case DAHDI_SETPOLARITY: + if (get_user(x, (__user int *)data)) + return -EFAULT; + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || + (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + break; + case WCTDM_GET_STATS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376; + stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376; + stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376; + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + } else + return -EINVAL; + if (copy_to_user((__user void *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + for (x=0;xchanpos -1, x); + for (x=0;xchanpos - 1, x); + } else { + memset(®s, 0, sizeof(regs)); + for (x=0;xchanpos - 1, x); + } + if (copy_to_user((__user void *)data, ®s, sizeof(regs))) + return -EFAULT; + break; + case WCTDM_SET_REG: + if (copy_from_user(®op, (__user void *)data, sizeof(regop))) + return -EFAULT; + if (regop.indirect) { + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + printk(KERN_INFO "Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); + } else { + regop.val &= 0xff; + printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); + } + break; + case WCTDM_SET_ECHOTUNE: + printk(KERN_INFO "-- Setting echo registers: \n"); + if (copy_from_user(&echoregs, (__user void *)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* Set the ACIM register */ + wctdm_setreg(wc, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim); + + /* Set the digital echo canceller registers */ + wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); + wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); + wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); + wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); + wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); + wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); + wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); + wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); + + printk(KERN_INFO "-- Set echo registers successfully\n"); + + break; + } else { + return -EINVAL; + + } + break; + case DAHDI_SET_HWGAIN: + if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) + return -EFAULT; + + wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); + + if (debug) + printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", + chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); + break; + default: + return -ENOTTY; + } + return 0; + +} + +static int wctdm_open(struct dahdi_chan *chan) +{ + struct wctdm *wc = chan->pvt; + if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) + return -ENODEV; + wc->usecount++; + + /*MOD_INC_USE_COUNT; */ + try_module_get(THIS_MODULE); + return 0; +} + +static int wctdm_watchdog(struct dahdi_span *span, int event) +{ + printk(KERN_INFO "opvxa1200: Restarting DMA\n"); + wctdm_restart_dma(span->pvt); + return 0; +} + +static int wctdm_close(struct dahdi_chan *chan) +{ + struct wctdm *wc = chan->pvt; + wc->usecount--; + + /*MOD_DEC_USE_COUNT;*/ + module_put(THIS_MODULE); + + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wctdm_release(wc); + return 0; +} + +static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) +{ + struct wctdm *wc = chan->pvt; + int reg=0; + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* XXX Enable hooksig for FXO XXX */ + switch(txsig) { + case DAHDI_TXSIG_START: + case DAHDI_TXSIG_OFFHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 1; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); + if(cidbeforering) + { + wc->cid_state[chan->chanpos - 1] = CID_STATE_IDLE; + wc->cid_history_clone_cnt[chan->chanpos - 1] = 0; + wc->cid_history_ptr[chan->chanpos - 1] = 0; + memset(wc->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + break; + case DAHDI_TXSIG_ONHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 0; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); + break; + default: + printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); + } + } else { + switch(txsig) { + case DAHDI_TXSIG_ONHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + case DAHDI_SIG_FXOKS: + case DAHDI_SIG_FXOLS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + case DAHDI_SIG_FXOGS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; + break; + } + break; + case DAHDI_TXSIG_OFFHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; + break; + default: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + } + break; + case DAHDI_TXSIG_START: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; + break; + case DAHDI_TXSIG_KEWL: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; + break; + default: + printk(KERN_NOTICE "opvxa1200: Can't set tx state to %d\n", txsig); + } + if (debug) + printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); + +#if 1 + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); +#endif + } + return 0; +} + +static int wctdm_initialize(struct wctdm *wc) +{ + int x; + + /* Dahdi stuff */ + sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); + snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc->variety, wc->pos + 1); + snprintf(wc->span.location, sizeof(wc->span.location) - 1, + "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); + wc->span.manufacturer = "OpenVox"; + dahdi_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype)); + if (alawoverride) { + printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); + wc->span.deflaw = DAHDI_LAW_ALAW; + } else + wc->span.deflaw = DAHDI_LAW_MULAW; + + x = __wctdm_getcreg(wc, WC_VER); + wc->fwversion = x; + if( x & FLAG_A800) + { + wc->card_name = A800P_Name; + wc->max_cards = 8; + } + else + { + wc->card_name = A1200P_Name; + wc->max_cards = 12; + } + + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + sprintf(wc->chans[x]->name, "OPVXA1200/%d/%d", wc->pos, x); + wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + wc->chans[x]->chanpos = x+1; + wc->chans[x]->pvt = wc; + } + wc->span.chans = wc->chans; + wc->span.channels = wc->max_cards; /*MAX_NUM_CARDS;*/ + wc->span.hooksig = wctdm_hooksig; + wc->span.irq = wc->dev->irq; + wc->span.open = wctdm_open; + wc->span.close = wctdm_close; + wc->span.flags = DAHDI_FLAG_RBS; + wc->span.ioctl = wctdm_ioctl; + wc->span.watchdog = wctdm_watchdog; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + if (dahdi_register(&wc->span, 0)) { + printk(KERN_NOTICE "Unable to register span with Dahdi\n"); + return -1; + } + return 0; +} + +static void wctdm_post_initialize(struct wctdm *wc) +{ + int x; + + /* Finalize signalling */ + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) { + if (wc->modtype[x] == MOD_TYPE_FXO) + wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + else + wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { + wc->chans[x]->sigcap = 0; + } + } +} + +static int wctdm_hardware_init(struct wctdm *wc) +{ + /* Hardware stuff */ + unsigned char ver; + unsigned char x,y; + int failed; + //long origjiffies; //ml. + + /* Signal Reset */ + //printk("before raise reset\n"); + outb(0x01, wc->ioaddr + WC_CNTL); + + /* Wait for 2 second */ + /* + origjiffies = jiffies; + + while(1) + { + if ((jiffies - origjiffies) >= (HZ*5)) + break;; + } + + printk(KERN_INFO "after raise reset\n");*/ + + /* Check OpenVox chip */ + x=inb(wc->ioaddr + WC_CNTL); + ver = __wctdm_getcreg(wc, WC_VER); + wc->fwversion = ver; + /*if( ver & FLAG_A800) + { + wc->card_name = A800P_Name; + wc->max_cards = 8; + } + else + { + wc->card_name = A1200P_Name; + wc->max_cards = 12; + }*/ + printk(KERN_NOTICE "OpenVox %s version: %01x.%01x\n", wc->card_name, (ver&(~FLAG_A800))>>4, ver&0x0f); + + failed = 0; + if (ver != 0x00) { + for (x=0;x<16;x++) { + /* Test registers */ + __wctdm_setcreg(wc, WC_CS, x); + y = __wctdm_getcreg(wc, WC_CS) & 0x0f; + if (x != y) { + printk(KERN_INFO "%02x != %02x\n", x, y); + failed++; + } + } + + if (!failed) { + printk(KERN_INFO "OpenVox %s passed register test\n", wc->card_name); + } else { + printk(KERN_NOTICE "OpenVox %s failed register test\n", wc->card_name); + return -1; + } + } else { + printk(KERN_INFO "No OpenVox chip %02x\n", ver); + } + + if (spibyhw) + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); // spi controled by hw MiaoLin; + else + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* 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 */ /* Useless in OpenVox by MiaoLin. */ + /* 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); + wc->ledstate = 0; + wctdm_set_led(wc, 0, 0); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + 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(0x01, wc->ioaddr + WC_FSCDELAY); /* Modify to 1 by MiaoLin */ + + /* Setup DMA Addresses */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + outl(wc->writedma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + outl(wc->writedma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + outl(wc->readdma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + outl(wc->readdma + DAHDI_CHUNKSIZE * 8 * 4 - 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->max_cards/*MAX_NUM_CARDS*/; x++) { + int sane=0,ret=0,readi=0; +#if 1 + touch_softlockup_watchdog(); // avoid showing CPU softlock message + /* Init with Auto Calibration */ + if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk(KERN_INFO "Module %d: Installed -- AUTO FXS/DPO\n",x); + wctdm_set_led(wc, (unsigned int)x, 1); + } else { + if(ret!=-2) { + sane=1; + + printk(KERN_INFO "Init ProSlic with Manual Calibration \n"); + /* Init with Manual Calibration */ + if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk(KERN_INFO "Module %d: Installed -- MANUAL FXS\n",x); + } else { + printk(KERN_NOTICE "Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); + wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN; + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + printk(KERN_INFO "Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); + wctdm_set_led(wc, (unsigned int)x, 1); + } else + printk(KERN_NOTICE "Module %d: Not installed\n", x); + } +#endif + } + + /* Return error if nothing initialized okay. */ + if (!wc->cardflag && !timingonly) + return -1; + /*__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); */ /* removed by MiaoLin */ + return 0; +} + +static void wctdm_enable_interrupts(struct wctdm *wc) +{ + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + + /* Enable interrupts (we care about all of them) */ + outb(0x3c, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void wctdm_restart_dma(struct wctdm *wc) +{ + /* Reset Master and TDM */ + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wctdm_start_dma(struct wctdm *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 wctdm_stop_dma(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void wctdm_reset_tdm(struct wctdm *wc) +{ + /* Reset TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); +} + +static void wctdm_disable_interrupts(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct wctdm *wc; + struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; + int x; + int y; + + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x= WC_MAX_IFACES) { + printk(KERN_NOTICE "Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); + if (wc) { + int cardcount = 0; + + wc->lastchan = -1; /* first channel offset = -1; */ + wc->ledstate = 0; + + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wctdm)); + for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) { + wc->chans[x] = &wc->_chans[x]; + } + + spin_lock_init(&wc->lock); + wc->curcard = -1; + wc->ioaddr = pci_resource_start(pdev, 0); + wc->mem_region = pci_resource_start(pdev, 1); + wc->mem_len = pci_resource_len(pdev, 1); + wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + for (y=0;yflags[y] = d->flags; + /* Keep track of whether we need to free the region */ + if (request_region(wc->ioaddr, 0xff, "opvxa1200")) + wc->freeregion = 1; + else + wc->freeregion = 0; + + if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200")) + wc->freeregion |= 0x02; + + /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 8 bits. */ + wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); + if (!wc->writechunk) { + printk(KERN_NOTICE "opvxa1200: Unable to allocate DMA-able memory\n"); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + + if (wctdm_initialize(wc)) { + printk(KERN_NOTICE "opvxa1200: 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 & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + + if (request_irq(pdev->irq, wctdm_interrupt, DAHDI_IRQ_SHARED, "opvxa1200", wc)) { + printk(KERN_NOTICE "opvxa1200: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + if (wctdm_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 & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + dahdi_unregister(&wc->span); + kfree(wc); + return -EIO; + + } + +#ifdef TEST_LOG_INCOME_VOICE + for(x=0; xvoc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL); + wc->voc_ptr[x] = 0; + } +#endif + + if(cidbeforering) + { + int len = cidbuflen * DAHDI_MAX_CHUNKSIZE; + if(debug) + printk("cidbeforering support enabled, length is %d msec\n", cidbuflen); + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) + { + wc->cid_history_buf[x] = kmalloc(len, GFP_KERNEL); + wc->cid_history_ptr[x] = 0; + wc->cid_history_clone_cnt[x] = 0; + wc->cid_state[x] = CID_STATE_IDLE; + } + } + + wctdm_post_initialize(wc); + + /* Enable interrupts */ + wctdm_enable_interrupts(wc); + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2); + + /* Start DMA */ + wctdm_start_dma(wc); + + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) + cardcount++; + } + + printk(KERN_INFO "Found an OpenVox %s: Version %x.%x (%d modules)\n", wc->card_name, (wc->fwversion&(~FLAG_A800))>>4, wc->fwversion&0x0f, cardcount); + if(debug) + printk(KERN_DEBUG "OpenVox %s debug On\n", wc->card_name); + + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wctdm_release(struct wctdm *wc) +{ +#ifdef TEST_LOG_INCOME_VOICE + struct file * f = NULL; + mm_segment_t orig_fs; + int i; + char fname[20]; +#endif + + dahdi_unregister(&wc->span); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + +#ifdef TEST_LOG_INCOME_VOICE + for(i=0; if_op || !f->f_op->read) + { + printk("WARNING: File (read) object is a null pointer!!!\n"); + continue; + } + + f->f_pos = 0; + + orig_fs = get_fs(); + set_fs(KERNEL_DS); + + if(wc->voc_buf[i]) + { + f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos); + kfree(wc->voc_buf[i]); + } + + set_fs(orig_fs); + fput(f); + } +#endif + + if(cidbeforering) + { + int x; + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) + kfree(wc->cid_history_buf[x]); + } + + kfree(wc); + printk(KERN_INFO "Freed a OpenVox A1200 card\n"); +} + +static void __devexit wctdm_remove_one(struct pci_dev *pdev) +{ + struct wctdm *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + wctdm_stop_dma(wc); + wctdm_reset_tdm(wc); + + /* In case hardware is still there */ + wctdm_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + if(wc->fwversion > 0x11) + outb(0x0e, wc->ioaddr + WC_CNTL); + else + { + wc->ledstate = 0; + wctdm_set_led(wc,0,0); // power off all leds. + } + + /* Release span, possibly delayed */ + if (!wc->usecount) + wctdm_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wctdm_pci_tbl[] = { + { 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x95D9, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9500, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9532, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9559, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9599, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); + +static struct pci_driver wctdm_driver = { + .name = "opvxa1200", + .probe = wctdm_init_one, + .remove = __devexit_p(wctdm_remove_one), + .suspend = NULL, + .resume = NULL, + .id_table = wctdm_pci_tbl, +}; + +static int __init wctdm_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(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); + for (x=0;x"); +MODULE_LICENSE("GPL v2"); + +module_init(wctdm_init); +module_exit(wctdm_cleanup); --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/wcopenpci.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/wcopenpci.c @@ -0,0 +1,1841 @@ +/* + * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Matthew Fredrickson + * Ben Kramer + * Ron Lee + * + * Copyright (C) 2001, Linux Support Services, Inc. + * Copyright (C) 2005 - 2007, Voicetronix + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Conditional debug options */ +#define VERBOSE_TIMING 0 + +/* Driver constants */ +#define DRIVER_DESCRIPTION "Voicetronix OpenPCI DAHDI driver" +#define DRIVER_AUTHOR "Mark Spencer "\ + "Voicetronix " + +#define NAME "wcopenpci" +#define MAX_PORTS 8 /* Maximum number of ports on each carrier */ +#define MAX_CARDS 8 /* Maximum number of carriers per host */ + +#define DEFAULT_COUNTRY "AUSTRALIA" + + +#include +#include +#include +#include + +#include +#include +#include "proslic.h" +#include + + + +/* Compatibility helpers */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + #include +#else + typedef void irqreturn_t; + #define IRQ_NONE + #define IRQ_HANDLED + #define IRQ_RETVAL(x) + #define __devexit_p(x) x +#endif + +// Centos4.3 uses a modified 2.6.9 kernel, with no indication that +// it is different from the mainstream (or even Centos4.2 2.6.9) +// kernel, so we must crowbar off the dunce-hat manually here. +#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + typedef int gfp_t; + static inline void *kzalloc( size_t n, gfp_t flags ){ + void *p = kmalloc(n,flags); + if (p) memset(p, 0, n); + return p; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) + #define DEFINE_MUTEX(x) DECLARE_MUTEX(x) + #define mutex_init(x) init_MUTEX(x) + #define mutex_lock(x) down(x) + #define mutex_lock_interruptible(x) down_interruptible(x) + #define mutex_trylock(x) down_trylock(x) + #define mutex_unlock(x) up(x) +#else + #include +#endif + + +static struct fxo_mode { + char *name; + int ohs; + int ohs2; + int rz; + int rt; + int ilim; + int dcv; + int mini; + int acim; + int ring_osc; + int ring_x; +} fxo_modes[] = +{ + { "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, /* US, Canada */ + { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, }, + /* Austria, Belgium, Denmark, Finland, France, Germany, + Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, + Norway, Portugal, Spain, Sweden, Switzerland, and UK */ + { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, }, + { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, }, + { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, + { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, }, + { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, /* Current loop >= 20ma */ + { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, }, + { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, }, + { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, }, + { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, }, + { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, }, + { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, }, + { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, }, + { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, }, +}; + +static struct ps_country_reg { + const char *country; + unsigned short value; +} ps_country_regs[] = { + {"ARGENTINA", 0x8}, + {"AUSTRALIA", 0xD}, + {"AUSTRIA", 0xD}, + {"BAHRAIN", 0xC}, + {"BELGIUM", 0xC}, + {"BRAZIL", 0x8}, + {"BULGARIA", 0xD}, + {"CANADA", 0x8}, + {"CHILE", 0x8}, + {"CHINA", 0xC}, + {"COLOMBIA", 0x8}, + {"CROATIA", 0xC}, + {"CYPRUS", 0xC}, + {"CZECH", 0xC}, + {"DENMARK", 0xC}, + {"ECUADOR", 0x8}, + {"EGYPT", 0x8}, + {"ELSALVADOR", 0x8}, + {"FINLAND", 0xC}, + {"FRANCE", 0xC}, + {"GERMANY", 0xD}, + {"GREECE", 0xC}, + {"GUAM", 0x8}, + {"HONGKONG", 0x8}, + {"HUNGARY", 0x8}, + {"ICELAND", 0xC}, + {"INDIA", 0xF}, + {"INDONESIA", 0x8}, + {"IRELAND", 0xC}, + {"ISRAEL", 0xC}, + {"ITALY", 0xC}, + {"JAPAN", 0x8}, + {"JORDAN", 0x8}, + {"KAZAKHSTAN", 0x8}, + {"KUWAIT", 0x8}, + {"LATVIA", 0xC}, + {"LEBANON", 0xC}, + {"LUXEMBOURG", 0xC}, + {"MACAO", 0x8}, + {"MALAYSIA", 0x8}, + {"MALTA", 0xC}, + {"MEXICO", 0x8}, + {"MOROCCO", 0xC}, + {"NETHERLANDS",0xC}, + {"NEWZEALAND", 0xF}, + {"NIGERIA", 0xC}, + {"NORWAY", 0xC}, + {"OMAN", 0x8}, + {"PAKISTAN", 0x8}, + {"PERU", 0x8}, + {"PHILIPPINES",0x8}, + {"POLAND", 0x8}, + {"PORTUGAL", 0xC}, + {"ROMANIA", 0x8}, + {"RUSSIA", 0x8}, + {"SAUDIARABIA",0x8}, + {"SINGAPORE", 0x8}, + {"SLOVAKIA", 0xE}, + {"SLOVENIA", 0xE}, + {"SOUTHAFRICA",0xE}, + {"SOUTHKOREA", 0x8}, + {"SPAIN", 0xC}, + {"SWEDEN", 0xC}, + {"SWITZERLAND",0xC}, + {"SYRIA", 0x8}, + {"TAIWAN", 0x8}, + {"THAILAND", 0x8}, + {"UAE", 0x8}, + {"UK", 0xC}, + {"USA", 0x8}, + {"YEMEN", 0x8} +}; + +#define INOUT 2 + +/* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 32 bits. Allocate an extra set just for control too */ +#define VT_PCIDMA_BLOCKSIZE (DAHDI_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) +#define VT_PCIDMA_MIDDLE (DAHDI_MAX_CHUNKSIZE * MAX_PORTS - 4) +#define VT_PCIDMA_END (DAHDI_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4) + +#define ID_DATA_MAXSIZE 30 + +#define NUM_CAL_REGS 12 +#define NUM_FXO_REGS 60 + +#define TREG(addr) (wc->ioaddr + addr) + +#define TJ_CNTL TREG(0x00) +#define TJ_OPER TREG(0x01) +#define TJ_AUXC TREG(0x02) +#define TJ_AUXD TREG(0x03) +#define TJ_MASK0 TREG(0x04) +#define TJ_MASK1 TREG(0x05) +#define TJ_INTSTAT TREG(0x06) +#define TJ_AUXR TREG(0x07) + +#define TJ_DMAWS TREG(0x08) +#define TJ_DMAWI TREG(0x0c) +#define TJ_DMAWE TREG(0x10) +#define TJ_DMAWC TREG(0x14) +#define TJ_DMARS TREG(0x18) +#define TJ_DMARI TREG(0x1c) +#define TJ_DMARE TREG(0x20) +#define TJ_DMARC TREG(0x24) + +#define TJ_AUXINTPOL TREG(0x2A) + +#define TJ_AUXFUNC TREG(0x2b) +#define TJ_SFDELAY TREG(0x2c) +#define TJ_SERCTL TREG(0x2d) +#define TJ_SFLC TREG(0x2e) +#define TJ_FSCDELAY TREG(0x2f) + +#define TJ_REGBASE TREG(0xc0) + +#define PIB(addr) (TJ_REGBASE + addr * 4) + +#define HTXF_READY (inb(PIB(0)) & 0x10) +#define HRXF_READY (inb(PIB(0)) & 0x20) + + +#define VT_PORT_EMPTY 0 +#define VT_PORT_VDAA 1 /* Voice DAA - FXO */ +#define VT_PORT_PROSLIC 2 /* ProSLIC - FXS */ + +#define VBAT 0xC7 + +#define HKMODE_FWDACT 1 +#define HKMODE_FWDONACT 2 +#define HKMODE_RINGING 4 + +#define HOOK_ONHOOK 0 +#define HOOK_OFFHOOK 1 + +#define DSP_CODEC_RING 12 /* RING rising edge detected */ +#define DSP_CODEC_HKOFF 22 /* station port off hook */ +#define DSP_CODEC_HKON 23 /* station port on hook */ +#define DSP_RING_OFF 24 /* RING falling edge detected */ +#define DSP_DROP 25 + +#define DSP_CODEC_FLASH 26 /* station port hook flash */ + +#define DSP_LOOP_OFFHOOK 38 /* Loop Off hook from OpenPCI */ +#define DSP_LOOP_ONHOOK 39 /* Loop On hook from OpenPCI */ +#define DSP_LOOP_POLARITY 40 /* Loop Polarity from OpenPCI */ +#define DSP_LOOP_NOBATT 41 + +#define DSP_PROSLIC_SANITY 50 /* Sanity alert from a ProSLIC port */ +#define DSP_PROSLIC_PWR_ALARM 51 /* Power Alarm from a ProSLIC port */ +#define DSP_VDAA_ISO_FRAME_E 52 /* ISO-cap frame sync lost on VDAA port*/ + +#if VERBOSE_TIMING + #define REPORT_WAIT(n,x) \ + cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x ) +#else + #define REPORT_WAIT(n,x) +#endif + +#define BUSY_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \ + return failret; \ + } \ + } \ + REPORT_WAIT(busy,i) + +#define LOCKED_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__); \ + spin_unlock_irqrestore(&wc->lock, flags); \ + return failret; \ + } \ + } \ + REPORT_WAIT(locked,i) + +#define HTXF_WAIT() BUSY_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT() BUSY_WAIT(i,!HRXF_READY,5,70000,RET_FAIL) +#define HTXF_WAIT_RET(failret) BUSY_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_RET(failret) BUSY_WAIT(i,!HRXF_READY,5,1000,failret) + +#define HTXF_WAIT_LOCKED() LOCKED_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT_LOCKED() LOCKED_WAIT(i,!HRXF_READY,5,1000,RET_FAIL) +#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,1000,failret) + + +struct openpci { + struct pci_dev *dev; + char *variety; + int boardnum; + int portcount; + int porttype[MAX_PORTS]; + + int firmware; + char serial[ID_DATA_MAXSIZE]; + + spinlock_t lock; + + //XXX Replace these with proper try_module_get locking in the dahdi driver. + //int usecount; //XXX + //int dead; //XXX + union { + struct { + int offhook; + } fxo; + struct { + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + } fxs; + } mod[MAX_PORTS]; + + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + + struct dahdi_chan _chans[MAX_PORTS]; + struct dahdi_chan *chans[MAX_PORTS]; + struct dahdi_span span; +} *cards[MAX_CARDS]; + +// You must hold this lock anytime you access or modify the cards[] array. +DEFINE_MUTEX(cards_mutex); + +static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c}; +static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13}; +static char wcopenpci[] = "Voicetronix OpenPCI"; + +static char *country = DEFAULT_COUNTRY; +static int reversepolarity; // = 0 +static int debug; // = 0 + +module_param(country, charp, 0444); +module_param(debug, int, 0600); +module_param(reversepolarity, int, 0600); +MODULE_PARM_DESC(country, "Set the default country name"); +MODULE_PARM_DESC(debug, "Enable verbose logging"); + +//#define DEBUG_LOOP_VOLTAGE 1 +#ifdef DEBUG_LOOP_VOLTAGE + // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum + // will enable voltage monitoring on that port (fxo only presently) + static int voltmeter; // = 0 + module_param(voltmeter, int, 0600); + MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering"); +#endif + + +/* boolean return values */ +#define RET_OK 1 +#define RET_FAIL 0 + +/* Convenience macros for logging */ +#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__) +#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__) +#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__) +#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__) +#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__) + + +static inline const char *porttype(struct openpci *wc, int port) +{ //{{{ + switch( wc->porttype[port] ) { + case VT_PORT_VDAA: return "VDAA"; + case VT_PORT_PROSLIC: return "ProSLIC"; + case VT_PORT_EMPTY: return "empty port"; + default: return "unknown type"; + } +} //}}} + + +static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x11, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x13, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxs(wc, port, reg, value) ) { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + outb(0x10, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + outb(0x12, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxs(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __wait_indreg_fxs(struct openpci *wc, int port) +{ //{{{ + unsigned char value; + int count = 100; + + while (--count) + { + if( __read_reg_fxs(wc, port, I_STATUS, &value) ){ + if( value == 0 ) + return RET_OK; + } else { + cardcrit(wc->boardnum, + "failed to read port %d PS_IND_ADDR_ST, retrying...", + port); + } + udelay(5); + } + cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port); + return RET_FAIL; +} //}}} + +static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IDA_LO, value & 0xff) + && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) ) + { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port); + return RET_FAIL; +} //}}} + +static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value) +{ //{{{ + unsigned long flags; + unsigned char lo, hi; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) + && __read_reg_fxs(wc, port, IDA_LO, &lo) + && __read_reg_fxs(wc, port, IDA_HI, &hi) ) + { + *value = lo | hi << 8; + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static void start_dma(struct openpci *wc) +{ //{{{ + outb(0x0f, TJ_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +static void restart_dma(struct openpci *wc) +{ //{{{ + /* Reset Master and TDM */ + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +/* You must hold the card spinlock to call this function */ +static int __ping_arm(struct openpci *wc) +{ //{{{ + int i; + int pong=0; + + while(pong != 0x02){ + outb(0x02, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x", pong); + } + while(pong == 0x02){ + // Poke no-ops into the arm while it is still returning data, + // if 500 usec elapses with no further response from it then + // the message queue is should be completely cleared. + outb(0x00, PIB(1)); HTXF_WAIT(); + i = 100; + while( !HRXF_READY && --i ) udelay(5); + if( i == 0 ) break; + pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x.", pong); + } + return RET_OK; +} //}}} + +static void arm_event(struct openpci *wc, char *msg) +{ //{{{ + int port = msg[0]; + + switch(msg[1]){ + case DSP_LOOP_OFFHOOK: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Loop OffHook", port); + break; + + case DSP_LOOP_ONHOOK: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Loop OnHook", port); + break; + + case DSP_LOOP_POLARITY: + dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_POLARITY); + dbginfo(wc->boardnum, "Port %d Loop Polarity", port); + break; + + case DSP_CODEC_RING: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_RING); + dbginfo(wc->boardnum, "Port %d Ring On", port); + break; + + case DSP_RING_OFF: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Ring Off", port); + break; + + case DSP_CODEC_HKOFF: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Station OffHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + break; + + case DSP_CODEC_HKON: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Station OnHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 6; + else + wc->mod[port].fxs.idletxhookstate = 2; + break; + + case DSP_CODEC_FLASH: + dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_WINKFLASH); + dbginfo(wc->boardnum, "Port %d Station Flash", port); + break; + + case DSP_DROP: + case DSP_LOOP_NOBATT: + break; + + //XXX What to do to recover from these? + case DSP_PROSLIC_SANITY: + dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port); + break; + + case DSP_PROSLIC_PWR_ALARM: + { + char errbuf[32] = " Unknown", *p = errbuf; + int i = 49; + + msg[2] >>= 2; + for(; i < 55; ++i, msg[2] >>= 1 ) + if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; } + if( p != errbuf ) *p = '\0'; + cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf); + //write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook ); + return; + } + + case DSP_VDAA_ISO_FRAME_E: + dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port); + break; + + default: + cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d", + msg[1], port); + break; + } +} //}}} + +/* You must hold the card spinlock to call this function */ +static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg ) +{ //{{{ + int i; + + HRXF_WAIT(); *msg = inb(PIB(1)); + return RET_OK; +} //}}} + +static inline int read_arm_msg( struct openpci *wc, unsigned char *msg ) +{ //{{{ + unsigned long flags; + int i, d, count; + int ret = RET_OK; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x08, PIB(1)); HTXF_WAIT_LOCKED(); + //XXX Do we need to clear the interrupt flag even if this fails? + HRXF_WAIT_LOCKED(); count = inb(PIB(1)); + if( count == 0 ){ + ret = RET_FAIL; + } else if( count < 3 || count > 4 ){ + cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count); + // NB: This may take a while (up to 500usec or more) to complete + // and we are in the isr at present when this is called, so + // we may miss an interrupt or two while this is done in the + // bottom half, but we are already in trouble, so... + d = debug; debug = 5; __ping_arm( wc ); debug = d; + ret = RET_FAIL; + } else while( --count ){ + if( ! __read_arm_byte(wc, msg) ){ + cardcrit(wc->boardnum, + "Failed to read arm message %d more bytes expected", + count); + ret = RET_FAIL; + break; + } + ++msg; + } + outb(0x09, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + return ret; +} //}}} + +static void openpci_arm_work( void *cardptr ) +{ //{{{ + struct openpci *wc = (struct openpci*)cardptr; + unsigned char armmsg[4]; + + if( read_arm_msg(wc, armmsg) ) arm_event(wc, armmsg); +} //}}} + + +static inline void openpci_write(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *writechunk; + + if (flags & 0x01) + writechunk = wc->writechunk; + else if (flags & 0x02) + writechunk = wc->writechunk + DAHDI_CHUNKSIZE*2; + else { + cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", + flags, inb(TJ_DMAWC) ); + return; + } + /* get data */ + dahdi_transmit(&wc->span); + for (y=0,x=0;xporttype[4]) + writechunk[y] |= (wc->chans[4]->writechunk[x] << 24); + else + writechunk[y] |= (0x01 << 24); + if (wc->porttype[5]) + writechunk[y] |= (wc->chans[5]->writechunk[x] << 16); + if (wc->porttype[6]) + writechunk[y] |= (wc->chans[6]->writechunk[x] << 8); + if (wc->porttype[7]) + writechunk[y] |= (wc->chans[7]->writechunk[x]); + ++y; + + /* transmit first 4 ports */ + writechunk[y]=0x01000000; + /* Make sure first port doesnt equal 0x00 */ + if (wc->porttype[0]){ + if (wc->chans[0]->writechunk[x] == 0) + writechunk[y] |= (0x01 << 24); + else + writechunk[y] |= (wc->chans[0]->writechunk[x] << 24); + } + //else writechunk[y] |= (0x00 << 24); + if (wc->porttype[1]) + writechunk[y] |= (wc->chans[1]->writechunk[x] << 16); + if (wc->porttype[2]) + writechunk[y] |= (wc->chans[2]->writechunk[x] << 8); + if (wc->porttype[3]) + writechunk[y] |= (wc->chans[3]->writechunk[x]); + ++y; +#endif + } +} //}}} + +static inline void openpci_read(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *readchunk; + + if (flags & 0x08) + readchunk = wc->readchunk + DAHDI_CHUNKSIZE*2; + else if (flags & 0x04) + readchunk = wc->readchunk; + else { + cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x", + flags, inb(TJ_DMARC)); + return; + } + + for (y=0,x=0;xporttype[0]) + wc->chans[0]->readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[1]) + wc->chans[1]->readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[2]) + wc->chans[2]->readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[3]) + wc->chans[3]->readchunk[x] = (readchunk[y]) & 0xff; + ++y; + /* Receive second 4 ports */ + if (wc->porttype[4]) + wc->chans[4]->readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[5]) + wc->chans[5]->readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[6]) + wc->chans[6]->readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[7]) + wc->chans[7]->readchunk[x] = (readchunk[y]) & 0xff; + ++y; +#endif + } + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < MAX_PORTS; x++) { + if (wc->porttype[x]) + dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + } + dahdi_receive(&wc->span); +} //}}} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static irqreturn_t openpci_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +static irqreturn_t openpci_isr(int irq, void *dev_id) +#endif +{ //{{{ + struct openpci *wc = dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&wc->lock, flags); + status = inb(TJ_INTSTAT); + outb(status, TJ_INTSTAT); + + if (!status) { + if(inb(TJ_AUXR) & 0x02) { + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_NONE; + } + spin_unlock_irqrestore(&wc->lock, flags); + openpci_arm_work(wc); + return IRQ_HANDLED; + } + if (status & 0x10){ + /* PCI Master abort */ + cardcrit(wc->boardnum, "PCI Master Abort."); + /* Stop DMA, wait for watchdog */ + outb(0x00, TJ_OPER); + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&wc->lock, flags); + + if (status & 0x20){ + /* PCI Target abort */ + cardcrit(wc->boardnum, "PCI Target Abort."); + return IRQ_HANDLED; + } + if (status & 0x03){ + openpci_write(wc, status); + } + if (status & 0x0c){ + #ifdef DEBUG_LOOP_VOLTAGE + //{{{ + static int counter[MAX_CARDS]; + int card = wc->boardnum; + int port = ++counter[card] & 0x07; + int ignore = counter[card] & 0xf0; + + if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) { + unsigned char lv; + if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) ) + cardinfo(wc->boardnum, "Port %d loop voltage %d", + port, lv < 128 ? lv : lv - 256); + } + //}}} + #endif + openpci_read(wc, status); + } + + return IRQ_HANDLED; +} //}}} + +static int openpci_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ //{{{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int x; + + switch (cmd) { + case DAHDI_ONHOOKTRANSFER: + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + if (get_user(x, (int *)data)) + return -EFAULT; + wc->mod[port].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[port].fxs.idletxhookstate = 0x2; + switch(wc->mod[port].fxs.lasttxhook) { + case 0x1: + case 0x5: + if (reversepolarity) + wc->mod[port].fxs.lasttxhook = 0x6; + else + wc->mod[port].fxs.lasttxhook = 0x2; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + } + break; + case DAHDI_SETPOLARITY: + if (get_user(x, (int *)data)) + return -EFAULT; + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[port].fxs.lasttxhook == 0x04) || + (wc->mod[port].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[port].fxs.lasttxhook |= 0x04; + else + wc->mod[port].fxs.lasttxhook &= ~0x04; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + break; + case WCTDM_GET_STATS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + unsigned char linevolt; + if( read_reg_fxs(wc, port, 80, &linevolt) ) + stats.tipvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 81, &linevolt) ) + stats.ringvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 82, &linevolt) ) + stats.batvolt = linevolt * -376; + else + return -EIO; + } else if (wc->porttype[port] == VT_PORT_VDAA) { + unsigned char linevolt; + if( read_reg_fxo(wc, port, 29, &linevolt) ) + stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000; + else + return -EIO; + } else + return -EINVAL; + if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + for (x=0;xporttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + printk("Setting indirect %d to 0x%04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + regop.val &= 0xff; + printk("Setting direct %d to %04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if( ! write_reg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + if( ! write_reg_fxo(wc, port, regop.reg, regop.val) ) + return -EIO; + } + } + break; + case WCTDM_SET_ECHOTUNE: + cardinfo(wc->boardnum, "Setting echo registers"); + if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->porttype[port] == VT_PORT_VDAA) { + /* Set the ACIM and digital echo canceller registers */ + if( ! write_reg_fxo(wc, port, 30, echoregs.acim) + || ! write_reg_fxo(wc, port, 45, echoregs.coef1) + || ! write_reg_fxo(wc, port, 46, echoregs.coef2) + || ! write_reg_fxo(wc, port, 47, echoregs.coef3) + || ! write_reg_fxo(wc, port, 48, echoregs.coef4) + || ! write_reg_fxo(wc, port, 49, echoregs.coef5) + || ! write_reg_fxo(wc, port, 50, echoregs.coef6) + || ! write_reg_fxo(wc, port, 51, echoregs.coef7) + || ! write_reg_fxo(wc, port, 52, echoregs.coef8) ) + { + cardcrit(wc->boardnum, "Failed to set echo registers"); + return -EIO; + } + break; + } else { + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} //}}} + +static int openpci_open(struct dahdi_chan *chan) +{ + struct openpci *wc = chan->pvt; + if( ! wc->porttype[chan->chanpos-1] ) + return -ENODEV; + + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the dahdi code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + //if( wc->dead ) return -ENODEV; + //wc->usecount++; + try_module_get(THIS_MODULE); //XXX + + return 0; +} + +static int openpci_watchdog(struct dahdi_span *span, int event) +{ + info("TDM: Restarting DMA"); + restart_dma(span->pvt); + return 0; +} + +static int openpci_close(struct dahdi_chan *chan) +{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + + //XXX wc->usecount--; + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the dahdi code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + module_put(THIS_MODULE); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + //XXX if (!wc->usecount && wc->dead) openpci_release(wc); + + return 0; +} + +static int openpci_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) +{ //{{{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int new_hk_state; + + dbginfo(wc->boardnum, "Setting %s port %d hook state %s", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, + txsig == 0 ? "ONHOOK" : + txsig == 1 ? "OFFHOOK" : + txsig == 2 ? "START" : + txsig == 3 ? "KEWL" : "UNKNOWN" ); + + switch(wc->porttype[port]) { + case VT_PORT_VDAA: + switch(txsig) { + case DAHDI_TXSIG_START: + case DAHDI_TXSIG_OFFHOOK: + if( write_reg_fxo(wc, port, 5, 0x9) + && write_reg_fxo(wc, port, 0x20, 0x0) ) + wc->mod[port].fxo.offhook = 1; + else + cardcrit(wc->boardnum, "Failed set fxo off-hook"); + break; + + case DAHDI_TXSIG_ONHOOK: + if( write_reg_fxo(wc, port, 5, 0x8) + && write_reg_fxo(wc, port, 0x20, 0x3) ) + wc->mod[port].fxo.offhook = 0; + else + cardcrit(wc->boardnum, "Failed set fxo on-hook"); + break; + + default: + cardcrit(wc->boardnum, + "Can't set FXO port %d tx state to %d", + port, txsig); + } + break; + + case VT_PORT_PROSLIC: + new_hk_state = wc->mod[port].fxs.lasttxhook; + switch(txsig) { + case DAHDI_TXSIG_ONHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + case DAHDI_SIG_FXOKS: + case DAHDI_SIG_FXOLS: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + case DAHDI_SIG_FXOGS: + new_hk_state = 3; + break; + } + break; + + case DAHDI_TXSIG_OFFHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + new_hk_state = 5; + break; + default: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + } + break; + + case DAHDI_TXSIG_START: + new_hk_state = 4; + break; + + case DAHDI_TXSIG_KEWL: + new_hk_state = 0; + break; + + default: + cardinfo(wc->boardnum, + "Can't set FXS port %d tx state to %d", + port, txsig); + } + dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, wc->mod[port].fxs.lasttxhook, new_hk_state ); + + if (new_hk_state != wc->mod[port].fxs.lasttxhook){ + if( write_reg_fxs(wc, port, 64, new_hk_state) ) + wc->mod[port].fxs.lasttxhook = new_hk_state; + else + cardcrit(wc->boardnum, + "Failed to set port %d fxs hookstate from %d to %d", + port, wc->mod[port].fxs.lasttxhook, new_hk_state); + } + break; + + default: + cardcrit(wc->boardnum, + "Unknown module type %d in openpci_hooksig", + wc->porttype[port] ); + } + return 0; +} //}}} + +static int span_initialize(struct openpci *wc) +{ //{{{ + int x; + + //XXX Set a THIS_MODULE as the owner of the span... + /* Zapata stuff */ + sprintf(wc->span.name, "WCTDM/%d", wc->boardnum); + sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1); + for (x = 0; x < MAX_PORTS; x++) { + struct dahdi_chan *chan = &wc->_chans[x]; + wc->chans[x] = chan; + sprintf(chan->name, "WCTDM/%d/%d", wc->boardnum, x); + chan->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS + | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + chan->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + chan->chanpos = x+1; + chan->pvt = wc; + } + wc->span.deflaw = DAHDI_LAW_MULAW; + wc->span.chans = wc->chans; + wc->span.channels = MAX_PORTS; + wc->span.hooksig = openpci_hooksig; + wc->span.open = openpci_open; + wc->span.close = openpci_close; + wc->span.flags = DAHDI_FLAG_RBS; + wc->span.ioctl = openpci_ioctl; + wc->span.watchdog = openpci_watchdog; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + if (dahdi_register(&wc->span, 0)) { + cardcrit(wc->boardnum, "Unable to register span with dahdi"); + return RET_FAIL; + } + return RET_OK; +} //}}} + +static int get_port_type(struct openpci *wc, int port) +{ //{{{ + int i, type; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1)); + spin_unlock_irqrestore(&wc->lock, flags); + + return type; +} //}}} + +static int check_ports(struct openpci *wc) +{ //{{{ + int i = 0; + + wc->portcount = 0; + for(; i < MAX_PORTS; ++i ){ + wc->porttype[i] = get_port_type(wc, i); + dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i)); + + switch( wc->porttype[i] ) { + case VT_PORT_PROSLIC: + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[i].fxs.idletxhookstate = 5; + else + wc->mod[i].fxs.idletxhookstate = 1; + + case VT_PORT_VDAA: + ++wc->portcount; + } + } + // we 'succeed' if any ports were discovered. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int configure_vdaa_country(struct openpci *wc, int port, char *name) +{ //{{{ + unsigned char value; + int i; + + for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){ + if(!strcmp(fxo_modes[i].name, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + i = 3; + cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name); + + part2: + value = (fxo_modes[i].ohs << 6); + value |= (fxo_modes[i].rz << 1); + value |= (fxo_modes[i].rt << 0); + if( ! write_reg_fxo(wc, port, 16, value) ) goto hell; + + /* DC Termination Control - Register 26 */ + value = (fxo_modes[i].dcv << 6); + value |= (fxo_modes[i].mini << 4); + value |= (fxo_modes[i].ilim << 1); + if( ! write_reg_fxo(wc, port, 26, value) ) goto hell; + + /* AC Termination Control - Register 30 */ + value = (fxo_modes[i].acim << 0); + if( ! write_reg_fxo(wc, port, 30, value) ) goto hell; + + /* DAA Control 5 - Register 31 */ + msleep(1); + if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell; + + value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3); + value = value | 0x02; + if( ! write_reg_fxo(wc, port, 31, value) ) goto hell; + + return RET_OK; + + hell: + cardcrit(wc->boardnum, "port %d failed configure vdaa country", port); + return RET_FAIL; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_vdaa_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + if( configure_vdaa_country(wc, port, country) ) + ++wc->portcount; + else { + cardcrit(wc->boardnum, "FAILED to configure vdaa port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; + } +} //}}} + +static int configure_proslic_country(struct openpci *wc, int port, const char *name) +{ //{{{ + int i; + + for(i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i) { + if(!strcmp(ps_country_regs[i].country, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + return -EINVAL; + + part2: + + if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){ + cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port); + return -EIO; + } + return 0; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_proslic_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + switch( configure_proslic_country(wc, port, country) ){ + case 0: + break; + + case -EINVAL: + cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country); + if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 ) + goto hell; + + default: + goto hell; + } + + ++wc->portcount; + return; + + hell: + cardcrit(wc->boardnum, "FAILED to configure proslic port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; +} //}}} + +// Do not call this from an interrupt context, it may (indirectly) sleep. +static int configure_ports(struct openpci *wc) +{ //{{{ + unsigned long flags; + int i; + + wc->portcount = 0; + for(i=0; i < MAX_PORTS; ++i){ + switch (wc->porttype[i]){ + case VT_PORT_VDAA: configure_vdaa_port(wc,i); break; + case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break; + } + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0x2c, PIB(1)); HTXF_WAIT_LOCKED(); + outb(0xff, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + + // otherwise we 'succeed' if any ports were configured successfully. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int __get_arm_id(struct openpci *wc, int field, char *value) +{ //{{{ + int i; + int x=0; + int count=0; + + outb(0x01, PIB(1)); HTXF_WAIT(); + outb(field, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); count = inb(PIB(1)); + if (count > ID_DATA_MAXSIZE){ + cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d", + field, count, ID_DATA_MAXSIZE); + return RET_FAIL; + } + //cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count); + for(; x < count; ++x){ + HRXF_WAIT(); *value = inb(PIB(1)); + //cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp); + ++value; + } + return RET_OK; +} //}}} + +static void enable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x3f, TJ_MASK0); + outb(0x02, TJ_MASK1); +} //}}} + +static void disable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x00, TJ_MASK0); + outb(0x00, TJ_MASK1); +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static int check_arm(struct openpci *wc) +{ //{{{ + char model[ID_DATA_MAXSIZE+1] = { 0 }; + char date[ID_DATA_MAXSIZE+1] = { 0 }; + unsigned long flags; + int i=0; + int tmp=0; + + spin_lock_irqsave(&wc->lock, flags); + while ((tmp != 0x88)&&(++i<100)){ + outb(0x88, PIB(0)); + msleep(1); + tmp = inb(PIB(1)); + } + if (i>=1000) goto limbo; + dbginfo(wc->boardnum, "Arm responded on attempt %d",i); + + // Flush out the queue if we sent several pings before a response. + if(i>1) __ping_arm(wc); + + if( ! __get_arm_id(wc, 0, model) ) goto hell; + sscanf(model, "OpenPCI8.%02d", &(wc->firmware)); + cardinfo(wc->boardnum, " model: %s", model); + + if( ! __get_arm_id(wc, 1, date) ) goto hell; + cardinfo(wc->boardnum, " date: %s", date); + + if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell; + cardinfo(wc->boardnum, " serial: %s", wc->serial); + + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + + hell: + spin_unlock_irqrestore(&wc->lock, flags); + cardwarn(wc->boardnum, "Found ARM processor, dumb firmware."); + return RET_OK; + + limbo: + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static int arm_monitor(struct openpci *wc, int on) +{ //{{{ + int i; + outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT(); + return RET_OK; +} //}}} + +static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent) +{ //{{{ + struct openpci *wc; + int boardnum = 0; + int failret = -ENOMEM; + int tmp = 0; + int i; + unsigned long flags; + + if( ent->driver_data != (kernel_ulong_t)&wcopenpci ) + { + info("Probe of non-OpenPCI card, ignoring."); + return -EINVAL; + } + wc = kzalloc(sizeof(struct openpci), GFP_KERNEL); + if (!wc){ + return -ENOMEM; + } + + mutex_lock(&cards_mutex); + for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum); + if (boardnum >= MAX_CARDS){ + crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS); + mutex_unlock(&cards_mutex); + goto hell; + } + cards[boardnum] = wc; + mutex_unlock(&cards_mutex); + + spin_lock_init(&wc->lock); + pci_set_drvdata(pdev, wc); + + wc->boardnum = boardnum; + wc->dev = pdev; + wc->variety = wcopenpci; + + cardinfo(boardnum, "Initialising card"); + if (pci_enable_device(pdev)) { + failret = -EIO; + goto hell_2; + } + wc->ioaddr = pci_resource_start(pdev, 0); + if( ! request_region(wc->ioaddr, 0xff, NAME) ){ + cardcrit(boardnum, "Failed to lock IO region, another driver already using it"); + failret = -EBUSY; + goto hell_2; + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0xff, TJ_AUXD); /* Set up TJ to access the ARM */ + outb(0x78, TJ_AUXC); /* Set up for Jtag */ + outb(0x00, TJ_CNTL); /* pull ERST low */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(1); /* Wait a bit */ + + dbginfo(boardnum,"Starting ARM"); + spin_lock_irqsave(&wc->lock, flags); + outb(0x01, TJ_CNTL); /* pull ERST high again */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(100); /* Give it all a chance to boot */ + + if( ! check_arm(wc) ){ + cardcrit(boardnum, "Couldnt find ARM processor"); + failret = -EIO; + goto hell_3; + } + if( wc->firmware < 11 ){ + cardcrit(boardnum, + "Firmware version %d not supported by this driver", + wc->firmware); + cardcrit(boardnum, " contact Voicetronix to have it updated"); + failret = -ENODEV; + goto hell_3; + } + if( ! check_ports(wc) ){ + cardcrit(boardnum, "Couldnt find ports!"); + failret = -EIO; + goto hell_3; + } + + wc->writechunk = pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma); + if (!wc->writechunk) { + cardcrit(boardnum, "Couldnt get DMA memory."); + goto hell_3; + } + wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); + wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2); + + memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE); + + spin_lock_irqsave(&wc->lock, flags); + outb(0xc1, TJ_SERCTL); + outb(0x0, TJ_FSCDELAY); + + outl(wc->writedma, TJ_DMAWS); + outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI); + outl(wc->writedma + VT_PCIDMA_END, TJ_DMAWE); + outl(wc->readdma, TJ_DMARS); + outl(wc->readdma + VT_PCIDMA_MIDDLE, TJ_DMARI); + outl(wc->readdma + VT_PCIDMA_END, TJ_DMARE); + + /* Clear interrupts */ + outb(0xff, TJ_INTSTAT); + spin_unlock_irqrestore(&wc->lock, flags); + + if( ! arm_monitor(wc, 1) ){ + cardcrit(boardnum, "failed to start arm monitoring"); + failret = -EIO; + goto hell_4; + } + msleep(1000); + + i = 0; + while(tmp != 0x88 && ++i < 1000) { + outb(0x88, PIB(0)); + msleep(250); + tmp = inb(PIB(1)); + } + if(i>=1000) { + cardcrit(boardnum, "FAILED to initialise board"); + goto hell_4; + } + + if( ! check_ports(wc) ) { + cardcrit(boardnum, "FAILED to initialise ports"); + failret = -EIO; + goto hell_4; + } + if( ! configure_ports(wc) ){ + cardcrit(boardnum, "Failed to configure ports."); + failret = -EIO; + goto hell_4; + } + cardinfo(wc->boardnum, "have %d configured ports", wc->portcount); + + if( ! span_initialize(wc) ) { + cardcrit(boardnum, "Failed to register with dahdi driver"); + failret = -EFAULT; + goto hell_4; + } + + /* Finalize signalling */ + for (i=0; i < MAX_PORTS; ++i) { + if (wc->porttype[i] == VT_PORT_VDAA) + wc->chans[i]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS + | DAHDI_SIG_CLEAR | DAHDI_SIG_SF; + else if (wc->porttype[i] == VT_PORT_PROSLIC) + wc->chans[i]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS + | DAHDI_SIG_FXOGS | DAHDI_SIG_SF + | DAHDI_SIG_CLEAR | DAHDI_SIG_EM; + else if (wc->porttype[i]) + cardcrit(wc->boardnum, "Port %d has unknown type (%d)", + i, wc->porttype[i]); + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + if (request_irq(pdev->irq, openpci_isr, DAHDI_IRQ_SHARED, NAME, wc)) { + cardcrit(boardnum, "Cant get IRQ!"); + failret = -EIO; + goto hell_5; + } + cardinfo(boardnum, "Got IRQ %d", pdev->irq); + + enable_interrupts(wc); + start_dma(wc); + + cardinfo(boardnum,"Initialised card."); + return 0; + + hell_5: + dahdi_unregister(&wc->span); + hell_4: + if (wc->writechunk){ + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, + (void*)wc->writechunk, wc->writedma); + } + hell_3: + outb(0x00, TJ_CNTL); + release_region(wc->ioaddr, 0xff); + hell_2: + cards[boardnum] = NULL; + hell: + kfree(wc); + return failret; +} //}}} + +static void __devexit openpci_remove_board(struct pci_dev *pdev) +{ //{{{ + struct openpci *wc = pci_get_drvdata(pdev); + + if(!wc) return; + + arm_monitor(wc,0); + + /* Stop DMA */ + outb(0x00, TJ_OPER); + disable_interrupts(wc); + + //XXX Replace this usecount business... + // and do this BEFORE we invalidate everything above... + // check that we wont try to write to it in the meantime. + /* Release span, possibly delayed */ + //XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1; + + dahdi_unregister(&wc->span); + outb(0x00, TJ_CNTL); + + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + release_region(wc->ioaddr, 0xff); + + mutex_lock(&cards_mutex); + cards[wc->boardnum] = NULL; + mutex_unlock(&cards_mutex); + + kfree(wc); + cardinfo(wc->boardnum, "Removed OpenPCI card."); +} //}}} + +static struct pci_device_id openpci_pci_tbl[] = { + { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, openpci_pci_tbl); + +static struct pci_driver openpci_driver = { + name: NAME, + probe: openpci_probe_board, + remove: __devexit_p(openpci_remove_board), + suspend: NULL, + resume: NULL, + id_table: openpci_pci_tbl, +}; + +static int __init openpci_init(void) +{ + if( dahdi_pci_module(&openpci_driver) ) + return -ENODEV; + + info("Module loaded %s", debug ? "with debug enabled" : ""); + return 0; +} + +static void __exit openpci_cleanup(void) +{ + pci_unregister_driver(&openpci_driver); + info("Module exit"); +} + +module_init(openpci_init); +module_exit(openpci_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_VERSION(DAHDI_VERSION); +MODULE_LICENSE("GPL"); + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/zaphfc.h +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/zaphfc.h @@ -0,0 +1,290 @@ +/* + * zaphfc.h - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * kernel module based on HFC PCI ISDN4Linux and Zaptel drivers + * + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +/* HFC register addresses - accessed using memory mapped I/O */ +/* For a list, see datasheet section 3.2.1 at page 21 */ + +#define hfc_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define hfc_inb(a,b) (readb((a)->pci_io+(b))) + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_CLTIMER 0x80 +#define hfc_CTMT_TIM3_125 0x04 +#define hfc_CTMT_TIM25 0x10 +#define hfc_CTMT_TIM50 0x14 +#define hfc_CTMT_TIM400 0x18 +#define hfc_CTMT_TIM800 0x1C +#define hfc_CTMT_AUTO_TIMER 0x20 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_shift 0 +#define hfc_CONNECT_B2_shift 3 + +#define hfc_CONNECT_HFC_from_ST 0x0 +#define hfc_CONNECT_HFC_from_GCI 0x1 +#define hfc_CONNECT_ST_from_HFC 0x0 +#define hfc_CONNECT_ST_from_GCI 0x2 +#define hfc_CONNECT_GCI_from_HFC 0x0 +#define hfc_CONNECT_GCI_from_ST 0x4 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FMASK 0x000f +#define hfc_ZMASK 0x01ff +#define hfc_ZMASKB 0x1fff + +#define hfc_D_FIFO_SIZE 0x0200 +#define hfc_B_SUB_VAL 0x0200 +#define hfc_B_FIFO_SIZE 0x1E00 +#define hfc_MAX_DFRAMES 0x000f + +#define hfc_FIFO_DTX_Z1 0x2080 +#define hfc_FIFO_DTX_Z2 0x2082 +#define hfc_FIFO_DTX_F1 0x20a0 +#define hfc_FIFO_DTX_F2 0x20a1 +#define hfc_FIFO_DTX 0x0000 +#define hfc_FIFO_DTX_ZOFF 0x000 + +#define hfc_FIFO_DRX_Z1 0x6080 +#define hfc_FIFO_DRX_Z2 0x6082 +#define hfc_FIFO_DRX_F1 0x60a0 +#define hfc_FIFO_DRX_F2 0x60a1 +#define hfc_FIFO_DRX 0x4000 +#define hfc_FIFO_DRX_ZOFF 0x4000 + +#define hfc_FIFO_B1TX_Z1 0x2000 +#define hfc_FIFO_B1TX_Z2 0x2002 +#define hfc_FIFO_B1RX_Z1 0x6000 +#define hfc_FIFO_B1RX_Z2 0x6002 + +#define hfc_FIFO_B1TX_F1 0x2080 +#define hfc_FIFO_B1TX_F2 0x2081 +#define hfc_FIFO_B1RX_F1 0x6080 +#define hfc_FIFO_B1RX_F2 0x6081 + +#define hfc_FIFO_B1RX_ZOFF 0x4000 +#define hfc_FIFO_B1TX_ZOFF 0x0000 + +#define hfc_FIFO_B2TX_Z1 0x2100 +#define hfc_FIFO_B2TX_Z2 0x2102 +#define hfc_FIFO_B2RX_Z1 0x6100 +#define hfc_FIFO_B2RX_Z2 0x6102 + +#define hfc_FIFO_B2TX_F1 0x2180 +#define hfc_FIFO_B2TX_F2 0x2181 +#define hfc_FIFO_B2RX_F1 0x6180 +#define hfc_FIFO_B2RX_F2 0x6181 + +#define hfc_FIFO_B2RX_ZOFF 0x6000 +#define hfc_FIFO_B2TX_ZOFF 0x2000 + +#define hfc_BTRANS_THRESHOLD 128 +#define hfc_BTRANS_THRESMASK 0x00 + +/* Structures */ + +typedef struct hfc_regs { + unsigned char fifo_en; + unsigned char ctmt; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_e; + unsigned char sctrl_r; + unsigned char connect; + unsigned char trm; + unsigned char mst_mode; + unsigned char bswapped; + unsigned char nt_mode; + unsigned char int_drec; +} hfc_regs; + +typedef struct hfc_card { + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; + int clicks; + unsigned char *pci_io; + void *fifomem; // start of the shared mem + volatile void *fifos; // 32k aligned mem for the fifos + struct hfc_regs regs; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + struct dahdi_hfc *ztdev; + int drecinframe; + unsigned char drecbuf[hfc_D_FIFO_SIZE]; + unsigned char dtransbuf[hfc_D_FIFO_SIZE]; + unsigned char brecbuf[2][DAHDI_CHUNKSIZE]; + unsigned char btransbuf[2][DAHDI_CHUNKSIZE]; + unsigned char cardno; + struct hfc_card *next; +} hfc_card; + +typedef struct dahdi_hfc { + unsigned int usecount; + struct dahdi_span span; + struct dahdi_chan chans[3]; + struct dahdi_chan *_chans[3]; + struct hfc_card *card; +} dahdi_hfc; + +/* tune this */ +#define hfc_BCHAN_BUFFER 8 +#define hfc_MAX_CARDS 8 --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/fifo.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/fifo.c @@ -0,0 +1,366 @@ +/* + * fifo.c - HFC FIFO management routines + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include + +#include "../zaptel.h" + +#include "fifo.h" + +static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan, + int z_start, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - z_start + 1; + if (bytes_to_boundary >= size) { + memcpy(data, + chan->z_base + z_start, + size); + } else { + // Buffer wrap + memcpy(data, + chan->z_base + z_start, + bytes_to_boundary); + + memcpy(data + bytes_to_boundary, + chan->fifo_base, + size - bytes_to_boundary); + } +} + +static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1; + if (bytes_to_boundary >= size) { + memcpy(chan->z_base + *Z1_F1(chan), + data, + size); + } else { + // FIFO wrap + + memcpy(chan->z_base + *Z1_F1(chan), + data, + bytes_to_boundary); + + memcpy(chan->fifo_base, + data + bytes_to_boundary, + size - bytes_to_boundary); + } +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int available_bytes; + + // Some useless statistic + chan->bytes += size; + + available_bytes = hfc_fifo_used_rx(chan); + + if (available_bytes < size && !chan->fifo_underrun++) { + // print the warning only once + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to receive!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + return -1; + } + + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size); + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); + return available_bytes - size; +} + +/* +static void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size) +{ + int available_bytes = hfc_fifo_used_rx(chan); + if (available_bytes + 1 < size) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to drop!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + + return; + } + + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); +} +*/ + +void hfc_fifo_put(struct hfc_chan_simplex *chan, + void *data, int size) +{ + struct hfc_card *card = chan->chan->card; + int used_bytes = hfc_fifo_used_tx(chan); + int free_bytes = hfc_fifo_free_tx(chan); + + if (!used_bytes && !chan->fifo_underrun++) { + // print warning only once, to make timing not worse + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO has become empty\n", + card->cardnum, + chan->chan->name); + } + if (free_bytes < size) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO full!\n", + chan->chan->card->cardnum, + chan->chan->name); + chan->fifo_full++; + hfc_clear_fifo_tx(chan); + } + + hfc_fifo_mem_write(chan, data, size); + chan->bytes += size; + *Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size); +} + +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size) +{ + int frame_size; + u16 newz2 ; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange uh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "get_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return -1; + } + + // frame_size includes CRC+CRC+STAT + frame_size = hfc_fifo_get_frame_size(chan); + +#ifdef DEBUG + if(debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + frame_size); + } else if(debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan), + frame_size); + } + + if(debug_level >= 3) { + int i; + for (i=0; i < frame_size; i++) { + printk("%02x", hfc_fifo_u8(chan, + Z_inc(chan, *Z2_F2(chan), i))); + } + + printk("\n"); + } +#endif + + if (frame_size <= 0) { +#ifdef DEBUG + if (debug_level >= 2) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid (empty) frame received.\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + hfc_fifo_drop_frame(chan); + return -1; + } + + // STAT is not really received + chan->bytes += frame_size - 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), frame_size); + + // We cannot use hfc_fifo_get because of different semantic of + // "available bytes" and to avoid useless increment of Z2 + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, + frame_size < max_size ? frame_size : max_size); + + if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan), + frame_size - 1)) != 0x00) { + // CRC not ok, frame broken, skipping +#ifdef DEBUG + if(debug_level >= 2) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "Received frame with wrong CRC\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + chan->crc++; + chan->chan->net_device_stats.rx_errors++; + + hfc_fifo_drop_frame(chan); + return -1; + } + + chan->frames++; + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; + + return frame_size; +} + +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan) +{ + int available_bytes; + u16 newz2; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange eh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "skip_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return; + } + +// chan->drops++; + + available_bytes = hfc_fifo_used_rx(chan) + 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes); + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; +} + +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, + void *data, int size) +{ + u16 newz1; + int available_frames; + +#ifdef DEBUG + if (debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + size); + } else if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan), + size); + } + + if (debug_level >= 3) { + int i; + for (i=0; i= chan->f_num) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO total number of frames exceeded!\n", + chan->chan->card->cardnum, + chan->chan->name); + + chan->fifo_full++; + + return; + } + + hfc_fifo_put(chan, data, size); + + newz1 = *Z1_F1(chan); + + *chan->f1 = F_inc(chan, *chan->f1, 1); + + *Z1_F1(chan) = newz1; + + chan->frames++; +} + +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan) +{ + *chan->f2 = *chan->f1; + *Z2_F2(chan) = *Z1_F2(chan); +} + +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan) +{ + *chan->f1 = *chan->f2; + *Z1_F1(chan) = *Z2_F1(chan); + + if (chan->chan->status == open_voice) { + // Make sure that at least hfc_TX_FIFO_PRELOAD bytes are + // present in the TX FIFOs + + // Create hfc_TX_FIFO_PRELOAD bytes of empty data + // (0x7f is mute audio) + u8 empty_fifo[hfc_TX_FIFO_PRELOAD + ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; + memset(empty_fifo, 0x7f, sizeof(empty_fifo)); + + hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); + } +} + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/vzaphfc_main.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/vzaphfc_main.c @@ -0,0 +1,2037 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2006, headiisue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + * Please read the README file for important infos. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vzaphfc.h" +#include "fifo.h" +#include "lapd.h" + +#if CONFIG_PCI + +#define ZT_B1 0 +#define ZT_B2 1 +#define ZT_D 2 + +#define D 0 +#define B1 1 +#define B2 2 + +static int modes = 0; // all TE +static int nt_modes[hfc_MAX_BOARDS] = { [0 ... (hfc_MAX_BOARDS-1)] = -1 }; +static int force_l1_up = 0; +static int sniff_zaptel_d_channel = 0; +static struct proc_dir_entry *hfc_proc_zaphfc_dir; + +#ifdef DEBUG +int debug_level = 0; +#endif + +static struct pci_device_id hfc_pci_ids[] = { + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, hfc_pci_ids); + +static int __devinit hfc_probe(struct pci_dev *dev + , const struct pci_device_id *ent); +static void __devexit hfc_remove(struct pci_dev *dev); + +struct pci_driver hfc_driver = { + .name = hfc_DRIVER_NAME, + .id_table = hfc_pci_ids, + .probe = hfc_probe, + .remove = hfc_remove, +}; + +/****************************************** + * HW routines + ******************************************/ + +static void hfc_softreset(struct hfc_card *card) +{ + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "resetting\n", + card->cardnum); + + hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); // softreset on + udelay(6); // wait at least 5.21us + hfc_outb(card, hfc_CIRM, 0); // softreset off + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); // wait 20 ms +} + +void hfc_resetCard(struct hfc_card *card) +{ + card->regs.m1 = 0; + hfc_outb(card, hfc_INT_M1, card->regs.m1); // no ints + + card->regs.m2 = 0; + hfc_outb(card, hfc_INT_M2, card->regs.m2); // not at all + + hfc_softreset(card); + + card->regs.trm = 0; + hfc_outb(card, hfc_TRM, card->regs.trm); + + // Select the non-capacitive line mode for the S/T interface */ + card->regs.sctrl = hfc_SCTRL_NONE_CAP; + + if (card->nt_mode) { + // ST-Bit delay for NT-Mode + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT); + + card->regs.sctrl |= hfc_SCTRL_MODE_NT; + } else { + // ST-Bit delay for TE-Mode + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE); + + card->regs.sctrl |= hfc_SCTRL_MODE_TE; + } + + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + + // S/T Auto awake + card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; + hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e); + + // No B-channel enabled at startup + card->regs.sctrl_r = 0; + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + // HFC Master Mode + hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER); + + // Connect internal blocks + card->regs.connect = + hfc_CONNECT_B1_HFC_from_ST | + hfc_CONNECT_B1_ST_from_HFC | + hfc_CONNECT_B1_GCI_from_HFC | + hfc_CONNECT_B2_HFC_from_ST | + hfc_CONNECT_B2_ST_from_HFC | + hfc_CONNECT_B2_GCI_from_HFC; + hfc_outb(card, hfc_CONNECT, card->regs.connect); + + // All bchans are HDLC by default, not useful, actually + // since mode is set during open() + hfc_outb(card, hfc_CTMT, 0); + + // bit order + hfc_outb(card, hfc_CIRM, 0); + + // Enable D-rx FIFO. At least one FIFO must be enabled (by specs) + card->regs.fifo_en = hfc_FIFOEN_DRX; + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); + + card->late_irqs=0; + + // Clear already pending ints + hfc_inb(card, hfc_INT_S1); + hfc_inb(card, hfc_INT_S2); + + // Enable IRQ output + card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER; + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + card->regs.m2 = hfc_M2_IRQ_ENABLE; + hfc_outb(card, hfc_INT_M2, card->regs.m2); + + // Unlocks the states machine + hfc_outb(card, hfc_STATES, 0); + + // There's no need to explicitly activate L1 now. + // Activation is managed inside the interrupt routine. +} + +static void hfc_update_fifo_state(struct hfc_card *card) +{ + // I'm not sure if irqsave is needed but there could be a race + // condition since hfc_update_fifo_state could be called from + // both the IRQ handler and the *_(open|close) functions + + unsigned long flags; + spin_lock_irqsave(&card->chans[B1].lock, flags); + if (!card->fifo_suspended && + (card->chans[B1].status == open_framed || + card->chans[B1].status == open_voice)) { + + if(!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1RX; + hfc_clear_fifo_rx(&card->chans[B1].rx); + } + + if(!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1TX; + hfc_clear_fifo_tx(&card->chans[B1].tx); + } + } else { + if(card->regs.fifo_en & hfc_FIFOEN_B1RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1RX; + if(card->regs.fifo_en & hfc_FIFOEN_B1TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1TX; + } + spin_unlock_irqrestore(&card->chans[B1].lock, flags); + + spin_lock_irqsave(&card->chans[B2].lock, flags); + if (!card->fifo_suspended && + (card->chans[B2].status == open_framed || + card->chans[B2].status == open_voice || + card->chans[B2].status == sniff_aux)) { + + if(!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2RX; + hfc_clear_fifo_rx(&card->chans[B2].rx); + } + + if(!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2TX; + hfc_clear_fifo_tx(&card->chans[B2].tx); + } + } else { + if(card->regs.fifo_en & hfc_FIFOEN_B2RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2RX; + if(card->regs.fifo_en & hfc_FIFOEN_B2TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2TX; + } + spin_unlock_irqrestore(&card->chans[B2].lock, flags); + + spin_lock_irqsave(&card->chans[D].lock, flags); + if (!card->fifo_suspended && + card->chans[D].status == open_framed) { + +// if(!(card->regs.fifo_en & hfc_FIFOEN_DRX)) { +// hfc_clear_fifo_rx(&card->chans[B2].rx); +// +// card->chans[B2].rx.ugly_framebuf_size = 0; +// card->chans[B2].rx.ugly_framebuf_off = 0; +// } +// + if(!(card->regs.fifo_en & hfc_FIFOEN_DTX)) { + card->regs.fifo_en |= hfc_FIFOEN_DTX; + + card->chans[D].tx.ugly_framebuf_size = 0; + card->chans[D].tx.ugly_framebuf_off = 0; + } + } else { +// if(card->regs.fifo_en & hfc_FIFOEN_DRX) +// card->regs.fifo_en &= ~hfc_FIFOEN_DRX; + if(card->regs.fifo_en & hfc_FIFOEN_DTX) + card->regs.fifo_en &= ~hfc_FIFOEN_DTX; + } + spin_unlock_irqrestore(&card->chans[D].lock, flags); + + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); +} + +static inline void hfc_suspend_fifo(struct hfc_card *card) +{ + card->fifo_suspended = TRUE; + + hfc_update_fifo_state(card); + + // When L1 goes down D rx receives garbage; it is nice to + // clear it to avoid a CRC error on reactivation + // udelay is needed because the FIFO deactivation happens + // in 250us + udelay(250); + hfc_clear_fifo_rx(&card->chans[D].rx); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs suspended\n", + card->cardnum); + } +#endif +} + +static inline void hfc_resume_fifo(struct hfc_card *card) +{ + card->fifo_suspended = FALSE; + + hfc_update_fifo_state(card); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs resumed\n", + card->cardnum); + } +#endif +} + +static void hfc_check_l1_up(struct hfc_card *card) +{ + if ((!card->nt_mode && card->l1_state != 7) || + (card->nt_mode && card->l1_state != 3)) { +// 0 because this is quite verbose when an inferface is unconnected, jaw +#if 0 + if(debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "L1 is down, bringing up L1.\n", + card->cardnum); + } +#endif + + hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + } +} + +/****************************************** + * /proc interface functions + ******************************************/ + +struct hfc_status_to_name_names { + enum hfc_chan_status status; + char *name; +}; + +static char *hfc_status_to_name(int status) +{ + struct hfc_status_to_name_names names[] = { + { free, "free" }, + { open_framed, "framed" }, + { open_voice, "voice" }, + { sniff_aux, "sniff aux" }, + { loopback, "loopback" }, + }; + + int i; + + for (i=0; i> 4]; + dst[i*2+1]= hexchars[src[i] & 0xf]; + } + + dst[size*2]='\0'; +} + +static int hfc_proc_read_info(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + int len; + + u8 chip_id; + chip_id = hfc_inb(card, hfc_CHIP_ID); + + len = snprintf(page, PAGE_SIZE, + "Driver : %s\n" + "Cardnum : %d\n" + "IRQ : %d\n" + "PCI Mem : %#08lx (0x%p)\n" + "FIFO Mem : %#08lx (0x%p)\n" + "Mode : %s\n" + "CHIP_ID : %#02x\n" + "L1 State : %c%d\n" + "Sync Lost : %s\n" + "Late IRQs : %d\n" + "FIFO susp : %s\n" + "\nChannel %12s %12s %12s %12s %4s %4s %4s %4s %4s\n" + "D : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + "B1 : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + "B2 : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + ,hfc_DRIVER_STRING + ,card->cardnum + ,card->pcidev->irq + ,card->io_bus_mem, card->io_mem + ,(ulong)card->fifo_bus_mem, card->fifo_mem + ,card->nt_mode?"NT":"TE" + ,chip_id + ,card->nt_mode?'G':'F' + ,card->l1_state + ,card->sync_loss_reported?"YES":"NO" + ,card->late_irqs + ,card->fifo_suspended?"YES":"NO" + + ,"RX Frames","TX Frames","RX Bytes","TX Bytes","RXFF","TXFF","RXU","TXU","CRC" + ,card->chans[D].rx.frames + ,card->chans[D].tx.frames + ,card->chans[D].rx.bytes + ,card->chans[D].tx.bytes + ,card->chans[D].rx.fifo_full + ,card->chans[D].tx.fifo_full + ,card->chans[D].rx.fifo_underrun + ,card->chans[D].tx.fifo_underrun + ,card->chans[D].rx.crc + ,card->chans[D].open_by_netdev ? 'N' : ' ' + ,card->chans[D].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[D].status) + + ,card->chans[B1].rx.frames + ,card->chans[B1].tx.frames + ,card->chans[B1].rx.bytes + ,card->chans[B1].tx.bytes + ,card->chans[B1].rx.fifo_full + ,card->chans[B1].tx.fifo_full + ,card->chans[B1].rx.fifo_underrun + ,card->chans[B1].tx.fifo_underrun + ,card->chans[B1].rx.crc + ,card->chans[B1].open_by_netdev ? 'N' : ' ' + ,card->chans[B1].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[B1].status) + + ,card->chans[B2].rx.frames + ,card->chans[B2].tx.frames + ,card->chans[B2].rx.bytes + ,card->chans[B2].tx.bytes + ,card->chans[B2].rx.fifo_full + ,card->chans[B2].tx.fifo_full + ,card->chans[B2].rx.fifo_underrun + ,card->chans[B2].tx.fifo_underrun + ,card->chans[B2].rx.crc + ,card->chans[B2].open_by_netdev ? 'N' : ' ' + ,card->chans[B2].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[B2].status) + ); + + return len; +} + +static int hfc_proc_read_fifos(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + + int len; + len = snprintf(page, PAGE_SIZE, + " Receive Transmit\n" + "Channel F1 F2 Z1 Z2 Used F1 F2 Z1 Z2 Used\n" + "D : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + "B1 : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + "B2 : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + ,*card->chans[D].rx.f1 + ,*card->chans[D].rx.f2 + ,*Z1_F2(&card->chans[D].rx) + ,*Z2_F2(&card->chans[D].rx) + ,hfc_fifo_used_rx(&card->chans[D].rx) + ,*card->chans[D].tx.f1 + ,*card->chans[D].tx.f2 + ,*Z1_F1(&card->chans[D].tx) + ,*Z2_F1(&card->chans[D].tx) + ,hfc_fifo_used_tx(&card->chans[D].tx) + + ,*card->chans[B1].rx.f1 + ,*card->chans[B1].rx.f2 + ,*Z1_F2(&card->chans[B1].rx) + ,*Z2_F2(&card->chans[B1].rx) + ,hfc_fifo_used_rx(&card->chans[B1].rx) + ,*card->chans[B1].tx.f1 + ,*card->chans[B1].tx.f2 + ,*Z1_F1(&card->chans[B1].tx) + ,*Z2_F1(&card->chans[B1].tx) + ,hfc_fifo_used_tx(&card->chans[B1].tx) + + ,*card->chans[B2].rx.f1 + ,*card->chans[B2].rx.f2 + ,*Z1_F2(&card->chans[B2].rx) + ,*Z2_F2(&card->chans[B2].rx) + ,hfc_fifo_used_rx(&card->chans[B2].rx) + ,*card->chans[B2].tx.f1 + ,*card->chans[B2].tx.f2 + ,*Z1_F1(&card->chans[B2].tx) + ,*Z2_F1(&card->chans[B2].tx) + ,hfc_fifo_used_tx(&card->chans[B2].tx) + ); + + return len; +} + +static int hfc_proc_read_bufs(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + int len; + + char hex_rchunk[3][ZT_CHUNKSIZE * 2 + 1]; + char hex_wchunk[3][ZT_CHUNKSIZE * 2 + 1]; + + hfc_hexdump(card->chans[D].rx.zaptel_buffer, hex_rchunk[0], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[D].tx.zaptel_buffer, hex_wchunk[0], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B1].rx.zaptel_buffer, hex_rchunk[1], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B1].tx.zaptel_buffer, hex_wchunk[1], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B2].rx.zaptel_buffer, hex_rchunk[2], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B2].tx.zaptel_buffer, hex_wchunk[2], ZT_CHUNKSIZE); + + len = snprintf(page, PAGE_SIZE, + "Channel Read Chunk Write Chunk\n" + "D : %16s %16s\n" + "B1 : %16s %16s\n" + "B2 : %16s %16s\n" + ,hex_rchunk[0],hex_wchunk[0] + ,hex_rchunk[1],hex_wchunk[1] + ,hex_rchunk[2],hex_wchunk[2] + ); + + return len; +} + +/****************************************** + * net_device interface functions + ******************************************/ + +static int hfc_open(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + if (chan->status != free && + (chan->number != D || chan->status != open_framed)) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_framed; + chan->open_by_netdev = TRUE; + + spin_unlock(&chan->lock); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s opened.\n", + card->cardnum, + chan->name); + + return 0; +} + +static int hfc_close(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + if (chan->status != open_framed) { + spin_unlock(&chan->lock); + return -EINVAL; + } + + chan->status = free; + chan->open_by_netdev = FALSE; + + spin_unlock(&chan->lock); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s closed.\n", + card->cardnum, + chan->name); + + return 0; +} + +static int hfc_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + netdev->trans_start = jiffies; + + hfc_check_l1_up(card); + + hfc_fifo_put_frame(&chan->tx, skb->data, skb->len); + + // We're not called from IRQ handler, otherwise we'd need + // dev_kfree_skb + dev_kfree_skb(skb); + + return 0; +} + +static struct net_device_stats *hfc_get_stats(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; +// struct hfc_card *card = chan->card; + + return &chan->net_device_stats; +} + +static void hfc_set_multicast_list(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&card->lock); + + if(netdev->flags & IFF_PROMISC && !card->echo_enabled) { + if (card->nt_mode) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d " + "is in NT mode, not going promiscuous\n", + card->cardnum); + + spin_unlock(&card->lock); + return; + } + + if (card->chans[B2].status != free) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s: is busy, not going promiscuous\n", + card->cardnum, + card->chans[B2].name); + + spin_unlock(&card->lock); + return; + } + + card->regs.trm |= hfc_TRM_ECHO; + card->regs.m1 |= hfc_INTS_B2REC; + card->regs.cirm &= ~hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + card->regs.connect |= hfc_CONNECT_B2_ST_from_GCI; + card->regs.ctmt &= ~hfc_CTMT_TRANSB2; + + card->chans[B2].status = sniff_aux; + + card->echo_enabled = TRUE; + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s entered echo mode on channel %s\n", + card->cardnum, + chan->name, + card->chans[B2].name); + + } else if(!(netdev->flags & IFF_PROMISC) && card->echo_enabled) { + if (!card->echo_enabled) { + spin_unlock(&card->lock); + return; + } + + card->regs.trm &= ~hfc_TRM_ECHO; + card->regs.m1 &= ~hfc_INTS_B2REC; + card->regs.cirm |= hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + card->regs.connect = + hfc_CONNECT_B1_HFC_from_ST | + hfc_CONNECT_B1_ST_from_HFC | + hfc_CONNECT_B1_GCI_from_HFC | + hfc_CONNECT_B2_HFC_from_ST | + hfc_CONNECT_B2_ST_from_HFC | + hfc_CONNECT_B2_GCI_from_HFC; + + card->chans[B2].status = free; + + card->echo_enabled = FALSE; + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s left promiscuous mode.\n", + card->cardnum, + chan->name); + } + + spin_unlock(&card->lock); + + hfc_outb(card, hfc_TRM, card->regs.trm); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + hfc_outb(card, hfc_CONNECT, card->regs.connect); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + // Enable appropriate B receive interrupt + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + hfc_update_fifo_state(card); +} + +/****************************************** + * Zaptel interface (zaptel sucks) + ******************************************/ + +static int hfc_zap_open(struct zt_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + switch (chan->number) { + case D: + if (chan->status != free && + chan->status != open_framed) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_framed; + break; + + case B1: + case B2: + if (chan->status != free) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_voice; + break; + } + + chan->open_by_zaptel = TRUE; + +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + // Zaptel is buggy + try_module_get(THIS_MODULE); +#endif + + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + // B1 + card->regs.m2 |= hfc_M2_PROC_TRANS; + card->regs.ctmt |= hfc_CTMT_TRANSB1; // Enable transparent mode + card->regs.cirm |= hfc_CIRM_B1_REV; // Reversed bit order + card->regs.sctrl |= hfc_SCTRL_B1_ENA; // Enable transmission + card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; // Enable reception + break; + + case B2: + // B2 + card->regs.m2 |= hfc_M2_PROC_TRANS; + card->regs.ctmt |= hfc_CTMT_TRANSB2; // Enable transparent mode + card->regs.cirm |= hfc_CIRM_B2_REV; // Reversed bit order + card->regs.sctrl |= hfc_SCTRL_B2_ENA; // Enable transmission + card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; // Enable reception + break; + + } + + // If not already enabled, enable processing transition (8KHz) + // interrupt + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s opened as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_close(struct zt_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "hfc_zap_close called with NULL card\n"); + return -1; + } + + spin_lock(&chan->lock); + + if (chan->status == free) { + spin_unlock(&chan->lock); + return -EINVAL; + } + + chan->status = free; + chan->open_by_zaptel = FALSE; + + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + // B1 + card->regs.ctmt &= ~hfc_CTMT_TRANSB1; + card->regs.cirm &= ~hfc_CIRM_B1_REV; + card->regs.sctrl &= ~hfc_SCTRL_B1_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA; + break; + + case B2: + // B2 + card->regs.ctmt &= ~hfc_CTMT_TRANSB2; + card->regs.cirm &= ~hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + break; + } + + if (card->chans[B1].status == free && + card->chans[B2].status == free) + card->regs.m2 &= ~hfc_M2_PROC_TRANS; + + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + // Zaptel is buggy + module_put(THIS_MODULE); +#endif + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s closed as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_rbsbits(struct zt_chan *chan, int bits) +{ + return 0; +} + +static int hfc_zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + switch(cmd) { + default: + return -ENOTTY; + } + + return 0; +} + +static int hfc_zap_startup(struct zt_span *span) +{ + struct hfc_card *card = span->pvt; + int alreadyrunning; + + if (!card) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "no card for span at startup!\n", + card->cardnum); + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + span->chans[ZT_D].flags &= ~ZT_FLAG_HDLC; + span->chans[ZT_D].flags |= ZT_FLAG_BRIDCHAN; + span->flags |= ZT_FLAG_RUNNING; + } + + return 0; +} + +static int hfc_zap_shutdown(struct zt_span *span) +{ + return 0; +} + +static int hfc_zap_maint(struct zt_span *span, int cmd) +{ + return 0; +} + +static int hfc_zap_chanconfig(struct zt_chan *chan, int sigtype) +{ + return 0; +} + +static int hfc_zap_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + span->lineconfig = lc->lineconfig; + + return 0; +} + +static int hfc_zap_initialize(struct hfc_card *card) +{ + int i; + memset(&card->zt_span, 0x0, sizeof(struct zt_span)); + + snprintf(card->zt_span.name, sizeof(card->zt_span.name), + "ZTHFC%d", card->cardnum + 1); + + snprintf(card->zt_span.desc, sizeof(card->zt_span.desc), + hfc_DRIVER_DESCR " card %d [%s]", + card->cardnum, + card->nt_mode?"NT":"TE"); + + card->zt_span.spanconfig = hfc_zap_spanconfig; + card->zt_span.chanconfig = hfc_zap_chanconfig; + card->zt_span.startup = hfc_zap_startup; + card->zt_span.shutdown = hfc_zap_shutdown; + card->zt_span.maint = hfc_zap_maint; + card->zt_span.rbsbits = hfc_zap_rbsbits; + card->zt_span.open = hfc_zap_open; + card->zt_span.close = hfc_zap_close; + card->zt_span.ioctl = hfc_zap_ioctl; + + card->zt_span.chans = card->zt_chans; + card->zt_span.channels = 3; + card->zt_span.deflaw = ZT_LAW_ALAW; + card->zt_span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; // <--- this is really BS + card->zt_span.offset = 0; + init_waitqueue_head(&card->zt_span.maintq); + card->zt_span.pvt = card; + + for (i = 0; i < card->zt_span.channels; i++) { + memset(&card->zt_chans[i], 0x0, sizeof(struct zt_chan)); + + snprintf(card->zt_chans[i].name, sizeof(card->zt_chans[i].name), + "ZTHFC%d/%d/%d", + card->cardnum + 1, 0, i + 1); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "registered %s\n", + card->cardnum, + card->zt_chans[i].name); + + card->zt_chans[i].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | + ZT_SIG_CAS | ZT_SIG_SF; + + card->zt_chans[i].chanpos = i + 1; + } + + card->zt_chans[ZT_D].readchunk = card->chans[D].rx.zaptel_buffer; + card->zt_chans[ZT_D].writechunk = card->chans[D].tx.zaptel_buffer; + card->zt_chans[ZT_D].pvt = &card->chans[D]; + + card->zt_chans[ZT_B1].readchunk = card->chans[B1].rx.zaptel_buffer; + card->zt_chans[ZT_B1].writechunk = card->chans[B1].tx.zaptel_buffer; + card->zt_chans[ZT_B1].pvt = &card->chans[B1]; + + card->zt_chans[ZT_B2].readchunk = card->chans[B2].rx.zaptel_buffer; + card->zt_chans[ZT_B2].writechunk = card->chans[B2].tx.zaptel_buffer; + card->zt_chans[ZT_B2].pvt = &card->chans[B2]; + + if (zt_register(&card->zt_span,0)) { + printk(KERN_CRIT "unable to register zaptel device!\n"); + return -1; + } + + return 0; +} + +static void hfc_zap_transmit(struct hfc_chan_simplex *chan) +{ + hfc_fifo_put(chan, chan->zaptel_buffer, ZT_CHUNKSIZE); +} + +static void hfc_zap_receive(struct hfc_chan_simplex *chan) +{ + hfc_fifo_get(chan, chan->zaptel_buffer, ZT_CHUNKSIZE); +} + +/****************************************** + * Interrupt Handler + ******************************************/ + +static void hfc_handle_timer_interrupt(struct hfc_card *card); +static void hfc_handle_state_interrupt(struct hfc_card *card); +static void hfc_handle_processing_interrupt(struct hfc_card *card); +static void hfc_frame_arrived(struct hfc_chan_duplex *chan); +static void hfc_handle_voice(struct hfc_card *card); + +ZAP_IRQ_HANDLER(hfc_interrupt) +{ + struct hfc_card *card = dev_id; + unsigned long flags; + u8 status,s1,s2; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "spurious interrupt (IRQ %d)\n", + irq); + return IRQ_NONE; + } + + spin_lock_irqsave(&card->lock, flags); + status = hfc_inb(card, hfc_STATUS); + if (!(status & hfc_STATUS_ANYINT)) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&card->lock,flags); + return IRQ_NONE; + } + + /* We used to ingore the IRQ when the card was in processing + * state but apparently there is no restriction to access the + * card in such state: + * + * Joerg Ciesielski wrote: + * > There is no restriction for the IRQ handler to access + * > HFC-S PCI during processing phase. A IRQ latency of 375 us + * > is also no problem since there are no interrupt sources in + * > HFC-S PCI which must be handled very fast. + * > Due to its deep fifos the IRQ latency can be several ms with + * > out the risk of loosing data. Even the S/T state interrupts + * > must not be handled with a latency less than <5ms. + * > + * > The processing phase only indicates that HFC-S PCI is + * > processing the Fifos as PCI master so that data is read and + * > written in the 32k memory window. But there is no restriction + * > to access data in the memory window during this time. + * + * // if (status & hfc_STATUS_PCI_PROC) { + * // return IRQ_HANDLED; + * // } + */ + + s1 = hfc_inb(card, hfc_INT_S1); + s2 = hfc_inb(card, hfc_INT_S2); + + if (s1 != 0) { + if (s1 & hfc_INTS_TIMER) { + // timer (bit 7) + hfc_handle_timer_interrupt(card); + } + + if (s1 & hfc_INTS_L1STATE) { + // state machine (bit 6) + hfc_handle_state_interrupt(card); + } + + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) + hfc_frame_arrived(&card->chans[D]); + } + + if (s1 & hfc_INTS_B1REC) { + // B1 chan RX (bit 3) + hfc_frame_arrived(&card->chans[B1]); + } + + if (s1 & hfc_INTS_B2REC) { + // B2 chan RX (bit 4) + hfc_frame_arrived(&card->chans[B2]); + } + + if (s1 & hfc_INTS_DTRANS) { + // D chan TX (bit 2) + } + + if (s1 & hfc_INTS_B1TRANS) { + // B1 chan TX (bit 0) + } + + if (s1 & hfc_INTS_B2TRANS) { + // B2 chan TX (bit 1) + } + + } + + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) + + /* CologneChip says: + * + * the meaning of this fatal error bit is that HFC-S PCI as PCI + * master could not access the PCI bus within 125us to finish its + * data processing. If this happens only very seldom it does not + * cause big problems but of course some B-channel or D-channel + * data will be corrupted due to this event. + * + * Unfortunately this bit is only set once after the problem occurs + * and can only be reseted by a software reset. That means it is not + * easily possible to check how often this fatal error happens. + */ + + if(!card->sync_loss_reported) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "sync lost, pci performance too low!\n", + card->cardnum); + + card->sync_loss_reported = TRUE; + } + } + + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) + } + + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } + + if (s2 & hfc_M2_PROC_TRANS){ + // processing/non-processing transition (bit 0) + hfc_handle_processing_interrupt(card); + } + + } + + spin_unlock_irqrestore(&card->lock,flags); + + return IRQ_HANDLED; +} + +static void hfc_handle_timer_interrupt(struct hfc_card *card) +{ + if(card->ignore_first_timer_interrupt) { + card->ignore_first_timer_interrupt = FALSE; + return; + } + + if ((card->nt_mode && card->l1_state == 3) || + (!card->nt_mode && card->l1_state == 7)) { + + card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + hfc_resume_fifo(card); + } +} + +static void hfc_handle_state_interrupt(struct hfc_card *card) +{ + u8 new_state = hfc_inb(card,hfc_STATES) & hfc_STATES_STATE_MASK; + +#ifdef DEBUG + if (debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "layer 1 state = %c%d\n", + card->cardnum, + card->nt_mode?'G':'F', + new_state); + } +#endif + + if (card->nt_mode) { + // NT mode + + if (new_state == 3) { + // fix to G3 state (see specs) + hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3); + } + + if (new_state == 3 && card->l1_state != 3) { + hfc_resume_fifo(card); + } + + if (new_state != 3 && card->l1_state == 3) { + hfc_suspend_fifo(card); + } + } else { + if (new_state == 3) { + // Keep L1 up... zaptel & libpri expects a always up L1... + // Enable only when using an unpatched libpri + + if (force_l1_up) { + hfc_outb(card, hfc_STATES, + hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + } + } + + if (new_state == 7 && card->l1_state != 7) { + // TE is now active, schedule FIFO activation after + // some time, otherwise the first frames are lost + + card->regs.ctmt |= hfc_CTMT_TIMER_50 | hfc_CTMT_TIMER_CLEAR; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + // Activating the timer firest an interrupt immediately, we + // obviously need to ignore it + card->ignore_first_timer_interrupt = TRUE; + } + + if (new_state != 7 && card->l1_state == 7) { + // TE has become inactive, disable FIFO + hfc_suspend_fifo(card); + } + } + + card->l1_state = new_state; +} + +static void hfc_handle_processing_interrupt(struct hfc_card *card) +{ + int available_bytes=0; + + // Synchronize with the first enabled channel + if(card->regs.fifo_en & hfc_FIFOEN_B1RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx); + if(card->regs.fifo_en & hfc_FIFOEN_B2RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx); + else + available_bytes = -1; + + if ((available_bytes == -1 && card->ticks == 8) || + available_bytes >= ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { + card->ticks = 0; + + if (available_bytes > ZT_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) { + card->late_irqs++; + // we are out of sync, clear fifos, jaw + hfc_clear_fifo_rx(&card->chans[B1].rx); + hfc_clear_fifo_tx(&card->chans[B1].tx); + hfc_clear_fifo_rx(&card->chans[B2].rx); + hfc_clear_fifo_tx(&card->chans[B2].tx); + +#ifdef DEBUG + if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "late IRQ, %d bytes late\n", + card->cardnum, + available_bytes - + (ZT_CHUNKSIZE + + hfc_RX_FIFO_PRELOAD)); + } +#endif + } else { + hfc_handle_voice(card); + } + } + + card->ticks++; +} + +static void hfc_sniff_zaptel_d_channel(struct hfc_card *card) +{ + struct sk_buff *skb = + dev_alloc_skb(card->chans[D].tx.ugly_framebuf_size); + + if (!skb) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "cannot allocate skb: sniffed frame dropped\n", + card->cardnum); + return; + } + + skb->dev = card->chans[D].netdev; + skb->protocol = htons(card->chans[D].protocol); +#ifndef CHECKSUM_HW + skb->ip_summed = CHECKSUM_COMPLETE; +#else + skb->ip_summed = CHECKSUM_HW; +#endif + skb->pkt_type = PACKET_OUTGOING; + + memcpy(skb_put(skb, card->chans[D].tx.ugly_framebuf_size), + card->chans[D].tx.ugly_framebuf, + card->chans[D].tx.ugly_framebuf_size); + + netif_rx(skb); +} + +static void hfc_handle_voice(struct hfc_card *card) +{ + if (card->chans[B1].status != open_voice && + card->chans[B2].status != open_voice) + return; + + card->zt_chans[ZT_D].bytes2transmit = 0; + card->zt_chans[ZT_D].maxbytes2transmit = ZT_CHUNKSIZE; + + zt_transmit(&card->zt_span); + + if (card->regs.fifo_en & hfc_FIFOEN_B1TX) + hfc_zap_transmit(&card->chans[B1].tx); + if (card->regs.fifo_en & hfc_FIFOEN_B2TX) + hfc_zap_transmit(&card->chans[B2].tx); + + if (card->zt_chans[ZT_D].bytes2transmit) { + hfc_check_l1_up(card); + + memcpy(card->chans[D].tx.ugly_framebuf + + card->chans[D].tx.ugly_framebuf_size, + card->chans[D].tx.zaptel_buffer, + card->zt_chans[ZT_D].bytes2transmit); + + card->chans[D].tx.ugly_framebuf_size += + card->zt_chans[ZT_D].bytes2transmit; + + if (card->zt_chans[ZT_D].eoftx) { + hfc_fifo_put_frame(&card->chans[D].tx, + card->chans[D].tx.ugly_framebuf, + card->chans[D].tx.ugly_framebuf_size); + + if (sniff_zaptel_d_channel) + hfc_sniff_zaptel_d_channel(card); + + card->chans[D].tx.ugly_framebuf_size = 0; + card->zt_chans[ZT_D].eoftx = FALSE; + } + } + + if (card->regs.fifo_en & hfc_FIFOEN_B1RX) + hfc_zap_receive(&card->chans[B1].rx); + else + memset(&card->chans[B1].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + if (card->regs.fifo_en & hfc_FIFOEN_B2RX) + hfc_zap_receive(&card->chans[B2].rx); + else + memset(&card->chans[B2].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + // Echo cancellation + zt_ec_chunk(&card->zt_chans[ZT_B1], + card->chans[B1].rx.zaptel_buffer, + card->chans[B1].tx.zaptel_buffer); + zt_ec_chunk(&card->zt_chans[ZT_B2], + card->chans[B2].rx.zaptel_buffer, + card->chans[B2].tx.zaptel_buffer); + + // If there's a frame in the FIFO, read it all and make it + // available to zaptel + if (hfc_fifo_has_frames(&card->chans[D].rx)) { + hfc_frame_arrived(&card->chans[D]); + } + + // Stupid zaptel frame handling + if (!card->chans[D].rx.ugly_framebuf_size) { + // hmm....ok, let zaptel receive nothing + card->zt_chans[ZT_D].bytes2receive = 0; + card->zt_chans[ZT_D].eofrx = FALSE; + } + else if (card->chans[D].rx.ugly_framebuf_size - + card->chans[D].rx.ugly_framebuf_off > ZT_CHUNKSIZE) { + + // the frame is longer than ZT_CHUNKSIZE + memcpy(card->chans[D].rx.zaptel_buffer, + card->chans[D].rx.ugly_framebuf+ + card->chans[D].rx.ugly_framebuf_off, + ZT_CHUNKSIZE); + + card->zt_chans[ZT_D].bytes2receive = ZT_CHUNKSIZE; + card->zt_chans[ZT_D].eofrx = FALSE; + + card->chans[D].rx.ugly_framebuf_off += ZT_CHUNKSIZE; + } else { + // we can read it all + memcpy(card->chans[D].rx.zaptel_buffer, + card->chans[D].rx.ugly_framebuf+ + card->chans[D].rx.ugly_framebuf_off, + card->chans[D].rx.ugly_framebuf_size); + + card->zt_chans[ZT_D].bytes2receive = card->chans[D].rx.ugly_framebuf_size- + card->chans[D].rx.ugly_framebuf_off; + card->zt_chans[ZT_D].eofrx = TRUE; + + card->chans[D].rx.ugly_framebuf_size = 0; + card->chans[D].rx.ugly_framebuf_off = 0; + } + + if (card->zt_span.flags & ZT_FLAG_RUNNING) { + zt_receive(&card->zt_span); + } + +} + +static void hfc_frame_arrived(struct hfc_chan_duplex *chan) +{ + struct hfc_card *card = chan->card; + int antiloop = 16; + struct sk_buff *skb; + + while(hfc_fifo_has_frames(&chan->rx) && --antiloop) { + int frame_size = hfc_fifo_get_frame_size(&chan->rx); + + if (frame_size < 3) { +#ifdef DEBUG + if (debug_level>=2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid frame received, just %d bytes\n", + card->cardnum, + chan->name, + frame_size); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } else if(frame_size == 3) { +#ifdef DEBUG + if (debug_level>=2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "empty frame received\n", + card->cardnum, + chan->name); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } + + if (chan->open_by_zaptel && + card->chans[D].rx.ugly_framebuf_size) { + // We have to wait for zaptel to transmit the + // frame... wait for next time + + break; + } + + skb = dev_alloc_skb(frame_size - 3); + + if (!skb) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "cannot allocate skb: frame dropped\n", + card->cardnum, + chan->name); + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } + + // Oh... this is the echo channel... redirect to D + // channel's netdev + if (card->echo_enabled && chan->number == B2) { + skb->protocol = htons(card->chans[D].protocol); + skb->dev = card->chans[D].netdev; + skb->pkt_type = PACKET_OTHERHOST; + } else { + skb->protocol = htons(chan->protocol); + skb->dev = chan->netdev; + skb->pkt_type = PACKET_HOST; + } + +#ifndef CHECKSUM_HW + skb->ip_summed = CHECKSUM_COMPLETE; +#else + skb->ip_summed = CHECKSUM_HW; +#endif + + if (chan->open_by_zaptel) { + card->chans[D].rx.ugly_framebuf_size = frame_size - 1; + + if (hfc_fifo_get_frame(&card->chans[D].rx, + card->chans[D].rx.ugly_framebuf, + frame_size - 1) == -1) { + dev_kfree_skb(skb); + continue; + } + + memcpy(skb_put(skb, frame_size - 3), + card->chans[D].rx.ugly_framebuf, + frame_size - 3); + } else { + if (hfc_fifo_get_frame(&chan->rx, + skb_put(skb, frame_size - 3), + frame_size - 3) == -1) { + dev_kfree_skb(skb); + continue; + } + } + + chan->net_device_stats.rx_packets++; + chan->net_device_stats.rx_bytes += frame_size - 1; + + netif_rx(skb); + } + + if (!antiloop) + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "Infinite loop detected\n", + card->cardnum); +} + +/****************************************** + * Module initialization and cleanup + ******************************************/ + +static void hfc_setup_lapd(struct hfc_chan_duplex *chan) +{ + chan->netdev->priv = chan; + chan->netdev->open = hfc_open; + chan->netdev->stop = hfc_close; + chan->netdev->hard_start_xmit = hfc_xmit_frame; + chan->netdev->get_stats = hfc_get_stats; + chan->netdev->set_multicast_list = hfc_set_multicast_list; + + memset(chan->netdev->dev_addr, 0x00, sizeof(chan->netdev->dev_addr)); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + SET_MODULE_OWNER(chan->netdev); +#endif +} + +static int __devinit hfc_probe(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + static int cardnum=0; + int err; + int i; + + struct hfc_card *card = NULL; + card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "unable to kmalloc!\n"); + err = -ENOMEM; + goto err_alloc_hfccard; + } + + memset(card, 0x00, sizeof(struct hfc_card)); + card->cardnum = cardnum; + card->pcidev = pci_dev; + spin_lock_init(&card->lock); + + pci_set_drvdata(pci_dev, card); + + if ((err = pci_enable_device(pci_dev))) { + goto err_pci_enable_device; + } + + if ((err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT))) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "No suitable DMA configuration available.\n", + card->cardnum); + goto err_pci_set_dma_mask; + } + + pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY); + + if((err = pci_request_regions(pci_dev, hfc_DRIVER_NAME))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot request I/O memory region\n", + card->cardnum); + goto err_pci_request_regions; + } + + pci_set_master(pci_dev); + + if (!pci_dev->irq) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no irq!\n", + card->cardnum); + err = -ENODEV; + goto err_noirq; + } + + card->io_bus_mem = pci_resource_start(pci_dev,1); + if (!card->io_bus_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no iomem!\n", + card->cardnum); + err = -ENODEV; + goto err_noiobase; + } + + if(!(card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot ioremap I/O memory\n", + card->cardnum); + err = -ENODEV; + goto err_ioremap; + } + + // pci_alloc_consistent guarantees alignment (Documentation/DMA-mapping.txt) + card->fifo_mem = pci_alloc_consistent(pci_dev, hfc_FIFO_SIZE, &card->fifo_bus_mem); + if (!card->fifo_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to allocate FIFO DMA memory!\n", + card->cardnum); + err = -ENOMEM; + goto err_alloc_fifo; + } + + memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE); + + card->fifos = card->fifo_mem; + + pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem); + + if ((err = request_irq(card->pcidev->irq, &hfc_interrupt, + ZAP_IRQ_SHARED, hfc_DRIVER_NAME, card))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to register irq\n", + card->cardnum); + goto err_request_irq; + } + + card->nt_mode = FALSE; + + if (modes & (1 << card->cardnum)) + card->nt_mode = TRUE; + + for (i=0; icardnum) { + card->nt_mode=TRUE; + } + } + +//---------------------------------- D + card->chans[D].card = card; + card->chans[D].name = "D"; + card->chans[D].status = free; + card->chans[D].number = D; + card->chans[D].protocol = ETH_P_LAPD; + spin_lock_init(&card->chans[D].lock); + + card->chans[D].rx.chan = &card->chans[D]; + card->chans[D].rx.fifo_base = card->fifos + 0x4000; + card->chans[D].rx.z_base = card->fifos + 0x4000; + card->chans[D].rx.z1_base = card->fifos + 0x6080; + card->chans[D].rx.z2_base = card->fifos + 0x6082; + card->chans[D].rx.z_min = 0x0000; + card->chans[D].rx.z_max = 0x01FF; + card->chans[D].rx.f_min = 0x10; + card->chans[D].rx.f_max = 0x1F; + card->chans[D].rx.f1 = card->fifos + 0x60a0; + card->chans[D].rx.f2 = card->fifos + 0x60a1; + card->chans[D].rx.fifo_size = card->chans[D].rx.z_max - card->chans[D].rx.z_min + 1; + card->chans[D].rx.f_num = card->chans[D].rx.f_max - card->chans[D].rx.f_min + 1; + + card->chans[D].tx.chan = &card->chans[D]; + card->chans[D].tx.fifo_base = card->fifos + 0x0000; + card->chans[D].tx.z_base = card->fifos + 0x0000; + card->chans[D].tx.z1_base = card->fifos + 0x2080; + card->chans[D].tx.z2_base = card->fifos + 0x2082; + card->chans[D].tx.z_min = 0x0000; + card->chans[D].tx.z_max = 0x01FF; + card->chans[D].tx.f_min = 0x10; + card->chans[D].tx.f_max = 0x1F; + card->chans[D].tx.f1 = card->fifos + 0x20a0; + card->chans[D].tx.f2 = card->fifos + 0x20a1; + card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - card->chans[D].tx.z_min + 1; + card->chans[D].tx.f_num = card->chans[D].tx.f_max - card->chans[D].tx.f_min + 1; + + if(!(card->chans[D].netdev = alloc_netdev(0, "isdn%dd", setup_lapd))) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "net_device alloc failed, abort.\n"); + err = -ENOMEM; + goto err_alloc_netdev_d; + } + + hfc_setup_lapd(&card->chans[D]); + + card->chans[D].netdev->irq = card->pcidev->irq; + card->chans[D].netdev->base_addr = card->io_bus_mem; +/* card->chans[D].netdev->rmem_start = card->fifo_bus_mem + 0x4000; + card->chans[D].netdev->rmem_end = card->fifo_bus_mem + 0x4000 + + card->chans[D].rx.fifo_size - 1;*/ + card->chans[D].netdev->mem_start = card->fifo_bus_mem + 0x0000; + card->chans[D].netdev->mem_end = card->fifo_bus_mem + 0x0000 + + card->chans[D].tx.fifo_size - 1; + + if((err = register_netdev(card->chans[D].netdev))) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "Cannot register net device, aborting.\n", + card->cardnum); + goto err_register_netdev_d; + } + +//---------------------------------- B1 + card->chans[B1].card = card; + card->chans[B1].name = "B1"; + card->chans[B1].status = free; + card->chans[B1].number = B1; + card->chans[B1].protocol = 0; + spin_lock_init(&card->chans[B1].lock); + + card->chans[B1].rx.chan = &card->chans[B1]; + card->chans[B1].rx.fifo_base = card->fifos + 0x4200; + card->chans[B1].rx.z_base = card->fifos + 0x4000; + card->chans[B1].rx.z1_base = card->fifos + 0x6000; + card->chans[B1].rx.z2_base = card->fifos + 0x6002; + card->chans[B1].rx.z_min = 0x0200; + card->chans[B1].rx.z_max = 0x1FFF; + card->chans[B1].rx.f_min = 0x00; + card->chans[B1].rx.f_max = 0x1F; + card->chans[B1].rx.f1 = card->fifos + 0x6080; + card->chans[B1].rx.f2 = card->fifos + 0x6081; + card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - card->chans[B1].rx.z_min + 1; + card->chans[B1].rx.f_num = card->chans[B1].rx.f_max - card->chans[B1].rx.f_min + 1; + + card->chans[B1].tx.chan = &card->chans[B1]; + card->chans[B1].tx.fifo_base = card->fifos + 0x0200; + card->chans[B1].tx.z_base = card->fifos + 0x0000; + card->chans[B1].tx.z1_base = card->fifos + 0x2000; + card->chans[B1].tx.z2_base = card->fifos + 0x2002; + card->chans[B1].tx.z_min = 0x0200; + card->chans[B1].tx.z_max = 0x1FFF; + card->chans[B1].tx.f_min = 0x00; + card->chans[B1].tx.f_max = 0x1F; + card->chans[B1].tx.f1 = card->fifos + 0x2080; + card->chans[B1].tx.f2 = card->fifos + 0x2081; + card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - card->chans[B1].tx.z_min + 1; + card->chans[B1].tx.f_num = card->chans[B1].tx.f_max - card->chans[B1].tx.f_min + 1; + +// card->chans[B1].netdev->irq = card->pcidev->irq; +// card->chans[B1].netdev->base_addr = card->io_bus_mem; +/* card->chans[B1].netdev->rmem_start = card->fifo_bus_mem + 0x4200; + card->chans[B1].netdev->rmem_end = card->fifo_bus_mem + 0x4200 + + card->chans[B1].rx.fifo_size - 1;*/ +// card->chans[B1].netdev->mem_start = card->fifo_bus_mem + 0x0200; +// card->chans[B1].netdev->mem_end = card->fifo_bus_mem + 0x0200 + +// card->chans[B1].tx.fifo_size - 1; + +//---------------------------------- B2 + card->chans[B2].card = card; + card->chans[B2].name = "B2"; + card->chans[B2].status = free; + card->chans[B2].number = B2; + card->chans[B2].protocol = 0; + spin_lock_init(&card->chans[B2].lock); + + card->chans[B2].rx.chan = &card->chans[B2]; + card->chans[B2].rx.fifo_base = card->fifos + 0x6200, + card->chans[B2].rx.z_base = card->fifos + 0x6000; + card->chans[B2].rx.z1_base = card->fifos + 0x6100; + card->chans[B2].rx.z2_base = card->fifos + 0x6102; + card->chans[B2].rx.z_min = 0x0200; + card->chans[B2].rx.z_max = 0x1FFF; + card->chans[B2].rx.f_min = 0x00; + card->chans[B2].rx.f_max = 0x1F; + card->chans[B2].rx.f1 = card->fifos + 0x6180; + card->chans[B2].rx.f2 = card->fifos + 0x6181; + card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - card->chans[B2].rx.z_min + 1; + card->chans[B2].rx.f_num = card->chans[B2].rx.f_max - card->chans[B2].rx.f_min + 1; + + card->chans[B2].tx.chan = &card->chans[B2]; + card->chans[B2].tx.fifo_base = card->fifos + 0x2200; + card->chans[B2].tx.z_base = card->fifos + 0x2000; + card->chans[B2].tx.z1_base = card->fifos + 0x2100; + card->chans[B2].tx.z2_base = card->fifos + 0x2102; + card->chans[B2].tx.z_min = 0x0200; + card->chans[B2].tx.z_max = 0x1FFF; + card->chans[B2].tx.f_min = 0x00; + card->chans[B2].tx.f_max = 0x1F; + card->chans[B2].tx.f1 = card->fifos + 0x2180; + card->chans[B2].tx.f2 = card->fifos + 0x2181; + card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - card->chans[B2].tx.z_min + 1; + card->chans[B2].tx.f_num = card->chans[B2].tx.f_max - card->chans[B2].tx.f_min + 1; + +// card->chans[B2].netdev->irq = card->pcidev->irq; +// card->chans[B2].netdev->base_addr = card->io_bus_mem; +/* card->chans[B2].netdev->rmem_start = card->fifo_bus_mem + 0x6200; + card->chans[B2].netdev->rmem_end = card->fifo_bus_mem + 0x6200 + + card->chans[B2].rx.fifo_size - 1;*/ +// card->chans[B2].netdev->mem_start = card->fifo_bus_mem + 0x2200; +// card->chans[B2].netdev->mem_end = card->fifo_bus_mem + 0x2200 + +// card->chans[B2].tx.fifo_size - 1; + +// ------------------------------------------------------- + + hfc_zap_initialize(card); + + snprintf(card->proc_dir_name, + sizeof(card->proc_dir_name), + "%d", card->cardnum); + card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir); + card->proc_dir->owner = THIS_MODULE; + + card->proc_info = create_proc_read_entry( + "info", 0444, card->proc_dir, + hfc_proc_read_info, card); + card->proc_info->owner = THIS_MODULE; + + card->proc_fifos = create_proc_read_entry( + "fifos", 0400, card->proc_dir, + hfc_proc_read_fifos, card); + card->proc_fifos->owner = THIS_MODULE; + + card->proc_bufs = create_proc_read_entry( + "bufs", 0400, card->proc_dir, + hfc_proc_read_bufs, card); + card->proc_bufs->owner = THIS_MODULE; + + hfc_resetCard(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n", + card->cardnum, + card->nt_mode?"NT":"TE", + card->io_bus_mem, + card->io_mem, + card->pcidev->irq); + + cardnum++; + + return 0; + +// unregister_netdev(card->chans[D].netdev); +err_register_netdev_d: + free_netdev(card->chans[D].netdev); +err_alloc_netdev_d: + free_irq(pci_dev->irq, card); +err_request_irq: + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); +err_alloc_fifo: + iounmap(card->io_mem); +err_ioremap: +err_noiobase: +err_noirq: + pci_release_regions(pci_dev); +err_pci_request_regions: +err_pci_set_dma_mask: +err_pci_enable_device: + kfree(card); +err_alloc_hfccard: + return err; +} + +static void __devexit hfc_remove(struct pci_dev *pci_dev) +{ + struct hfc_card *card = pci_get_drvdata(pci_dev); + + unregister_netdev(card->chans[D].netdev); + +// unsigned long flags; +// spin_lock_irqsave(&card->lock,flags); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "shutting down card at %p.\n", + card->cardnum, + card->io_mem); + + hfc_softreset(card); + + zt_unregister(&card->zt_span); + + + // disable memio and bustmaster + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + +// spin_unlock_irqrestore(&card->lock,flags); + + remove_proc_entry("bufs", card->proc_dir); + remove_proc_entry("fifos", card->proc_dir); + remove_proc_entry("info", card->proc_dir); + remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir); + + free_irq(pci_dev->irq, card); + + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); + + iounmap(card->io_mem); + + pci_release_regions(pci_dev); + + pci_disable_device(pci_dev); + + free_netdev(card->chans[D].netdev); + kfree(card); +} + +/****************************************** + * Module stuff + ******************************************/ + +static int __init hfc_init_module(void) +{ + int ret; + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " loading\n"); + + hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); + + ret = zap_pci_module(&hfc_driver); + return ret; +} + +module_init(hfc_init_module); + +static void __exit hfc_module_exit(void) +{ + pci_unregister_driver(&hfc_driver); + + remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " unloaded\n"); +} + +module_exit(hfc_module_exit); + +#endif + +MODULE_DESCRIPTION(hfc_DRIVER_DESCR); +MODULE_AUTHOR("Jens Wilke , Daniele (Vihai) Orlandi "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef LINUX26 + +module_param(modes, int, 0444); + +/* + * Old 2.6 kernels had module_param_array() macro that receive the counter + * by value. + */ +int nt_modes_num_values; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +module_param_array(nt_modes, int, nt_modes_num_values, 0444); +#else +module_param_array(nt_modes, int, &nt_modes_num_values, 0444); +#endif + +module_param(force_l1_up, int, 0444); +module_param(sniff_zaptel_d_channel, int, 0444); +#ifdef DEBUG +module_param(debug_level, int, 0444); +#endif + +#else + +MODULE_PARM(modes,"i"); +MODULE_PARM(force_l1_up,"i"); +MODULE_PARM(sniff_zaptel_d_channel,"i"); +#ifdef DEBUG +MODULE_PARM(debug_level,"i"); +#endif + +#endif // LINUX26 + +MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode"); +MODULE_PARM_DESC(nt_modes, "Comma-separated list of card IDs to configure in NT mode"); +MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down"); +MODULE_PARM_DESC(sniff_zaptel_d_channel, "Make frames transmitted from zaptel" + " appear as received by the board"); +#ifdef DEBUG +MODULE_PARM_DESC(debug_level, "Debug verbosity level"); +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/lapd.h +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/lapd.h @@ -0,0 +1,26 @@ +/* + * lapd.c - net_device LAPD link layer support functionss + * + * Copyright (C) 2004 Daniele Orlandi + * + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_LAPD_H +#define _HFC_LAPD_H + +#ifndef ARPHRD_LAPD +#define ARPHRD_LAPD 1000 /* LAPD pseudo type */ +#endif + +#ifndef ETH_P_LAPD +#define ETH_P_LAPD 0x0030 /* LAPD pseudo type */ +#endif + +void setup_lapd(struct net_device *netdev); + +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/Makefile +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2006 headissue GmbH; Jens Wilke +# +# This program is free software and may be modified and +# distributed under the terms of the GNU Public License. +# + +KSOURCE ?= /lib/modules/`uname -r`/build + +ifndef src + src=$(shell pwd) +endif + +ZAPTEL_SOURCE = $(src)/../../bristuff12/zaptel/ + +EXTRA_CFLAGS += -I $(ZAPTEL_SOURCE) + +obj-m := vzaphfc.o +vzaphfc-objs := vzaphfc_main.o fifo.o lapd.o + +all: + $(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` modules + +clean: + $(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` clean + +debug: + $(MAKE) -C $(KSOURCE) modules SUBDIRS=`pwd` EXTRA_CFLAGS="$(EXTRA_CFLAGS) -DDEBUG" CONFIG_DEBUG_INFO=1 + +# 2.4 stuff #### +modules: $(obj-m) + sync + +vzaphfc.o: vzaphfc_main.c fifo.c vzaphfc.h fifo.h lapd.h + $(CC) -c vzaphfc_main.c fifo.c $(CFLAGS) $(EXTRA_CFLAGS) + +install: + install -D -m 644 vzaphfc.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/vzaphfc.ko --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/vzaphfc.h +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/vzaphfc.h @@ -0,0 +1,387 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Orginal author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_ZAPHFC_H +#define _HFC_ZAPHFC_H + +#include +#include + +#include "../zaptel.h" + +#define hfc_DRIVER_NAME "vzaphfc" +#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " +#define hfc_DRIVER_DESCR "HFC-S PCI A Zaptel Driver" +#define hfc_DRIVER_VERSION "1.44" +#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" + +#define hfc_MAX_BOARDS 32 + +#ifndef LINUX26 +#define IRQ_NONE +#define IRQ_HANDLED +#define irqreturn_t void +#endif + +#ifndef PCI_DMA_32BIT +#define PCI_DMA_32BIT 0x00000000ffffffffULL +#endif + +#ifndef PCI_VENDOR_ID_SITECOM +#define PCI_VENDOR_ID_SITECOM 0x182D +#endif + +#ifndef PCI_DEVICE_ID_SITECOM_3069 +#define PCI_DEVICE_ID_SITECOM_3069 0x3069 +#endif + +#define hfc_RESET_DELAY 20 + +#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* PCI memory mapped I/O */ + +#define hfc_PCI_MEM_SIZE 0x0100 +#define hfc_PCI_MWBA 0x80 + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_TRANSB1 0x01 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TIMER_CLEAR 0x80 +#define hfc_CTMT_TIMER_MASK 0x1C +#define hfc_CTMT_TIMER_3_125 (0x01 << 2) +#define hfc_CTMT_TIMER_6_25 (0x02 << 2) +#define hfc_CTMT_TIMER_12_5 (0x03 << 2) +#define hfc_CTMT_TIMER_25 (0x04 << 2) +#define hfc_CTMT_TIMER_50 (0x05 << 2) +#define hfc_CTMT_TIMER_400 (0x06 << 2) +#define hfc_CTMT_TIMER_800 (0x07 << 2) +#define hfc_CTMT_AUTO_TIMER 0x20 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in SCTRL_R */ +#define hfc_SCTRL_R_B1_ENA 0x01 +#define hfc_SCTRL_R_B2_ENA 0x02 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_HFC_from_ST 0x00 +#define hfc_CONNECT_B1_HFC_from_GCI 0x01 +#define hfc_CONNECT_B1_ST_from_HFC 0x00 +#define hfc_CONNECT_B1_ST_from_GCI 0x02 +#define hfc_CONNECT_B1_GCI_from_HFC 0x00 +#define hfc_CONNECT_B1_GCI_from_ST 0x04 + +#define hfc_CONNECT_B2_HFC_from_ST 0x00 +#define hfc_CONNECT_B2_HFC_from_GCI 0x08 +#define hfc_CONNECT_B2_ST_from_HFC 0x00 +#define hfc_CONNECT_B2_ST_from_GCI 0x10 +#define hfc_CONNECT_B2_GCI_from_HFC 0x00 +#define hfc_CONNECT_B2_GCI_from_ST 0x20 + +/* bits in the TRM register */ +#define hfc_TRM_TRANS_INT_00 0x00 +#define hfc_TRM_TRANS_INT_01 0x01 +#define hfc_TRM_TRANS_INT_10 0x02 +#define hfc_TRM_TRANS_INT_11 0x04 +#define hfc_TRM_ECHO 0x20 +#define hfc_TRM_B1_PLUS_B2 0x40 +#define hfc_TRM_IOM_TEST_LOOP 0x80 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FIFO_SIZE 0x8000 + +#define hfc_UGLY_FRAMEBUF 0x2000 + +#define hfc_TX_FIFO_PRELOAD ZT_CHUNKSIZE + 2 +#define hfc_RX_FIFO_PRELOAD 4 + +/* NOTE: FIFO pointers are not declared volatile because accesses to the + * FIFOs are inherently safe. + */ + +#ifdef DEBUG +extern int debug_level; +#endif + +struct hfc_chan; + +struct hfc_chan_simplex { + struct hfc_chan_duplex *chan; + + u8 zaptel_buffer[ZT_CHUNKSIZE]; + + u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; + int ugly_framebuf_size; + u16 ugly_framebuf_off; + + void *z1_base,*z2_base; + void *fifo_base; + void *z_base; + u16 z_min; + u16 z_max; + u16 fifo_size; + + u8 *f1,*f2; + u8 f_min; + u8 f_max; + u8 f_num; + + unsigned long long frames; + unsigned long long bytes; + unsigned long long fifo_full; + unsigned long long crc; + unsigned long long fifo_underrun; +}; + +enum hfc_chan_status { + free, + open_framed, + open_voice, + sniff_aux, + loopback, +}; + +struct hfc_chan_duplex { + struct hfc_card *card; + + char *name; + int number; + + enum hfc_chan_status status; + int open_by_netdev; + int open_by_zaptel; + + unsigned short protocol; + + spinlock_t lock; + + struct hfc_chan_simplex rx; + struct hfc_chan_simplex tx; + + struct net_device *netdev; + struct net_device_stats net_device_stats; +}; + +typedef struct hfc_card { + int cardnum; + spinlock_t lock; + + int ticks; + + struct pci_dev *pcidev; + + struct proc_dir_entry *proc_dir; + char proc_dir_name[32]; + + struct proc_dir_entry *proc_info; + struct proc_dir_entry *proc_fifos; + struct proc_dir_entry *proc_bufs; + + unsigned long io_bus_mem; + void *io_mem; + + dma_addr_t fifo_bus_mem; + void *fifo_mem; + void *fifos; + + int nt_mode; + int sync_loss_reported; + int late_irqs; + + u8 l1_state; + int fifo_suspended; + int ignore_first_timer_interrupt; + + struct { + u8 m1; + u8 m2; + u8 fifo_en; + u8 trm; + u8 connect; + u8 sctrl; + u8 sctrl_r; + u8 sctrl_e; + u8 ctmt; + u8 cirm; + } regs; + + struct hfc_chan_duplex chans[3]; + int echo_enabled; + + struct zt_span zt_span; + struct zt_chan zt_chans[3]; + + int debug_event; +} hfc_card; + +static inline u8 hfc_inb(struct hfc_card *card, int offset) +{ + return readb(card->io_mem + offset); +} + +static inline void hfc_outb(struct hfc_card *card, int offset, u8 value) +{ + writeb(value, card->io_mem + offset); +} + +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/lapd.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/lapd.c @@ -0,0 +1,48 @@ +/* + * lapd.c + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include +#include +#include + +#include "lapd.h" + +static int lapd_change_mtu(struct net_device *dev, int mtu) +{ + return -EINVAL; +} + +static int lapd_mac_addr(struct net_device *dev, void *addr) +{ + return -EINVAL; +} + +void setup_lapd(struct net_device *netdev) +{ + + netdev->change_mtu = lapd_change_mtu; + netdev->set_mac_address = lapd_mac_addr; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + netdev->hard_header = NULL; + netdev->rebuild_header = NULL; + netdev->hard_header_cache = NULL; + netdev->header_cache_update= NULL; +#endif + + netdev->type = ARPHRD_LAPD; + netdev->hard_header_len = 0; + netdev->mtu = 512; + netdev->addr_len = 0; + netdev->tx_queue_len = 10; + + memset(netdev->broadcast, 0x00, sizeof(netdev->broadcast)); + + netdev->flags = IFF_NOARP; +} + + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/fifo.h +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/fifo.h @@ -0,0 +1,131 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_FIFO_H +#define _HFC_FIFO_H + +#include "vzaphfc.h" + +static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f1 * 4); +} + +static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f1 * 4); +} + +static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f2 * 4); +} + +static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f2 * 4); +} + +static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) +{ + // declared as u32 in order to manage overflows + u32 newz = z + inc; + if (newz > chan->z_max) + newz -= chan->fifo_size; + + return newz; +} + +static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) +{ + // declared as u16 in order to manage overflows + u16 newf = f + inc; + if (newf > chan->f_max) + newf -= chan->f_num; + + return newf; +} + +static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F2(chan) - *Z2_F2(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) +{ + // This +1 is needed because in frame mode the available bytes are Z2-Z1+1 + // while in transparent mode I wouldn't consider the byte pointed by Z2 to + // be available, otherwise, the FIFO would always contain one byte, even + // when Z1==Z2 + + return hfc_fifo_used_rx(chan) + 1; +} + +static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) +{ + return *((u8 *)(chan->z_base + z)); +} + +static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F1(chan) - *Z2_F1(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) +{ + return *chan->f1 != *chan->f2; +} + +static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; +} + +static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); + +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/ChangeLog +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/ChangeLog @@ -0,0 +1,13 @@ +1.44 - 2007aug26 - Jens Wilke +- Fix for changes irq callback signature (pt_reqs removed) +- Fix for missing CHECKSUM_COMPLETE in newer kernel version +- Fix for missing linux/config.h in kernel headers +- tested with Linux kernel 2.6.20 + +1.43 - 2006oct03 - Jens Wilke +- fix wrong nt_modes parsing, found with kernel 2.6.15 + +1.42 - 2006jun06 - Jens Wilke +- fixed fifo timing issue +- added statistics for fifo underruns (RXU&TXU) +- maintanance now done by jw --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/vzaphfc/README +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/vzaphfc/README @@ -0,0 +1,45 @@ +== General Info == + +This is a Zaptel driver for HFC-S PCI A based ISDN BRI cards (Cologne +Chipdesign). + +== Driver Build == + +You need the kernel source (2.6) and the Zaptel driver sources +to build the module. + +== Module Parameters == + + nt_modes: + comma seperated list + + sniff_zaptel_d_channel: + Make frames transmitted from Zaptel appear as received by the board + Set this to 1 if you want to debug D channel signaling with Etherreal + + force_l1_up: + Keep ISDN bus activated. Usually set by PtP TE interfaces. + + nt_modes: + Comma-separated list of card IDs to configure in NT mode (array of int) + +== Information in the proc file system == + + You will find valuable data about the current state of your ISDN + interface in /proc/drivers/vzaphfc/. The following files + are present for each card: + + info: + Information about the bus activation, transmit errors (CRC), and + overall transfer statistics + + bufs: + Except from the data that is currently transmitted + + fifo: + sizes and counter values from the FIFOs, mainly for debugging + purposes + + +Latest Change: 6 Jun 2006 Jens Wilke + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/zaphfc/Makefile +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/zaphfc/Makefile @@ -0,0 +1,116 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") +RTAI = $(shell [ -f /usr/realtime/include/rtai.h ] && echo "-DRTAITIMING -I/usr/realtime/include") + +CFLAGS+=-I. $(ZAP) $(RTAI) -O2 -g -Wall -DBUILDING_TONEZONE +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) $(RTAI) -Wall +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +OBJS=zaphfc.o + +MODULES=zaphfc + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + + +zaphfc.o: zaphfc.c zaphfc.h + $(CC) -c zaphfc.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + rm -rf .tmp_versions + +test: all + modprobe zaptel + insmod ./zaphfc.o + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod zaphfc + rmmod zaptel + +load: load$(BUILDVER) + +loadNT: load$(BUILDVER)NT + +load-debug: load$(BUILDVER)-debug + +loadNT-debug: load$(BUILDVER)NT-debug + +loadlinux24: all + modprobe zaptel + insmod ./zaphfc.o + ztcfg -v + +loadlinux24-debug: all + modprobe zaptel + insmod ./zaphfc.o debug=1 + ztcfg -v + +loadlinux26: linux26 + modprobe zaptel + insmod ./zaphfc.ko + ztcfg -v + +loadlinux26-debug: linux26 + modprobe zaptel + insmod ./zaphfc.ko debug=1 + ztcfg -v + +loadlinux24NT: all + modprobe zaptel + insmod ./zaphfc.o modes=1 + ztcfg -v + +loadlinux24NT-debug: all + modprobe zaptel + insmod ./zaphfc.o modes=1 debug=1 + ztcfg -v + +loadlinux26NT: linux26 + modprobe zaptel + insmod ./zaphfc.ko modes=1 + ztcfg -v + +loadlinux26NT-debug: linux26 + modprobe zaptel + insmod ./zaphfc.ko modes=1 debug=1 + ztcfg -v + +unload: + -rmmod zaphfc zaptel + +zaphfc.ko: zaphfc.c zaphfc.h + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules + +install: install$(BUILDVER) + +installlinux26: + install -D -m 644 zaphfc.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/zaphfc.ko + +installlinux24: + install -D -m 644 zaphfc.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/zaphfc.o + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/zaphfc/zapata.conf +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/zaphfc/zapata.conf @@ -0,0 +1,38 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn +; p2mp TE mode +signalling = bri_cpe_ptmp + +; p2p TE mode +;signalling = bri_cpe +; p2mp NT mode +;signalling = bri_net_ptmp +; p2p NT mode +;signalling = bri_net + +pridialplan = dynamic +prilocaldialplan = local +nationalprefix = 0 +internationalprefix = 00 + +echocancel=yes +echotraining = 100 +echocancelwhenbridged=yes + +immediate=yes +group = 1 +context=demo +channel => 1-2 --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/zaphfc/zaptel.conf +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/zaphfc/zaptel.conf @@ -0,0 +1,8 @@ +# hfc-s pci a span definition +# most of the values should be bogus because we are not really zaptel +loadzone=nl +defaultzone=nl + +span=1,1,3,ccs,ami +bchan=1-2 +dchan=3 --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zaptel.conf.hdlcnet.doubleE1 +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zaptel.conf.hdlcnet.doubleE1 @@ -0,0 +1,10 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 +span=2,2,3,ccs,hdb3,crc4 + +; hdlc0 +hdlcnet = 1 + +; hdlc 1 +hdlcnet = 2 \ No newline at end of file --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/TODO +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/TODO @@ -0,0 +1 @@ + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zaptel.conf.hdlcnet +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zaptel.conf.hdlcnet @@ -0,0 +1,8 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 + +; hdlc0 +hdlcnet=1 + + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zaptel.conf.doubleE1 +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zaptel.conf.doubleE1 @@ -0,0 +1,14 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 +span=2,2,3,ccs,hdb3,crc4 + +alaw=1-62 + +bchan=1-15 +dchan=16 +bchan=17-31 + +bchan=32-46 +dchan=47 +bchan=48-62 --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/Makefile +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/Makefile @@ -0,0 +1,88 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") + +CFLAGS+=-I. $(ZAP) -DRELAXED_LOCKING -O2 -g -Wall #-DBLINKYBLINK +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -DRELAXED_LOCKING -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +OBJS=cwain.o + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +MODULES=cwain + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules +obj-m := $(OBJS) + +cwain.o: cwain.c cwain.h + $(CC) -c cwain.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + +testlinux24: all + modprobe zaptel + insmod ./cwain.o + ztcfg -v + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod cwain zaptel + +testlinux26: all + modprobe zaptel + insmod ./cwain.ko + ztcfg -v + cat /proc/interrupts + sleep 5 + cat /proc/interrupts + rmmod cwain zaptel + +reload: unload load +load: load$(BUILDVER) + +test: test$(BUILDVER) + + +loadlinux24: linux24 + modprobe zaptel + insmod ./cwain.o + ztcfg -v + +loadlinux26: linux26 + sync + modprobe zaptel + insmod ./cwain.ko debug=1 + ztcfg -v + +install: install$(BUILDVER) + +installlinux26: + install -D -m 644 cwain.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/cwain.ko + +installlinux24: + install -D -m 644 cwain.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/cwain.o + +unload: + rmmod cwain zaptel --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zapata.conf +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zapata.conf @@ -0,0 +1,32 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +signalling = pri_cpe + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo + +group = 1 +channel => 1-15,17-31 + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/cwain.c +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/cwain.c @@ -0,0 +1,2236 @@ +/* + * cwain.c - Zaptel driver for the Junghanns.NET E1 card + * + * c.w.a.i.n. == card without an interesting name + * + * single/double E1 board + * + * Copyright (C) 2004, 2005, 2006, 2007 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ +#include +#include +#include +#include +#include +#include +#include "cwain.h" + +#ifdef LINUX26 +#include +#endif + +#if CONFIG_PCI + +static int ports=-1; /* autodetect */ +static int debug=0; +static int hw_hdlc=1; +static int hdlcnet=0; +static int pwm0 = 0x50; /* TX level */ +static int pwm1 = 0x50; /* RX level */ +static int dacs = 1; /* 0 = no dacs, 1 = oncard dacs */ +static int require_ext_clock = 0; +static struct zt_cwain *cwain_span_list = NULL; +static int cwain_span_count = 0; +static struct zt_cwain_card *cwain_card_list = NULL; +static int cwain_card_count = 0; +static struct pci_dev *multi_cwain = NULL; +static spinlock_t cwain_span_registerlock = SPIN_LOCK_UNLOCKED; +static spinlock_t cwain_card_registerlock = SPIN_LOCK_UNLOCKED; + +static int ztcwain_shutdown(struct zt_span *span); + +int cwain_waitbusy(struct zt_cwain *cwaintmp) { + int x=1000; + while (x-- && (cwain_inb(cwaintmp,cwain_R_STATUS) & 1)); + if (x < 0) { + printk(KERN_CRIT "cwain: really busy waiting!\n"); + return -1; + } else { + if ((x < 990) && (cwaintmp->ticks > 500)) { + printk(KERN_CRIT "cwain: waited %d\n", 1000 - x); + } + return 0; + } +} + +void cwain_unregister_zap_span(struct zt_cwain *cwainspan) { + if (!cwainspan) { + printk(KERN_INFO "cwain: shutting down NULL span!\n"); + return; + } + if(cwainspan->span.flags & ZT_FLAG_RUNNING) { + ztcwain_shutdown(&cwainspan->span); + if (debug) + printk(KERN_INFO "cwain: shutdown span %d.\n",cwainspan->cardno); + } + if(cwainspan->span.flags & ZT_FLAG_REGISTERED) { + zt_unregister(&cwainspan->span); + if (debug) + printk(KERN_INFO "cwain: unregistered span %d.\n",cwainspan->cardno); + } +} + +void cwain_shutdown_span(struct zt_cwain *cwainspan) { + + if (!cwainspan) { + printk(KERN_INFO "cwain: shutting down NULL span!\n"); + return; + } + + if (cwainspan->pci_io == NULL) { + return; + } + + if (debug) + printk(KERN_INFO "cwain: shutting down span %d (cardID %d) at %p.\n",cwainspan->cardno,cwainspan->cardID,cwainspan->pci_io); + + // turn off irqs + + // softreset + cwain_outb(cwainspan,cwain_R_CIRM,0x8); + cwain_outb(cwainspan,cwain_R_CIRM,0x0); + cwain_waitbusy(cwainspan); + + cwain_outb(cwainspan,cwain_R_IRQMSK_MISC, 0); + cwain_outb(cwainspan,cwain_R_IRQ_CTRL, 0); + + pci_write_config_word(cwainspan->pcidev, PCI_COMMAND, 0); // disable memio + + if (cwainspan->pcidev != NULL) { + pci_disable_device(cwainspan->pcidev); + cwainspan->pcidev = NULL; + } + +} + +void cwain_shutdown_card(struct zt_cwain_card *cwaintmp) { + unsigned long flags = 0; + int i = 0; + + if (!cwaintmp) { + printk(KERN_INFO "cwain: shutting down NULL card!\n"); + return; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + + // turn off irqs + cwain_outb(cwaintmp->span[0],cwain_R_IRQ_CTRL, 0); + cwain_outb(cwaintmp->span[0],cwain_R_IRQMSK_MISC, 0); + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + + for (i=0;ispans;i++) { + cwain_unregister_zap_span(cwaintmp->span[i]); + } + + + for (i=0;ispans;i++) { + release_region(cwaintmp->span[i]->ioport, 8); + cwaintmp->span[i]->ioport = 0; + iounmap((void *) cwaintmp->span[i]->pci_io); + cwaintmp->span[i]->pci_io = NULL; + release_mem_region((unsigned long)cwaintmp->span[i]->pci_io_phys, 256); + } + + if (cwaintmp->spans == 2) { + free_irq(cwaintmp->span[1]->irq,cwaintmp); + } + free_irq(cwaintmp->irq,cwaintmp); +} + +void cwain_doLEDs(struct zt_cwain *cwaintmp) { + /* + O1 O3 (red) + O2 O4 (green) + */ + if (!(cwaintmp->span.flags & ZT_FLAG_RUNNING)) { + return; + } + if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554) || (cwaintmp->type == 0xb555)) { + /* sync status */ + if (((cwaintmp->sync_sta & 0x07) == 0x07) && cwaintmp->sync) { + cwaintmp->leds[0] = 1; + cwaintmp->leds[1] = 0; + } else { + cwaintmp->leds[0] = 0; + cwaintmp->leds[1] = 1; + } + /* multiframe alignment */ + if ((cwaintmp->sync_sta & 0x20) == 0x20) { + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 0; + } else { + if ((cwaintmp->span.lineconfig & ZT_CONFIG_CRC4) && cwaintmp->sync) { + /* CRC4 requested */ + cwaintmp->leds[2] = 0; + cwaintmp->leds[3] = 1; + } else { + /* no CRC4, disable 3 and 4 */ + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 1; + } + } + cwain_outb(cwaintmp,cwain_R_GPIO_OUT1,(cwaintmp->leds[0] | (cwaintmp->leds[1] << 1) | (cwaintmp->leds[2] << 2) | (cwaintmp->leds[3] << 3))); + } +} + +void cwain_reset_pcm(struct zt_cwain *cwaintmp, int master, int int_clock) { + /* PCM reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x20); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + if (master) { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0x91); + cwain_outb(cwaintmp,cwain_R_PCM_MD1, 0x20); + if (int_clock) { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0xA1); + cwain_outb(cwaintmp,cwain_R_PCM_MD2, 0x04); + } else { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0xA1); + cwain_outb(cwaintmp,cwain_R_PCM_MD2, 0x00); + } + } else { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0x90); + cwain_outb(cwaintmp,cwain_R_PCM_MD1, 0x20); + } +} + +void cwain_reset_span(struct zt_cwain *cwaintmp) { + + pci_write_config_word(cwaintmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO); // enable memio + + cwain_reset_pcm(cwaintmp,0,0); + + /* E1 reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x40); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + /* soft reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x10); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + /* 128 byte B chans, 4096 byte D chans */ + if (hdlcnet) { + cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + } else { + cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x36); + } + + + cwain_outb(cwaintmp,cwain_R_BRG_PCM_CFG,0x0); + cwain_outb(cwaintmp,cwain_R_CTRL,0x0); + + /* no blinky blink */ + if (cwaintmp->type == 0xb555) { + cwain_outb(cwaintmp,cwain_R_GPIO_SEL,0x20 | 0x10 | 0x02); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x0f); + cwain_outb(cwaintmp,cwain_R_GPIO_EN0,0x08 | 0x04); + } else { + cwain_outb(cwaintmp,cwain_R_GPIO_SEL,0x20 | 0x10); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x0f); + } + cwain_outb(cwaintmp,cwain_R_GPIO_OUT1,0x0f); + + /* IRQs off */ + cwain_outb(cwaintmp,cwain_R_IRQMSK_MISC,0x0); + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL,0x0); + + cwaintmp->leds[0] = 1; + cwaintmp->leds[1] = 1; + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 1; + + cwaintmp->ticks = 0; + cwaintmp->clicks = 0; +} + +struct zt_cwain_card *cwain_get_card(unsigned int pcibus) { + struct zt_cwain_card *cwaintmp = cwain_card_list; + spin_lock(&cwain_card_registerlock); + while (cwaintmp) { + if (cwaintmp->pcibus == pcibus) { + break; + } + cwaintmp = cwaintmp->next; + } + spin_unlock(&cwain_card_registerlock); + return cwaintmp; +} + + +void cwain_register_card(struct zt_cwain_card *cwaincard) { + spin_lock(&cwain_card_registerlock); + if (cwaincard != NULL) { + cwaincard->prev = NULL; + cwaincard->next = cwain_card_list; + if (cwain_card_list) { + cwain_card_list->prev = cwaincard; + } + cwain_card_list = cwaincard; + cwain_card_count++; + } else { + printk(KERN_INFO "cwain: trying to register NULL card.\n"); + } + spin_unlock(&cwain_card_registerlock); +} + +int cwain_register_span(struct zt_cwain *cwainspan) { + struct zt_cwain_card *cwaintmp; + spin_lock(&cwain_span_registerlock); + if (cwainspan != NULL) { + cwainspan->prev = NULL; + cwainspan->next = cwain_span_list; + if (cwain_span_list) { + cwain_span_list->prev = cwainspan; + } + cwain_span_list = cwainspan; + cwainspan->cardno = ++cwain_span_count; + } else { + printk(KERN_INFO "cwain: trying to register NULL span.\n"); + } + spin_unlock(&cwain_span_registerlock); + + if (cwainspan->type == 0xb553) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + return -1; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain_card)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; + cwaintmp->span[0] = cwainspan; + cwaintmp->syncs[0] = -1; + cwaintmp->spans = 1; + cwaintmp->cardID = cwainspan->cardID; + cwain_register_card(cwaintmp); + printk(KERN_INFO + "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", + (unsigned long) cwainspan->pci_io, + cwaintmp->span[0]->irq, HZ, cwainspan->cardID); + } else { + cwaintmp = cwain_get_card(cwainspan->pcibus); + if (!cwaintmp) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + return -1; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain_card)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; +// cwaintmp->spans = cwainspan->type - 46419; + cwaintmp->spans = (cwainspan->type & (2321 * 20)) - 46419; + cwaintmp->span[0] = cwainspan; + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->syncs[0] = -1; + cwain_register_card(cwaintmp); + } else { + cwaintmp->spans = (cwainspan->type & (2321 * 20)) - 46418; + if (cwainspan->cardID < cwaintmp->cardID) { + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->span[1] = cwaintmp->span[0]; + cwaintmp->syncs[1] = cwaintmp->syncs[0]; + cwaintmp->span[0] = cwainspan; + } else { + cwaintmp->span[1] = cwainspan; + cwaintmp->syncs[1] = -1; + } + printk(KERN_INFO + "cwain: Junghanns.NET doubleE1 PCI ISDN %d.0 card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", + (cwainspan->type - 0xb553), (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, + cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); + } + } + return 0; +} + +static void cwain_dfifo_check(struct zt_cwain *cwaintmp) { + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + unsigned char f1=1,f2=1; + unsigned char of1=0,of2=0; + int space = 0; + int len; + int chan; + int fifo = 0x1f; + unsigned long flags; + int frames = 0; + + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + + /* calculate how much data we can allow zaptel to transmit */ + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select tx fifo */ + + cwain_outb(cwaintmp,cwain_R_FIFO, fifo << 1); + cwain_waitbusy(cwaintmp); + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0xfff; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0xfff; + } + + len = z1-(z2 & 0xfff); + if (len < 0) { + len += cwain_DFIFO_SIZE; + } + space = cwain_DFIFO_SIZE - len; + + if (((debug > 2) && (space < cwain_DFIFO_SIZE)) || (space == 0)) { + printk(KERN_INFO "cwain: card %d TX fifo %d space now %d\n", cwaintmp->cardno, fifo, space); + } + cwaintmp->chans[chan].maxbytes2transmit = space; + + + /* calculate how many frames are in the receive fifo */ + + /* select rx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while ((of1 != f1) && (of2 != f2)) { + of1 = f1; + of2 = f2; + f1 = cwain_inb(cwaintmp,cwain_A_F1) & 0xf; + f2 = cwain_inb(cwaintmp,cwain_A_F2) & 0xf; + } + + frames = f1 - f2; + if (frames < 0) { + frames += cwain_DFIFO_FRAMES; + } + cwaintmp->drx = frames; + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + + if ((frames > 0) && (debug > 3)) + printk(KERN_INFO "\ncwain: %d frames in RX fifo\n", frames); +} + +static int cwain_dfifo_tx(struct zt_cwain *cwaintmp) { + int chan; + int x=0; + char fifo = 0; + unsigned long flags = 0; + + fifo = 0x1F; + + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + if (cwaintmp->chans[chan].bytes2transmit < 1) { + return 0; + } else { + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,fifo << 1); + cwain_waitbusy(cwaintmp); + + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk(KERN_INFO "cwain: card %d TX [ ", cwaintmp->cardno); + /* copy frame to fifo */ + for (x=0;xchans[chan].bytes2transmit;x++) { + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk("%#x ",cwaintmp->dtxbuf[x]); + cwain_outb(cwaintmp,cwain_A_FIFO_DATA0,cwaintmp->dtxbuf[x]); + } + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk("]\n"); + if (((debug > 2 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk(KERN_INFO "ztx %d bytes\n",cwaintmp->chans[chan].bytes2transmit); + + if (cwaintmp->chans[chan].eoftx == 1) { + /* transmit HDLC frame */ + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x1); + cwain_waitbusy(cwaintmp); + if ((debug > 3 ) && hdlcnet) + printk(KERN_INFO "cwain: TX flag\n"); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + } + return 0; +} + +static int cwain_fifo_tx(struct zt_cwain *cwaintmp, char fifo) { + int chan,f; + unsigned long flags = 0; + + if (hw_hdlc) { + if (fifo <= 14) { + chan = fifo; + } else { + chan = fifo + 1; + } + } else { + chan = fifo; + } + + if (dacs && (cwaintmp->ts_rx[chan] > -1)) { + /* dont transmit when we receive from the pcm bus */ + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,0x80 | (fifo << 1)); + cwain_waitbusy(cwaintmp); + + for (f=0; f < (cwain_FRAME_SIZE/4); f++) { + cwain_outdw(cwaintmp,cwain_A_FIFO_DATA0,*((unsigned int *) &cwaintmp->ftxbuf[chan][f * 4])); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +static int cwain_dfifo_rx(struct zt_cwain *cwaintmp) { + int chan; + unsigned char data,stat; + int len,i = 0; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + char fifo = 0; + unsigned long flags = 0; + + fifo = 0x1F; + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0xfff; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0xfff; + } + + len = z1-(z2 & 0xfff); + if (len < 0) { + len += cwain_DFIFO_SIZE; + } + + if (len > cwain_DFIFO_SIZE) { + printk(KERN_INFO "\ncwain: buffer overflow in D channel RX!\n"); + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; + } else { + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk(KERN_INFO "cwain: card %d RX [ ", cwaintmp->cardno); + for (i=0; idrxbuf[i] = data; + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk("%#x ",data); + } + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk("] %d bytes\n", i); + cwaintmp->chans[chan].bytes2receive = i; + cwaintmp->chans[chan].eofrx = 1; + } + + stat = cwain_inb(cwaintmp,cwain_A_FIFO_DATA0); + if (stat != 0x0) { + // bad CRC, skip it + if (cwaintmp->sync) { + printk(KERN_INFO "cwain: BAD CRC for hdlc frame on card %d (cardID %d) stat %#x len %d\n",cwaintmp->cardno, cwaintmp->cardID, stat, i); + } + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; +// zt_qevent_nolock(&cwaintmp->chans[chan], ZT_EVENT_BADFCS); + } + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x1); + cwain_waitbusy(cwaintmp); + + /* frame received */ + cwaintmp->drx--; + if (!cwaintmp->sync) { + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; + stat = 0xff; + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + if (stat == 0) { + return 1; + } else { + return 0; + } +} + +static int cwain_fifo_rx(struct zt_cwain *cwaintmp, char fifo) { + int chan; + unsigned int data; + int len = 0,i,f,flen = 0; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + int mumbojumbo=0; + unsigned long flags = 0; + int x = 1000; + + if (hw_hdlc) { + if (fifo <= 14) { + chan = fifo; + } else { + chan = fifo + 1; + } + } else { + chan = fifo; + } + + // select rx fifo + spin_lock_irqsave(&cwaintmp->lock,flags); + // no hdlc, transparent data + cwain_outb(cwaintmp,cwain_R_FIFO,0x80 | (fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while (x-- && ((oz1 != z1) && (oz2 != z2))) { + len = z1 - z2; + if (len < 0) { + len += cwain_FIFO_SIZE; + } + flen = len; + if (len > cwain_FIFO_HW) { + mumbojumbo = len - (cwain_FIFO_HW - cwain_FRAME_SIZE); + len = cwain_FRAME_SIZE; + } + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0x7f; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0x7f; + } + if (x < 500) { + printk(KERN_CRIT "cwain: prevented endless loop\n"); + } + + if (mumbojumbo > 0) { + for (i=0;i<(mumbojumbo/4);i++) { + data = cwain_indw(cwaintmp,cwain_A_FIFO_DATA0); + } + cwaintmp->clicks++; + } + if (len < cwain_FRAME_SIZE) { + /* dont get nervous here */ + if ((cwaintmp->clicks > 600) && (cwaintmp->span.alarms == ZT_ALARM_NONE)) { + printk(KERN_INFO "cwain: cardID %d not enough to receive (%d bytes), fifo %d\n",cwaintmp->cardID, len, fifo); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; + } else { + for (f=0;f<(cwain_FRAME_SIZE / 4);f++) { + *((unsigned int *) &cwaintmp->frxbuf[chan][f*4]) = cwain_indw(cwaintmp,cwain_A_FIFO_DATA0); + } + } + + + /* dont get nervous here */ + if (((cwaintmp->clicks > 50) || (debug > 3)) && ((cwaintmp->span.alarms == ZT_ALARM_NONE) && (mumbojumbo > 0))) { + printk(KERN_INFO "cwain: span %d dropped audio fifo %d mj %d flen %d z1 %d z2 %d\n", cwaintmp->cardID, fifo, mumbojumbo, flen, z1, z2); + cwaintmp->clicks = 0; + } +// printk(KERN_INFO "s/t port %d, channel %d, dbufi=%d, f1=%d, f2=%d, z1=%d, z2=%d => len = %d stat=%#x, hdlc=%d\n",stport,chan,cwaintmp->st[stport].dbufi,f1,f2,z1,z2,len,stat,hdlc); + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +void cwain_set_master(struct zt_cwain_card *cwaintmp, int span) { + int i=0; + unsigned long flags = 0; + + if (cwaintmp->syncsrc == span) return; + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* disable the old master */ + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->master[i]) { + /* enable PCM slave mode, PCM128 */ + cwain_reset_pcm(cwaintmp->span[i],0,0); + cwaintmp->master[i] = 0; + } + } + + /* enable the new master */ + if (cwaintmp->syncs[span] > 0) { + /* enable PCM master mode, PCM128, synced to E1 receive */ + cwain_reset_pcm(cwaintmp->span[span],1,0); + if (debug) + printk(KERN_INFO "cwain: cardID %d span %d, PCM master E1 sync\n", cwaintmp->cardID, span); + } else { + /* enable PCM master mode, PCM128, free running */ + cwain_reset_pcm(cwaintmp->span[span],1,1); + if (debug) + printk(KERN_INFO "cwain: cardID %d span %d, PCM master internal clock\n", cwaintmp->cardID, span); + } + + /* reset the slaves */ + for (i=0; i < cwaintmp->spans; i++) { + if (i != span) { + /* enable PCM slave mode, PCM128 */ + cwain_reset_pcm(cwaintmp->span[i],0,0); + cwaintmp->master[i] = 0; + } + } + + cwaintmp->master[span] = 1; + cwaintmp->syncsrc = span; + + /* restore pcm assignments !!! */ + + spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + +void cwain_check_timing(struct zt_cwain_card *cwaintmp) { + int i=0; + int bestsync = 42; + int sync_ok = 0; + + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->span[i]->span.lineconfig & ZT_CONFIG_CRC4) { + /* CRC4 requested */ + sync_ok = 0x27; + } else { + /* doubleframing requested */ + sync_ok = 0x07; + } + if ((cwaintmp->syncs[i] > 0) && ((cwaintmp->span[i]->sync_sta & sync_ok) == sync_ok)) { + if (bestsync < cwaintmp->spans) { + if (cwaintmp->syncs[i] < cwaintmp->syncs[bestsync]) { + bestsync = i; + } + } else { + bestsync = i; + } + } + } + + if (cwaintmp->syncsrc >= 0) { + if (debug > 3) + printk(KERN_INFO "cwain: bestsync %d cwaintmp->syncsrc %d\n", bestsync, cwaintmp->syncsrc); + + if (bestsync == cwaintmp->syncsrc) { + if (debug > 3) + printk(KERN_INFO "cwain: already on best syncsrc %d\n", bestsync); + return; + } + + /* if we have a better syncsrc */ + if (bestsync < cwaintmp->spans) { + if (debug) + printk(KERN_INFO "cwain: found better syncsrc %d\n", bestsync); + cwain_set_master(cwaintmp, bestsync); + return; + } + } + + /* if reelection failed, find internal sync source, if not forbidden! */ + if ((cwaintmp->syncsrc == -1) && !require_ext_clock) { + /* no master yet */ + if (debug > 3) + printk(KERN_INFO "cwain: no clocksource found cardID %d\n", cwaintmp->cardID); + for (i=0; i < cwaintmp->spans; i++) { + /* find the first internal source */ + if (debug > 3) + printk(KERN_INFO "cwain: cwaintmp->syncs[%d] = %d\n", i, cwaintmp->syncs[i]); + if (cwaintmp->syncs[i] == 0) { + if (debug) + printk(KERN_INFO "cwain: using internal clock of span %d\n", i); + cwain_set_master(cwaintmp, i); + return; + } + } + } + + /* if we have no internal sync source the only thing we can do is to enable any of the possible sync sources*/ + if (cwaintmp->syncsrc == -1) { + /* find the first possible sync source with framing */ + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->syncs[i] > 0) { + if (debug) + printk(KERN_INFO "cwain: desperately using clock of span %d\n", i); + cwain_set_master(cwaintmp, i); + break; + } + } + } +} + + +static void cwain_assign(struct zt_cwain_card *cwaincard, int src_span, int src_chan, int dst_span, int dst_chan, int timeslot, int use_pcm_bus) { + int dst_fifo = dst_chan - 1; + int src_fifo = src_chan - 1; + int src_hfc_chan = src_chan; + int dst_hfc_chan = dst_chan; + struct zt_cwain *cwain_src = NULL, *cwain_dst = NULL; + unsigned long flags = 0; + /* hw_hdlc == 1 + fifo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 >< 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 + time 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + chan 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + + hw_hdlc == 0 + fifo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + time 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + chan 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + */ + + if (hw_hdlc) { + if (dst_chan > 0x10) { + dst_fifo--; + } + if (src_chan > 0x10) { + src_fifo--; + } + } + + if (cwaincard) { + cwain_src = cwaincard->span[src_span]; + cwain_dst = cwaincard->span[dst_span]; + } else { + return; + } + + if (debug) + printk(KERN_INFO "cwain: assign(src_span %d (ID %d), src_chan %d, dst_span %d (ID %d), dst_chan %d, use_pcm_bus %d, timeslot %d\n", src_span, cwain_src->cardID, src_chan, dst_span, cwain_dst->cardID, dst_chan, use_pcm_bus, timeslot); + + spin_lock_irqsave(&cwaincard->lock,flags); + + /* assign the data received from the hfc-channel "src_hfc_chan" to transmit pcm slot "timeslot" on pin STIO1 */ + /* TX up */ + cwain_outb(cwain_src,cwain_R_FIFO, src_fifo << 1); + cwain_waitbusy(cwain_src); + cwain_outb(cwain_src,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwain_src); + cwain_outb(cwain_src,cwain_A_CON_HDLC,0xC2); + cwain_outb(cwain_src,cwain_A_CHANNEL,src_hfc_chan << 1); + + cwain_outb(cwain_src,cwain_R_SLOT,timeslot << 1); + if (use_pcm_bus) { + cwain_outb(cwain_src,cwain_A_SL_CFG, (src_hfc_chan << 1) | 0 | 0x80); + } else { + cwain_outb(cwain_src,cwain_A_SL_CFG, (src_hfc_chan << 1) | 0 | 0x40); + } + cwain_src->ts_tx[src_chan - 1] = timeslot; + + /* assign the data received from the receive pcm slot "timeslot" on pin STIO1 to the hfc-channel "dst_hfc_chan"*/ + /* RX down */ + cwain_outb(cwain_dst,cwain_R_FIFO, (dst_fifo << 1) | 1); + cwain_waitbusy(cwain_dst); + cwain_outb(cwain_dst,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwain_dst); + cwain_outb(cwain_dst,cwain_A_CHANNEL,(dst_hfc_chan << 1) | 1); + cwain_outb(cwain_dst,cwain_A_CON_HDLC,0xC2); + + cwain_outb(cwain_dst,cwain_R_SLOT,(timeslot << 1) | 1); + if (use_pcm_bus) { + cwain_outb(cwain_dst,cwain_A_SL_CFG, (dst_hfc_chan << 1) | 1 | 0xC0); + } else { + cwain_outb(cwain_dst,cwain_A_SL_CFG, (dst_hfc_chan << 1) | 1 | 0x40); + } + cwain_dst->ts_rx[dst_chan - 1] = timeslot; +// printk(KERN_INFO "cwain: span %d ts_tx[%d] = %d\n", src_span, src_chan - 1, timeslot); +// printk(KERN_INFO "cwain: span %d ts_rx[%d] = %d\n", dst_span, dst_chan - 1, timeslot); + + spin_unlock_irqrestore(&cwaincard->lock,flags); +} + +static void cwain_unassign(struct zt_cwain *cwaintmp, int chan, int timeslot, int lock) { + int fifo = chan - 1; + int hfc_chan = chan; + unsigned long flags = 0; + + if (hw_hdlc && (chan > 0x10)) { + fifo--; + } + + if (lock) spin_lock_irqsave(&cwaintmp->lock,flags); + + /* unassign from_where we receive and to_where we transmit */ + + + /* we were transmitting on cwaintmp->ts_tx[chan - 1] */ + cwain_outb(cwaintmp,cwain_R_SLOT, cwaintmp->ts_tx[chan - 1] << 1); + cwain_outb(cwaintmp,cwain_A_SL_CFG, 0x0); + cwaintmp->ts_tx[chan - 1] = -1; + + /* we were receiving on cwaintmp->ts_rx[chan - 1] */ + cwain_outb(cwaintmp,cwain_R_SLOT, (cwaintmp->ts_rx[chan - 1] << 1) | 1); + cwain_outb(cwaintmp,cwain_A_SL_CFG, 0x0); + cwaintmp->ts_rx[chan - 1] = -1; + + /* restore our tx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,fifo << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,hfc_chan << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + + /* restore our rx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(hfc_chan << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + +// printk(KERN_INFO "cwain: ts_tx[%d] = %d\n", chan - 1, -1); +// printk(KERN_INFO "cwain: ts_rx[%d] = %d\n", chan - 1, -1); + if (lock) spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + + +static int ztcwain_dacs(struct zt_chan *dst, struct zt_chan *src) { + struct zt_cwain *cwaintmp = NULL; + int use_pcm_bus = 0; + int timeslot = 0; + if (hdlcnet) return -1; + if (!dacs) return -1; + + if (src) { + cwaintmp = src->pvt; + + if ((src->pvt != dst->pvt) && (src->span->pvt != dst->span->pvt)) { + if (dacs == 2) { + if (debug) + printk("cwain: Assigning %d/%d -> %d/%d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + timeslot = src->channo; + use_pcm_bus = 1; + } else { + if (debug) + printk("cwain: Not Assigning %d/%d -> %d/%d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + return -1; + } + } else { + if (debug) + printk("cwain: Assigning %d/%d -> %d/%d, same cwain card!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + timeslot = src->channo; +/* if (dacs == 2) { + timeslot = src->channo; + } else { + timeslot = (src->span->offset * 30) + (src->chanpos - 1); + } */ + if (src->span == dst->span) { + use_pcm_bus = 0; + } else { + use_pcm_bus = 1; + } + } + + if (hw_hdlc && ((src->chanpos == 16) || (dst->chanpos == 16))) { + if (debug) + printk("cwain: Not Assigning D-channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } else { + cwain_assign(src->span->pvt, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos, timeslot, use_pcm_bus); + if (debug) + printk("cwain: Assigning channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } + } else { + cwaintmp = dst->pvt; + if (hw_hdlc && (dst->chanpos == 16)) { + if (debug) + printk("cwain: Not Unassigning D-channel %d/%d!\n", dst->span->offset, dst->chanpos); + } else { + timeslot = dst->channo; +/* if (dacs == 2) { + timeslot = dst->channo; + } else { + timeslot = (dst->span->offset * 30) + (dst->chanpos - 1); + } */ +// printk(KERN_INFO "cwain: unassing chan %d ts_rx %d ts_tx %d ?\n", dst->chanpos - 1, cwaintmp->ts_rx[dst->chanpos - 1], cwaintmp->ts_tx[dst->chanpos - 1]); + if ((cwaintmp->ts_rx[dst->chanpos - 1] > -1) || (cwaintmp->ts_tx[dst->chanpos - 1] > -1)) { + cwain_unassign(cwaintmp, dst->chanpos, timeslot, 1); + if (debug) + printk("cwain: Unassigning channel %d/%d!\n", dst->span->offset, dst->chanpos); + } + } + } + return 0; +} + +static inline void cwain_isr_run(struct zt_cwain *cwaintmp, int ticks) { + int dchan; + int chan = 0; + + if (cwaintmp->span.flags & ZT_FLAG_RUNNING) { + + if (hdlcnet) { + dchan = 0; + } else { + dchan = 15; + } + cwaintmp->chans[dchan].bytes2transmit = 0; + cwaintmp->chans[dchan].eoftx = 0; + if (hw_hdlc) { + cwain_dfifo_check(cwaintmp); + } + + /* oh zaptel! tell us what to transmit... */ + zt_transmit(&cwaintmp->span); + + if (hdlcnet) { + + if (cwaintmp->sync) { + cwain_dfifo_tx(cwaintmp); + } + + /* d-chan data */ + while (cwaintmp->drx > 0) { + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].eofrx = 0; + if (debug > 3) + printk(KERN_CRIT "drx = %d\n", cwaintmp->drx); + if (cwain_dfifo_rx(cwaintmp)) { + zt_receive(&(cwaintmp->span)); // XXX + } + } + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].eofrx = 0; + } else { + if (hw_hdlc) { + /* B chans 1-15 mapped to fifos 0-14 */ + /* B chans 17-31 mapped to fifos 15-29 */ + for (chan=0; chan < 31; chan++) { + /* copy to fbuffer */ + if ((ticks < 1) || (ticks > 8)) { + printk(KERN_INFO "cwain: whicked ticks make whicked tricks (%d)\n",cwaintmp->ticks); + } else { + if (chan != dchan) { + memcpy(&cwaintmp->ftxbuf[chan][(ticks-1)*8], cwaintmp->txbuf[chan], ZT_CHUNKSIZE); + } + } + } + + if (cwaintmp->sync) { + cwain_dfifo_tx(cwaintmp); + } + + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].bytes2transmit = 0; + cwaintmp->chans[dchan].eofrx = 0; + cwaintmp->chans[dchan].eoftx = 0; + + for (chan=0; chan < 31; chan++) { + /* copy from fbuffer */ + if (chan != dchan) { + memcpy(cwaintmp->rxbuf[chan], &cwaintmp->frxbuf[chan][(ticks-1)*8], ZT_CHUNKSIZE); + zt_ec_chunk(&cwaintmp->span.chans[chan], cwaintmp->span.chans[chan].readchunk, cwaintmp->span.chans[chan].writechunk); + } + } + + /* d-chan data */ + if (cwaintmp->drx > 0) { + if (debug > 3) + printk(KERN_CRIT "drx = %d\n", cwaintmp->drx); + cwain_dfifo_rx(cwaintmp); + } + } else { + /* software HDLC */ + for (chan=0; chan < 31; chan++) { + /* copy to fbuffer */ + if ((ticks < 1) || (ticks > 8)) { + printk(KERN_INFO "cwain: whicked ticks make whicked tricks (%d)\n",cwaintmp->ticks); + } else { + memcpy(&cwaintmp->ftxbuf[chan][(ticks-1)*8], cwaintmp->txbuf[chan], ZT_CHUNKSIZE); + } + } + + for (chan=0; chan < 31; chan++) { + /* copy from fbuffer */ + memcpy(cwaintmp->rxbuf[chan], &cwaintmp->frxbuf[chan][(ticks-1)*8], ZT_CHUNKSIZE); + zt_ec_chunk(&cwaintmp->span.chans[chan], cwaintmp->span.chans[chan].readchunk, cwaintmp->span.chans[chan].writechunk); + } + } + } + /* oh zaptel! thou shall receive! */ + zt_receive(&(cwaintmp->span)); + } +} + +static inline void cwain_isr_err(struct zt_cwain *cwaintmp) { + unsigned long flags = 0; + unsigned short crc, vio, ebit, fas; + unsigned short rx_slip, tx_slip; + + spin_lock_irqsave(&cwaintmp->lock,flags); + crc = (cwain_inb(cwaintmp, cwain_R_CRC_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_CRC_ECL); + vio = (cwain_inb(cwaintmp, cwain_R_VIO_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_VIO_ECL); + ebit = (cwain_inb(cwaintmp, cwain_R_E_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_E_ECL); + fas = (cwain_inb(cwaintmp, cwain_R_FAS_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_FAS_ECL); + rx_slip = cwain_inb(cwaintmp, cwain_R_SLIP) & 0x01; + tx_slip = (cwain_inb(cwaintmp, cwain_R_SLIP) >> 4) & 0x01; + if (cwaintmp->sync) { + if (rx_slip) { + cwain_outb(cwaintmp,cwain_R_RX_OFFS,0x06); + if (debug) + printk(KERN_INFO "cwain: cardID %d detected RX slip\n", cwaintmp->cardID); + } + if (tx_slip) { + cwain_outb(cwaintmp,cwain_R_TX_OFFS,0x06); + if (debug) + printk(KERN_INFO "cwain: cardID %d detected TX slip\n", cwaintmp->cardID); + } + } + + cwaintmp->span.crc4count += crc; + cwaintmp->span.bpvcount += vio; + cwaintmp->span.ebitcount += ebit; + cwaintmp->span.fascount += fas; + + if ((debug > 3) && (crc || vio || ebit || fas)) + printk(KERN_INFO "cwain: CRC4 %d BPVIOL %d EBIT %d FAS %d\n", cwaintmp->span.crc4count, cwaintmp->span.bpvcount, cwaintmp->span.ebitcount, cwaintmp->span.fascount); + spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + + +static inline void cwain_audio_run(struct zt_cwain *cwaintmp) { + int fifo=0; + + if (cwaintmp->wdp) { + if (cwaintmp->wdp == 0x04) { + cwaintmp->wdp = 0x08; + } else { + cwaintmp->wdp = 0x04; + } + cwain_outb(cwaintmp, cwain_R_GPIO_OUT0, cwaintmp->wdp); + } + + if (hdlcnet) return; + + if (hw_hdlc) { + for (fifo=0; fifo < 30; fifo++) { + /* B tx */ + cwain_fifo_tx(cwaintmp, fifo); + } + + for (fifo=0; fifo < 30; fifo++) { + /* B rx */ + cwain_fifo_rx(cwaintmp, fifo); + } + } else { + /* software HDLC */ + for (fifo=0; fifo < 31; fifo++) { + /* B tx */ + cwain_fifo_tx(cwaintmp, fifo); + } + + for (fifo=0; fifo < 31; fifo++) { + /* B rx */ + cwain_fifo_rx(cwaintmp, fifo); + } + } +} + +int cwain_isr_sync(struct zt_cwain *cwainspan) { + unsigned char sync_sta; + unsigned char sync_ok = 0; + unsigned char jatt_sta = 0; + unsigned long flags = 0; +// int chan = 0; + int res = 0; /* assume no l1event */ + + if (!cwainspan->span.flags & ZT_FLAG_RUNNING) { + return res; + } + + spin_lock_irqsave(&cwainspan->lock,flags); + sync_sta = cwain_inb(cwainspan, cwain_R_SYNC_STA); + + if ((!cwainspan->sync) || (sync_sta != cwainspan->sync_sta)) { + + if (debug > 2) + printk(KERN_CRIT "cwain: cardID %d R_SYNC_STA =%#x\n", cwainspan->cardID, sync_sta); + + if (cwainspan->span.lineconfig & ZT_CONFIG_CRC4) { + if ((sync_sta & 0x80) == 0x80) { + /* reset MFA detection */ + cwain_outb(cwainspan ,cwain_R_RX_SL0_CFG1,0x41); + } else if ((sync_sta & 0x27) == 0x27) { + if (((cwainspan->sync_sta & 0x27) != 0x27) && cwainspan->span.syncsrc) { + /* sync achieved, restart JATT */ +/* if (debug) + printk(KERN_INFO "cwain: %d starting jitter attenuator\n", cwainspan->cardID); */ +// cwain_outb(cwainspan, cwain_R_JATT_CFG,0x9c); + } + sync_ok = 0x27; + } else { + sync_ok = 0x00; + } + } else { + if ((sync_sta & 0x07) == 0x07) { + if (((cwainspan->sync_sta & 0x7) != 0x7) && cwainspan->span.syncsrc) { + /* sync achieved, restart JATT */ + /* if (debug) + printk(KERN_INFO "cwain: %d starting jitter attenuator\n", cwainspan->cardID); */ +// cwain_outb(cwainspan, cwain_R_JATT_CFG,0x9c); + } + sync_ok = 0x07; + } else { + sync_ok = 0x00; + } + } + + cwainspan->sync_sta = sync_sta; + + if (cwainspan->span.syncsrc) { + jatt_sta = cwain_inb(cwainspan, cwain_R_JATT_STA); + if ((jatt_sta & 0x60) != 0x60) { + if (debug > 2) + printk(KERN_INFO "cwain: %d jitter attenuator %#x\n", cwainspan->cardID, (jatt_sta & 0x60) >> 5); + sync_ok = 0x00; + } else if (!cwainspan->sync && sync_ok) { + if (debug) + printk(KERN_CRIT "cwain: %d jitter attenuator %#x ok!\n", cwainspan->cardID, (jatt_sta & 0x60) >> 5); + } + } + + if (sync_ok && (!cwainspan->sync)) { + if (debug) + printk(KERN_INFO "cwain: cardID %d link up\n", cwainspan->cardID); + + cwain_outb(cwainspan,cwain_R_RX_OFFS,0x06); + cwain_outb(cwainspan,cwain_R_TX_OFFS,0x06); + + cwainspan->span.crc4count = 0; + cwainspan->span.bpvcount = 0; + cwainspan->span.ebitcount = 0; + cwainspan->span.fascount = 0; + cwainspan->span.alarms = ZT_ALARM_NONE; + zt_alarm_notify(&cwainspan->span); + res = 1; + } + if (!sync_ok && cwainspan->sync) { + if (debug) + printk(KERN_INFO "cwain: cardID %d link down\n", cwainspan->cardID); + + cwainspan->span.crc4count = 0; + cwainspan->span.bpvcount = 0; + cwainspan->span.ebitcount = 0; + cwainspan->span.fascount = 0; + cwainspan->span.alarms = ZT_ALARM_RED; + zt_alarm_notify(&cwainspan->span); + res = 1; + } + + cwainspan->sync = sync_ok; + if (sync_ok) { + switch (cwainspan->type) { + case 0xb553: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + case 0xb554: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + case 0xb555: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + } + } else { + switch (cwainspan->type) { + case 0xb553: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + case 0xb554: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + case 0xb555: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + } + } + cwain_doLEDs(cwainspan); + } + spin_unlock_irqrestore(&cwainspan->lock,flags); + return res; +} + +static int ztcwain_proc_read(struct zt_span *span, char *output) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + unsigned char fstate; + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + fstate = cwain_inb(cwaintmp, cwain_R_E1_RD_STA); + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return sprintf(output, "cwain: span state = F%d.", fstate & 0x7); +} + +/* called locked */ +int cwain_isr_fifo(struct zt_cwain *cwainspan, unsigned char status) { + unsigned char irq_foview,fi; + + if (status & 0x80) { + /* fifo irq */ + irq_foview = cwain_inb(cwainspan,cwain_R_IRQ_OVIEW); + if (irq_foview & 0x80) { + fi = cwain_inb(cwainspan,cwain_R_IRQ_FIFO_BL7); + if (fi & 0x80) { + if (debug > 4) + printk(KERN_CRIT "cwain: fifo 31 RX irq for D channel cardID %d\n", cwainspan->cardID); + } + if (fi & 0x40) { + if (debug > 4) + printk(KERN_CRIT "cwain: fifo 31 TX irq for D channel cardID %d\n", cwainspan->cardID); + } + } + return 1; + } + return 0; +} + +ZAP_IRQ_HANDLER(cwain_dummy_interrupt) { + struct zt_cwain_card *cwaintmp = dev_id; + if (!cwaintmp) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + if (debug > 3) + printk(KERN_INFO "cwain: dummy irq\n"); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + + +ZAP_IRQ_HANDLER(cwain_interrupt) { + struct zt_cwain_card *cwaintmp = dev_id; + unsigned char status, status2, status_tmp, irq_misc, irq_misc2 = 0; + unsigned long flags; + int i = 0; + int l1event = 0; + + if (!cwaintmp || cwaintmp->dead) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + spin_lock_irqsave(&(cwaintmp->lock),flags); + status = cwain_inb(cwaintmp->span[0],cwain_R_STATUS); + status2 = 0; + + for (i=0;ispans;i++) { + if (i == 0) { + status_tmp = status; + } else { + status_tmp = cwain_inb(cwaintmp->span[i],cwain_R_STATUS); + status2 = status_tmp; + } + cwain_isr_fifo(cwaintmp->span[i], status_tmp); + } + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + + if (!(status & 0x80) && !(status & 0x40)) { + // it's not us! +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + // misc irq + if (status & 0x40) { + spin_lock_irqsave(&(cwaintmp->lock),flags); + irq_misc = cwain_inb(cwaintmp->span[0],cwain_R_IRQ_MISC); + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + if (irq_misc & 0x2) { + /* cwain timer */ + cwaintmp->ticks++; + if (cwaintmp->ticks == 1) { + for (i=0;ispans;i++) { + cwain_audio_run(cwaintmp->span[i]); + } + } + for (i=0;ispans;i++) { + cwain_isr_run(cwaintmp->span[i], cwaintmp->ticks); + } + if (cwaintmp->ticks == (cwain_FRAME_SIZE / ZT_CHUNKSIZE)) { + cwaintmp->ticks = 0; + } + } + if (irq_misc & 0x1) { + /* state machine */ + if (debug > 4) + printk(KERN_INFO "cwain: state machine irq\n"); + l1event++; + } + if (irq_misc & 0x10) { + for (i=0;ispans;i++) { + cwain_isr_err(cwaintmp->span[i]); + } + if (l1event == 0) { + /* just in case we missed it */ + for (i=0;ispans;i++) { + l1event += cwain_isr_sync(cwaintmp->span[i]); + } + } + } + } + + // misc irq + if (status2 & 0x40) { + if (cwaintmp->spans == 2) { + spin_lock_irqsave(&(cwaintmp->lock),flags); + irq_misc2 = cwain_inb(cwaintmp->span[1],cwain_R_IRQ_MISC); + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + } + if (irq_misc2 & 0x1) { + /* state machine 2 */ + if (debug > 4) + printk(KERN_INFO "cwain: state machine 2 irq\n"); + l1event++; + } + } + + if (l1event > 0) { +// printk(KERN_INFO "cwain: l1event %d\n", l1event); + for (i=0;ispans;i++) { + cwain_isr_sync(cwaintmp->span[i]); + } + if (cwaintmp->spans == 2) { + cwain_check_timing(cwaintmp); + } + } + +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +static int ztcwain_open(struct zt_chan *chan) { +// printk(KERN_INFO "cwain: channel %d opened.\n",chan->channo); +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int ztcwain_close(struct zt_chan *chan) { +// printk(KERN_INFO "cwain: channel %d closed.\n",chan->channo); +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static int ztcwain_rbsbits(struct zt_chan *chan, int bits) { + return 0; +} + +static int ztcwain_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int ztcwain_startup(struct zt_span *span) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + int alreadyrunning; + int i=0; + int idx = 0; + +// printk(KERN_INFO "cwain: startup spanno %d offset %d\n", span->spanno, span->offset); + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; +// printk(KERN_CRIT "already running %d flags %d\n", alreadyrunning, span->flags); + + if (!alreadyrunning) { + for (i=0; i<31 ; i++) { + cwaintmp->ts_rx[i] = -1; + cwaintmp->ts_tx[i] = -1; + } + + if (hdlcnet) { + span->chans[0].flags &= ~ZT_FLAG_HDLC; + span->chans[0].flags |= ZT_FLAG_BRIDCHAN; /* yes! */ + + memset(cwaintmp->dtxbuf,0x0,sizeof(cwaintmp->dtxbuf)); + span->chans[0].writechunk = cwaintmp->dtxbuf; + + cwaintmp->chans[0].maxbytes2transmit = 248; +// cwaintmp->chans[0].maxbytes2transmit = 64; + + memset(cwaintmp->drxbuf,0x0,sizeof(cwaintmp->drxbuf)); + span->chans[0].readchunk = cwaintmp->drxbuf; + } else { + if (hw_hdlc) { + span->chans[15].flags &= ~ZT_FLAG_HDLC; + span->chans[15].flags |= ZT_FLAG_BRIDCHAN; /* yes! */ + + /* setup B channel buffers (8 bytes each) */ + for (i=0; i<31 ; i++) { + if (i != 15) { + memset(cwaintmp->rxbuf[i],0x0,sizeof(cwaintmp->rxbuf[i])); + memset(cwaintmp->txbuf[i],0x0,sizeof(cwaintmp->txbuf[i])); + span->chans[i].readchunk = cwaintmp->rxbuf[i]; + span->chans[i].writechunk = cwaintmp->txbuf[i]; + } + } + /* setup D channel buffer */ + memset(cwaintmp->dtxbuf,0x0,sizeof(cwaintmp->dtxbuf)); + span->chans[15].writechunk = cwaintmp->dtxbuf; + cwaintmp->chans[15].maxbytes2transmit = sizeof(cwaintmp->dtxbuf) / 2; + + memset(cwaintmp->drxbuf,0x0,sizeof(cwaintmp->drxbuf)); + span->chans[15].readchunk = cwaintmp->drxbuf; + } else { + /* software HDLC */ + /* setup B channel buffers (8 bytes each) */ + for (i=0; i<31 ; i++) { + memset(cwaintmp->rxbuf[i],0x0,sizeof(cwaintmp->rxbuf[i])); + memset(cwaintmp->txbuf[i],0x0,sizeof(cwaintmp->txbuf[i])); + span->chans[i].readchunk = cwaintmp->rxbuf[i]; + span->chans[i].writechunk = cwaintmp->txbuf[i]; + } + } + } + span->flags |= ZT_FLAG_RUNNING; + } else { + printk(KERN_CRIT "already running\n"); + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* irqs off */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + + if (hdlcnet) { + /* map ts 1 to 31 to fifo 31 */ + /* TX fifo */ +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + cwain_outb(cwaintmp,cwain_R_FIRST_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + + idx = 0; + for (i=1; i<=31; i++) { + cwain_outb(cwaintmp,cwain_R_FSM_IDX, idx++); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + if (i == 31) { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, (0x1F << 1) | 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i , (0x1f << 1) | 1); + } else { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, 0x1F << 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i ,0x1f << 1); + } + } + + /* map ts 1 to 31 to fifo 31 */ + /* RX fifo */ + + for (i=1; i<=31; i++) { + cwain_outb(cwaintmp,cwain_R_FSM_IDX, idx++); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + if (i == 31) { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, 0x40); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i, 0x40); + } else { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, (0x1F << 1) | 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i, (0x1f << 1) | 1); + } + } + +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x36); + + cwain_outb(cwaintmp,cwain_R_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xc); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK, 0x1); + + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xc); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK, 0x1); + +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + + } else { + if (hw_hdlc) { + /* setup D-FIFO TX */ + cwain_outb(cwaintmp,cwain_R_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xd); + cwain_outb(cwaintmp,cwain_A_SUBCH_CFG,0x0); + cwain_outb(cwaintmp,cwain_A_CHANNEL,0x10 << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x1); + + /* setup D-FIFO RX */ + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xd); + cwain_outb(cwaintmp,cwain_A_SUBCH_CFG,0x0); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(0x10 << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x1); + + /* setup B-FIFOs TX */ + /* map ts 1 to 15 to fifos 0 to 14 */ + for (i=1; i<16 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 1) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* map ts 17 to 31 to fifos 15 to 29 */ + for (i=17; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 2) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + /* setup B-FIFOs RX */ + /* map ts 1 to 15 to fifos 0 to 14 */ + for (i=1; i<16 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-1) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* map ts 17 to 31 to fifos 15 to 29 */ + for (i=17; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-2) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + } else { + /* software HDLC */ + /* setup B-FIFOs TX */ + for (i=1; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 1) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* setup B-FIFOs RX */ + for (i=1; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-1) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + } + } + + if (debug) + printk(KERN_INFO "cwain: starting card %d span %d/%d.\n",cwaintmp->cardno,span->spanno,span->offset); + + if (cwaincard->spans == 1) { + cwain_set_master(cwaincard, 0); + } + + /* setup E1 amplitude */ + cwain_outb(cwaintmp,cwain_R_PWM_MD,0x20); + cwain_outb(cwaintmp,cwain_R_PWM0,pwm0); + cwain_outb(cwaintmp,cwain_R_PWM1,pwm1); + + /* setup E1 transceiver */ + cwain_outb(cwaintmp,cwain_R_TX_SL0,0xf8); + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG0,0x00); /* semiautomatic mode */ + + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG0,0x6); + + if (cwaintmp->span.lineconfig & ZT_CONFIG_AMI) { + cwain_outb(cwaintmp,cwain_R_TX0,0x82); + cwain_outb(cwaintmp,cwain_R_RX0,0x02); + } else if (cwaintmp->span.lineconfig & ZT_CONFIG_HDB3) { + cwain_outb(cwaintmp,cwain_R_TX0,0x81); + cwain_outb(cwaintmp,cwain_R_RX0,0x01); + } + cwain_outb(cwaintmp,cwain_R_TX1,0x60); /* transmitter mode */ + + cwain_outb(cwaintmp,cwain_R_LOS0,0x0f); + cwain_outb(cwaintmp,cwain_R_LOS1,0x0f); + + if (cwaintmp->span.lineconfig & ZT_CONFIG_CRC4) { + /* crc4 multiframe */ + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG1,0x31); + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG1,0x03); + } else { + /* doubleframe */ + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG1,0x0); + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG1,0x0); + } + + + /* automatic JATT */ + cwain_outb(cwaintmp, cwain_R_JATT_CFG,0x9c); + + /* setup sync mode */ + if (cwaincard->syncs[span->offset] > 0) { + cwain_outb(cwaintmp,cwain_R_SYNC_CTRL,0x2); // phase offset arb. + cwain_outb(cwaintmp,cwain_R_SYNC_OUT,0x00); // sync from e1 tx + /* layer 1, here we go! */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x00); + } else { + cwain_outb(cwaintmp,cwain_R_SYNC_CTRL,0x5); // pcm_f0IO + cwain_outb(cwaintmp,cwain_R_SYNC_OUT,0x00); // sync from e1 tx + /* layer 1, up! */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x11); + } + + /* elastic buffer offsets */ + cwain_outb(cwaintmp,cwain_R_RX_OFFS,0x06); + cwain_outb(cwaintmp,cwain_R_TX_OFFS,0x06); + + cwaintmp->sync = 0; + cwaintmp->sync_sta = 0; + + /* enable irqs */ + if (!span->offset) { + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 8 | 1); + } else { + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0x1); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +static int ztcwain_shutdown(struct zt_span *span) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + int alreadyrunning; + int i=0; + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + return 0; + } + spin_lock_irqsave(&cwaintmp->lock,flags); + +// printk(KERN_CRIT "cwain: stopping card %d span %d/%d.\n",cwaintmp->cardno,span->spanno,span->offset); + + // turn off irqs for all fifos + if (hdlcnet) { + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } else { + /* disable FIFO TX */ + for (i=0; i<0x20 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,i << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + /* disable FIFO RX */ + for (i=0; i<0x20 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + } + + /* Deactivate Layer 1 */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x10); + + /* Disable transmit */ + if (cwaintmp->span.lineconfig & ZT_CONFIG_AMI) { + cwain_outb(cwaintmp,cwain_R_TX0,0x02); + } else if (cwaintmp->span.lineconfig & ZT_CONFIG_HDB3) { + cwain_outb(cwaintmp,cwain_R_TX0,0x01); + } + + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + cwain_inb(cwaintmp,cwain_R_STATUS); + + cwain_outb(cwaintmp,cwain_R_GPIO_EN0,0x00); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x00); + + + span->flags &= ~ZT_FLAG_RUNNING; + + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + +// printk(KERN_CRIT "cwain: card %d span %d/%d down.\n",cwaintmp->cardno,span->spanno,span->offset); + return 0; +} + +static int ztcwain_maint(struct zt_span *span, int cmd) { + return 0; +} + +static int ztcwain_chanconfig(struct zt_chan *chan,int sigtype) { +// printk(KERN_INFO "chan_config sigtype=%d\n", sigtype); + return 0; +} + +static int ztcwain_spanconfig(struct zt_span *span,struct zt_lineconfig *lc) { + struct zt_cwain_card *cwaincard = span->pvt; + int alreadyrunning; + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + span->lineconfig = lc->lineconfig; + span->syncsrc = lc->sync; + + cwaincard->syncs[span->offset] = lc->sync; + cwaincard->syncsrc = -1; + } +// printk(KERN_INFO "span_config %d lineconfig=%d syncsrc=%d\n", span->spanno, lc->lineconfig, lc->sync); +// cwain_check_timing(cwaincard); + return 0; +} + +static int ztcwain_initialize(struct zt_cwain *cwaintmp, struct zt_cwain_card *cwaincard, int offset) { + int i=0; + + memset(&cwaintmp->span,0,sizeof(struct zt_span)); // you never can tell... + sprintf(cwaintmp->span.name,"cwain/%d",cwaintmp->cardno); + switch (cwaintmp->type) { + case 0xb553: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d)",cwaintmp->cardno,cwaintmp->cardID); + break; + case 0xb554: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + break; + case 0xb555: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + cwaintmp->wdp = 0x04; + break; + default: + return -1; + } + cwaintmp->span.spanconfig = ztcwain_spanconfig; + cwaintmp->span.chanconfig = ztcwain_chanconfig; + cwaintmp->span.startup = ztcwain_startup; + cwaintmp->span.shutdown = ztcwain_shutdown; + cwaintmp->span.maint = ztcwain_maint; + cwaintmp->span.rbsbits = ztcwain_rbsbits; + cwaintmp->span.open = ztcwain_open; + cwaintmp->span.close = ztcwain_close; + cwaintmp->span.ioctl = ztcwain_ioctl; + cwaintmp->span.proc_read = ztcwain_proc_read; + cwaintmp->span.dacs = ztcwain_dacs; + + cwaintmp->span.chans = cwaintmp->chans; + if (hdlcnet) { + cwaintmp->span.channels = 1; + } else { + cwaintmp->span.channels = 31; + } + cwaintmp->span.deflaw = ZT_LAW_ALAW; + cwaintmp->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_HDB3 | ZT_CONFIG_CCS; + init_waitqueue_head(&cwaintmp->span.maintq); + cwaintmp->span.pvt = cwaincard; + cwaintmp->span.offset = offset; + + for (i=0; i < cwaintmp->span.channels; i++) { + memset(&(cwaintmp->chans[i]),0x0,sizeof(struct zt_chan)); + sprintf(cwaintmp->chans[i].name,"cwain%d/%d",cwain_span_count + 1,i + 1); + cwaintmp->chans[i].pvt = cwaintmp; + cwaintmp->chans[i].sigcap = ZT_SIG_CLEAR | ZT_SIG_DACS; + cwaintmp->chans[i].chanpos = i + 1; + } + + if (zt_register(&cwaintmp->span,0)) { + printk(KERN_INFO "cwain: unable to register zaptel span!\n"); + return -1; + } +// printk(KERN_INFO "cwain: registered zaptel span %d.\n",s+1); + + return 0; +} + +int cwain_reset_card(struct zt_cwain_card *cwaintmp) { + unsigned long flags; + int i = 0; + + cwaintmp->irq = cwaintmp->span[0]->irq; + + if (cwaintmp->spans == 2) { + if (request_irq(cwaintmp->irq, cwain_interrupt, ZAP_IRQ_SHARED, "cwain2", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + if (request_irq(cwaintmp->span[1]->irq, cwain_dummy_interrupt, ZAP_IRQ_SHARED, "cwaindummy", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + } else { + if (request_irq(cwaintmp->irq, cwain_interrupt, ZAP_IRQ_SHARED, "cwain", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + } + + spin_lock_irqsave(&(cwaintmp->lock),flags); + + for (i=0;ispans;i++) { + cwain_reset_span(cwaintmp->span[i]); + } + + /* no master yet, force reelection */ + cwaintmp->syncsrc = -1; + +#if (ZT_CHUNKSIZE == 32) + /* set up the timer 250 Hz, zaptel timing */ + cwain_outb(cwaintmp->span[0],cwain_R_TI_WD, 0x4); +#endif +#if (ZT_CHUNKSIZE == 8) + /* set up the timer 1 khz, zaptel timing */ + cwain_outb(cwaintmp->span[0],cwain_R_TI_WD, 0x2); +#endif + + if (cwaintmp->spans == 2) { +// cwain_outb(cwaintmp->span[1],cwain_R_IRQMSK_MISC, 0x1); + cwain_outb(cwaintmp->span[1],cwain_R_IRQMSK_MISC, 0x0); + } + /* enable timer interrupts */ + cwain_outb(cwaintmp->span[0],cwain_R_IRQMSK_MISC, 0x13); + + /* Finally enable IRQ output */ +// cwain_outb(cwaintmp->span[0],cwain_R_IRQ_CTRL, 0x8 | 0x1); + + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + return 0; +} + +int cwain_find_spans(unsigned int pcidid) { + struct pci_dev *tmp; + struct zt_cwain *cwaintmp = NULL; + int i=0; + unsigned char dips=0; + int cid=0; + int modes=0; + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_cwain); + while (tmp != NULL) { + multi_cwain = tmp; // skip this next time. + + if (pci_enable_device(tmp)) { + multi_cwain = NULL; + return -1; + } + + cwaintmp = kmalloc(sizeof(struct zt_cwain),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -ENOMEM; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcidev = tmp; + cwaintmp->pcibus = tmp->bus->number; + cwaintmp->pcidevfn = tmp->devfn; + + + cwaintmp->pci_io_phys = (char *) tmp->resource[1].start; + if (!cwaintmp->pci_io_phys) { + printk(KERN_WARNING "cwain: no iomem!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + if (!tmp->irq) { + printk(KERN_WARNING "cwain: PCI device has no irq!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + cwaintmp->ioport = tmp->resource[0].start; + if (!cwaintmp->ioport) { + printk(KERN_WARNING "cwain: no ioport!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + if (!request_region(cwaintmp->ioport, 8, "cwain")) { + printk(KERN_WARNING "cwain: couldnt request io range!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + if (!request_mem_region((unsigned long) cwaintmp->pci_io_phys, 256, "cwain")) { + printk(KERN_WARNING "cwain: couldnt request io mem range!\n"); + release_region(cwaintmp->ioport, 8); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + cwaintmp->irq = tmp->irq; + + cwaintmp->pci_io = ioremap((ulong) cwaintmp->pci_io_phys, 256); + + /* enable memio */ + pci_write_config_word(cwaintmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + + /* disable interrupts */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + + if (((tmp->subsystem_device==0xb553)|| (tmp->subsystem_device==0xb554) || (tmp->subsystem_device==0xb555))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { + dips = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 5); + cid = 7; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) != 0) { + cid -= (1 << (2-i)); + } + } +// printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + } else { + cid = 0xff; + } + + if (ports == -1) { + if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_E)) { + modes = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 4) & 0x01; + } else { + modes = 0; // assume TE mode + } + } else { + modes = ports >> cwain_span_count; + } + + + cwaintmp->cardID = cid; + cwaintmp->type = tmp->subsystem_device; + + if ((modes & 1) != 0) { + cwaintmp->nt_mode = 1; + } else { + cwaintmp->nt_mode = 0; + } + + cwain_register_span(cwaintmp); + + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_cwain); + } + return 0; +} + + +int cwain_sort_cards(void) { + int changed=0,tmpcardno; + struct zt_cwain_card *tmpcard,*tmpcard2; + spin_lock(&cwain_card_registerlock); + do { + changed = 0; + tmpcard = cwain_card_list; + while (tmpcard != NULL) { + if (tmpcard->prev) { + if (tmpcard->prev->cardID > tmpcard->cardID) { + tmpcardno = tmpcard->prev->cardno; + tmpcard->prev->cardno = tmpcard->cardno; + tmpcard->cardno = tmpcardno; + + tmpcard2 = tmpcard->prev; + if (tmpcard2->prev) { + tmpcard2->prev->next = tmpcard; + } else { + cwain_card_list = tmpcard; + } + if (tmpcard->next) { + tmpcard->next->prev = tmpcard2; + } + tmpcard2->next = tmpcard->next; + tmpcard->prev = tmpcard2->prev; + tmpcard->next = tmpcard2; + tmpcard2->prev = tmpcard; + changed = 1; + tmpcard = tmpcard2; + } + } + tmpcard = tmpcard->next; + } + } while (changed == 1); + spin_unlock(&cwain_card_registerlock); + return 0; +} + +int cwain_zap_cards(void) { + struct zt_cwain_card *tmpcard = cwain_card_list; + int i=0; + int res=0; + while (tmpcard != NULL) { + for (i=0; ispans; i++) { + ztcwain_initialize(tmpcard->span[i], tmpcard, i); + } + res = cwain_reset_card(tmpcard); + tmpcard = tmpcard->next; + } + return res; +} + + +int init_module(void) { + if (hdlcnet) { + hw_hdlc = 1; /* otherwise it makes no sense at all... */ + dacs = 0; + } + multi_cwain = NULL; + cwain_find_spans(PCI_DEVICE_ID_CCD_E); + cwain_sort_cards(); + cwain_zap_cards(); + if (cwain_card_count == 0) { + printk(KERN_INFO "cwain: no cwain cards found.\n"); + } else { + printk(KERN_INFO "cwain: %d cwain card(s) in this box, %d E1 ports total, hw_hdcl = %d, dacs =%d, require_ext_clock = %d, ZT_CHUNKSIZE = %d, timer = %d.\n", cwain_card_count, cwain_span_count, hw_hdlc, dacs, require_ext_clock, ZT_CHUNKSIZE, cwain_TIMER_INT); + } + return 0; +} + +void cleanup_module(void) { + struct zt_cwain_card *tmpcard,*tmplist; + struct zt_cwain *tmpspan,*spanlist; + int i=0; + int j=0; + + tmplist = cwain_card_list; + tmpcard = NULL; + while (tmplist) { + tmpcard = tmplist; + tmplist = tmplist->next; + + tmpcard->dead = 1; + cwain_shutdown_card(tmpcard); + kfree(tmpcard); + i++; + } + + spanlist = cwain_span_list; + tmpspan = NULL; + while (spanlist) { + tmpspan = spanlist; + spanlist = spanlist->next; + kfree(tmpspan); + j++; + } + printk(KERN_INFO "cwain: shutdown %d spans, %d cwain cards.\n", j, i); +} +#endif + +#ifdef LINUX26 +module_param(ports, int, 0600); +module_param(debug, int, 0600); +module_param(hw_hdlc, int, 0600); +module_param(hdlcnet, int, 0600); +module_param(pwm0, int, 0600); +module_param(pwm1, int, 0600); +module_param(dacs, int, 0600); +module_param(require_ext_clock, int, 0600); +#else +MODULE_PARM(ports,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(hw_hdlc,"i"); +MODULE_PARM(hdlcnet,"i"); +MODULE_PARM(pwm0,"i"); +MODULE_PARM(pwm1,"i"); +MODULE_PARM(dacs,"i"); +MODULE_PARM(require_ext_clock,"i"); +#endif + +MODULE_DESCRIPTION("cwain zaptel driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/cwain.h +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/cwain.h @@ -0,0 +1,252 @@ +#define cwain_FIFO_SIZE 128 +#define cwain_DFIFO_SIZE 4096 +#define cwain_DFIFO_FRAMES 0x10 +#define cwain_FRAME_SIZE 16 /* has to be %4==0 */ +#define cwain_FIFO_HW cwain_FRAME_SIZE * 2 + ZT_CHUNKSIZE +#define cwain_TIMER_INT (8000 / ZT_CHUNKSIZE) + + +typedef struct zt_cwain { + /* pci resources */ + unsigned int irq; + unsigned int iomem; + unsigned long ioport; + unsigned char *pci_io; + void *pci_io_phys; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + unsigned int type; + + /* lock to protect the structure */ + spinlock_t lock; + int cardID; + unsigned char cardno; + + /* ticks and clicks, fish and chips */ + unsigned int ticks; + unsigned int clicks; + unsigned char nt_mode; + unsigned char sync_sta; + unsigned char sync; + int syncmode; + + /* blinky blink */ + unsigned char leds[4]; + unsigned int wdp; + + /* B chan buffers */ + unsigned char rxbuf[31][ZT_CHUNKSIZE]; + unsigned char txbuf[31][ZT_CHUNKSIZE]; + + signed short ts_rx[31]; + signed short ts_tx[31]; + + /* buffers */ + unsigned char frxbuf[31][cwain_FRAME_SIZE]; + unsigned char ftxbuf[31][cwain_FRAME_SIZE]; + + /* number of RXed dchan frames */ + unsigned char drx; + /* D chan buffer */ + unsigned char drxbuf[cwain_DFIFO_SIZE]; + unsigned char dtxbuf[cwain_DFIFO_SIZE]; + + + /* zaptel resources */ + struct zt_span span; + struct zt_chan chans[31]; + + /* more zaptel stuff */ + unsigned int usecount; + int spantype; + int spanflags; + + /* linked list */ + struct zt_cwain *next; + struct zt_cwain *prev; + +} zt_cwain; + +typedef struct zt_cwain_card { + /* lock to protect the structure */ + spinlock_t lock; + + unsigned int spans; + unsigned int dead; + int cardID; + unsigned char cardno; + unsigned int ticks; + + struct zt_cwain *span[2]; + int syncsrc; + int syncs[2]; + int master[2]; + + unsigned int irq; + unsigned int pcibus; + + /* linked list */ + struct zt_cwain_card *next; + struct zt_cwain_card *prev; +} zt_cwain_card; + +#define cwain_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define cwain_inb(a,b) (readb((a)->pci_io+(b))) + +#define cwain_outw(a,b,c) (writew((c),(a)->pci_io+(b))) +#define cwain_inw(a,b) (readw((a)->pci_io+(b))) + +#define cwain_outdw(a,b,c) (writel((c),(a)->pci_io+(b))) +#define cwain_indw(a,b) (readl((a)->pci_io+(b))) + + +/* Write only registers */ +#define cwain_A_CH_MSK 0xF4 +#define cwain_A_CHANNEL 0xFC +#define cwain_A_CON_HDLC 0xFA +#define cwain_A_CONF 0xD1 +#define cwain_A_FIFO_SEQ 0xFD +#define cwain_R_INC_RES_FIFO 0x0E +#define cwain_A_IRQ_MSK 0xFF +#define cwain_A_SL_CFG 0xD0 +#define cwain_A_SUBCH_CFG 0xFB +#define cwain_R_BERT_WD_MD 0x1B +#define cwain_R_BRG_PCM_CFG 0x02 +#define cwain_R_CIRM 0x00 +#define cwain_R_CONF_EN 0x18 +#define cwain_R_CTRL 0x01 +#define cwain_R_DTMF 0x1C +#define cwain_R_DTMF_N 0x1D +#define cwain_R_E1_WR_STA 0x20 +#define cwain_R_FIFO_MD 0x0D +#define cwain_R_FIFO 0x0F +#define cwain_R_FIRST_FIFO 0x0B +#define cwain_R_FSM_IDX 0x0F +#define cwain_R_GPIO_EN0 0x42 +#define cwain_R_GPIO_EN1 0x43 +#define cwain_R_GPIO_OUT0 0x40 +#define cwain_R_GPIO_OUT1 0x41 +#define cwain_R_GPIO_SEL 0x44 +#define cwain_R_IRQ_CTRL 0x13 +#define cwain_R_IRQMSK_MISC 0x11 +#define cwain_R_JATT_CFG 0x2F +#define cwain_R_LOS0 0x22 +#define cwain_R_LOS1 0x23 +#define cwain_R_PCM_MD0 0x14 +#define cwain_R_PCM_MD1 0x15 +#define cwain_R_PCM_MD2 0x15 +#define cwain_R_PWM_MD 0x46 +#define cwain_R_PWM0 0x38 +#define cwain_R_PWM1 0x39 +#define cwain_R_RAM_ADDR0 0x08 +#define cwain_R_RAM_ADDR1 0x09 +#define cwain_R_RAM_ADDR2 0x0A +#define cwain_R_RAM_MISC 0x0C +#define cwain_R_RX_OFFS 0x30 +#define cwain_R_RX_SL0_CFG0 0x25 +#define cwain_R_RX_SL0_CFG1 0x26 +#define cwain_R_RX0 0x24 +#define cwain_R_SH0H 0x15 +#define cwain_R_SH0L 0x15 +#define cwain_R_SH1H 0x15 +#define cwain_R_SH1L 0x15 +#define cwain_R_SL_SEL0 0x15 +#define cwain_R_SL_SEL1 0x15 +#define cwain_R_SL_SEL2 0x15 +#define cwain_R_SL_SEL3 0x15 +#define cwain_R_SL_SEL4 0x15 +#define cwain_R_SL_SEL5 0x15 +#define cwain_R_SL_SEL6 0x15 +#define cwain_R_SL_SEL7 0x15 +#define cwain_R_SLOT 0x10 +#define cwain_R_SYNC_CTRL 0x35 +#define cwain_R_SYNC_OUT 0x31 +#define cwain_R_TI_WD 0x1A +#define cwain_R_TX_OFFS 0x34 +#define cwain_R_TX_SL0_CFG0 0x2C +#define cwain_R_TX_SL0_CFG1 0x2E +#define cwain_R_TX_SL0 0x2D +#define cwain_R_TX0 0x28 +#define cwain_R_TX1 0x29 + +#define cwain_R_TX_FR0 0x2C +#define cwain_R_TX_FR1 0x2D +#define cwain_R_TX_FR2 0x2E + +#define cwain_R_RX_FR0 0x25 +#define cwain_R_RX_FR1 0x26 + +/* Read only registers */ +#define cwain_A_F1 0x0C +#define cwain_A_F12 0x0C +#define cwain_A_F2 0x0D +#define cwain_A_Z1 0x04 +#define cwain_A_Z12 0x04 +#define cwain_A_Z1H 0x05 +#define cwain_A_Z1L 0x04 +#define cwain_A_Z2 0x06 +#define cwain_A_Z2H 0x07 +#define cwain_A_Z2L 0x06 +#define cwain_R_BERT_ECH 0x1B +#define cwain_R_BERT_ECL 0x1A +#define cwain_R_BERT_STA 0x17 +#define cwain_R_CHIP_ID 0x16 +#define cwain_R_CHIP_RV 0x1F +#define cwain_R_CONF_OFLOW 0x14 +#define cwain_R_CRC_ECH 0x35 +#define cwain_R_CRC_ECL 0x34 +#define cwain_R_E_ECH 0x37 +#define cwain_R_E_ECL 0x36 +#define cwain_R_E1_RD_STA 0x20 +#define cwain_R_F0_CNTH 0x19 +#define cwain_R_F0_CNTL 0x18 +#define cwain_R_FAS_ECH 0x31 +#define cwain_R_FAS_ECL 0x30 +#define cwain_R_GPI_IN0 0x44 +#define cwain_R_GPI_IN1 0x45 +#define cwain_R_GPI_IN2 0x46 +#define cwain_R_GPI_IN3 0x47 +#define cwain_R_GPIO_IN0 0x40 +#define cwain_R_GPIO_IN1 0x41 +#define cwain_R_INT_DATA 0x88 +#define cwain_R_IRQ_FIFO_BL0 0xC8 +#define cwain_R_IRQ_FIFO_BL1 0xC9 +#define cwain_R_IRQ_FIFO_BL2 0xCA +#define cwain_R_IRQ_FIFO_BL3 0xCB +#define cwain_R_IRQ_FIFO_BL4 0xCC +#define cwain_R_IRQ_FIFO_BL5 0xCD +#define cwain_R_IRQ_FIFO_BL6 0xCE +#define cwain_R_IRQ_FIFO_BL7 0xCF +#define cwain_R_IRQ_MISC 0x11 +#define cwain_R_IRQ_OVIEW 0x10 +#define cwain_R_JATT_STA 0x2B +#define cwain_R_RAM_USE 0x15 +#define cwain_R_RX_SL0_0 0x25 +#define cwain_R_RX_SL0_1 0x26 +#define cwain_R_RX_SL0_2 0x27 +#define cwain_R_SA6_VAL13_ECH 0x39 +#define cwain_R_SA6_VAL13_ECL 0x38 +#define cwain_R_SA6_VAL23_ECH 0x3B +#define cwain_R_SA6_VAL23_ECL 0x3A +#define cwain_R_SLIP 0x2C +#define cwain_R_STATUS 0x1C +#define cwain_R_SYNC_STA 0x24 +#define cwain_R_VIO_ECH 0x33 +#define cwain_R_VIO_ECL 0x32 + + +/* Read/Write registers */ +#define cwain_A_FIFO_DATA0_NOINC 0x84 +#define cwain_A_FIFO_DATA0 0x80 +#define cwain_A_FIFO_DATA1_NOINC 0x84 +#define cwain_A_FIFO_DATA1 0x80 +#define cwain_A_FIFO_DATA2_NOINC 0x84 +#define cwain_A_FIFO_DATA2 0x80 +#define cwain_R_RAM_DATA 0xC0 + +#define PCI_DEVICE_ID_CCD_E 0x30b1 + +#define CLKDEL_TE 0xe /* CLKDEL in TE mode */ +#define CLKDEL_NT 0xc /* CLKDEL in NT mode */ + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zapata.conf.doubleE1 +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zapata.conf.doubleE1 @@ -0,0 +1,35 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +signalling = pri_cpe + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo + +group = 1 +channel => 1-15,17-31 + +group = 2 +channel => 32-46,48-62 + --- dahdi-linux-2.2.0~dfsg~rc5.orig/drivers/dahdi/cwain/zaptel.conf +++ dahdi-linux-2.2.0~dfsg~rc5/drivers/dahdi/cwain/zaptel.conf @@ -0,0 +1,11 @@ +loadzone=nl +defaultzone=nl +span=1,2,3,ccs,hdb3,crc4 + +alaw=1-31 + +bchan=1-15 +dchan=16 +bchan=17-31 + + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/zaptel.postrm +++ dahdi-linux-2.2.0~dfsg~rc5/debian/zaptel.postrm @@ -0,0 +1,11 @@ +#! /bin/sh -e + +if [ "$1" = 'purge' ]; then + # leftovers from the Sarge: + rm -f /etc/modutils/zaptel /etc/udev/rules.d/zaptel.perms + + # leftovers from Etch: + rm -f /etc/zaptel.conf +fi + +#DEBHELPER# --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/dahdi-linux.dahdi.udev +++ dahdi-linux-2.2.0~dfsg~rc5/debian/dahdi-linux.dahdi.udev @@ -0,0 +1,2 @@ +SUBSYSTEM=="dahdi", GROUP="dialout", MODE="0660" + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/modulestest +++ dahdi-linux-2.2.0~dfsg~rc5/debian/modulestest @@ -0,0 +1,74 @@ +#!/bin/sh + +# debian/modulestest: a postbuild script to build dahdi modules +# example usage: +# +# svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new + +# At least one of the following two must be set to a sensible value: +# If both are empty, the script does nothing useful) +# +# kernel versions: Comma-separated. Use those if you have their +# kernel-headers/linux-headers packages installed +# + +# PACKAGE is set by svn-buildpackage to "dahdi-linux" . Override that: +PACKAGE=dahdi + +# Full pathes to trees: +# Use this to provide a full path to a custom kernel tree: +#KERNEL_SOURCES=$HOME/Proj/Debs/Kernel/SwSusp/linux-2.6.15-rc5 +KERNEL_HEADERS= +KERNEL_SOURCES= +# run module-asustant with environment params that will generate +# .changes files even without signing +MODASS="env SIGNCHANGES=1 DEBSIGNCOMMAND=not_an_executable m-a" +me=`basename $0` + +# workaround for silly bash parsing issue in our build scripts: +if [ "$#" -lt 1 ]; then + set -- $MODULESTEST_ARGS +fi + +while getopts ah:rs:t arg +do + case "$arg" in + a) # All of the kernel-headers packages installed: + KERNEL_HEADERS=`COLUMNS=160 dpkg -l 'kernel-headers-2.[46].*-*-*' | awk '/^.i/{print $2}' | sed -e 's/^kernel-headers-//'| xargs| tr ' ' ,` + ;; + h) KERNEL_HEADERS=$OPTARG;; + s) KERNEL_SOURCES=$OPTARG;; + r) KERNEL_HEADERS=`uname -r`;; + t) # TODO: make this test per-distro or something + KERNEL_HEADERS=2.4.27-2-386,2.6.8-2-686-smp + ;; + esac +done +shift $(( $OPTIND-1 )) + +echo "Building for: Headers: $KERNEL_HEADERS, Sources: $KERNEL_SOURCES" + +if [ "$KERNEL_HEADERS" != '' ]; then hdrs_sw="-l $KERNEL_HEADERS"; fi +if [ "$KERNEL_SOURCES" != '' ]; then srcs_sw="-k $KERNEL_SOURCES"; fi + +# must be absolute for m-a ta accept TARBALL: +# Also note that $PWD is ugly and is about to be deleted. We need +# $PWD/.. +TOP_DIR=`dirname $PWD` +MODS_DIR=$TOP_DIR/modules +TAR_BALL=$MODS_DIR/usr/src/$PACKAGE.tar.bz2 +DEB=$TOP_DIR/$PACKAGE-source_${TAG_VERSION}_all.deb +LOG_FILE=$TOP_DIR/$PACKAGE-modules-build-$TAG_VERSION.log + +rm -f $LOG_FILE +dpkg -x $DEB $MODS_DIR + +if [ "$hdrs_sw" != '' ]; then + TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $hdrs_sw build $PACKAGE >>$LOG_FILE +fi +if [ "$srcs_sw" != '' ]; then + TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $srcs_sw build $PACKAGE >>$LOG_FILE +fi + +ls -l $TOP_DIR/$PACKAGE-modules-*_$TAG_VERSION+*.deb +echo "$me: Log file: $LOG_FILE" --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/dahdi-linux.install +++ dahdi-linux-2.2.0~dfsg~rc5/debian/dahdi-linux.install @@ -0,0 +1,4 @@ +drivers/dahdi/xpp/xpp.conf etc/dahdi +drivers/dahdi/xpp/xpp.rules etc/udev/rules.d +drivers/dahdi/xpp/init_card_* usr/share/dahdi/ +drivers/dahdi/xpp/XppConfig.pm usr/share/dahdi/ --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/postinst +++ dahdi-linux-2.2.0~dfsg~rc5/debian/postinst @@ -0,0 +1,3 @@ +#!/bin/sh + +#DEBHELPER# --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/compat +++ dahdi-linux-2.2.0~dfsg~rc5/debian/compat @@ -0,0 +1 @@ +7 --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/NEWS.Debian +++ dahdi-linux-2.2.0~dfsg~rc5/debian/NEWS.Debian @@ -0,0 +1,13 @@ +zaptel (1:1.4.10~dfsg-1) unstable; urgency=low + + Certain versions of Zaptel introduce incompatibilities between older kernel + modules and newer userspace programs. If programs such as ztcfg or asterisk + fail talking with Zaptel kernel with error 25 (ENOTTY: "Inappropriate ioctl + for this device") then you should upgrade the Zaptel modules by e.g: + 'm-a a-i zaptel' and reload drivers. + . + The version of the currently running Zaptel is in + /sys/module/zaptel/version . + + -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/copyright +++ dahdi-linux-2.2.0~dfsg~rc5/debian/copyright @@ -0,0 +1,100 @@ +This package was debianized by Matt Zimmerman on +Mon, 17 Jun 2002 10:31:21 -0400. + +It was downloaded from + http://www.asterisk.org/ +bristuff patch was downloaded from + http://www.junghanns.net/download/ +florz' patch for the zaphfc driver was downloaded from + http://zaphfc.florz.dyndns.org/ +OpenVox drivers were downloaded from + http://www.openvox.cn/download/drivers/dahdi/ + +Upstream source has been modified to comply with the Debian Free +Software Guildlines (DFSG), by the removal of the firmware files: + + wct4xxp/OCT6114-128D.ima + +xpp/LICENSE.firmware permits redistribution but does not mention +modification, which is a requirement of Debian Policy 2.1 ("Derived works"). + + +Upstream Authors: Jim Dixon / Zapate Telephony, Linux Support Services, Inc. + +Copyright (from zaptel.c): + + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001 Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + +On Debian systems, a copy of the GNU General Public License may be found in +/usr/share/common-licenses/GPL. + +Other Portions: + + * Copyright (C) 2001-2005, Digium, Inc. + * Copyright (C) 2002 Steve Underwood +sethdlc.c: * Copyright (C) 2000 Krzysztof Halasa +ztd-loc.c: * Copyright (C) 2004, Axialys Interactive +ztdummy.c: * Copyright (C) 2002, Hermes Softlab + +debian/patches/bristuff.dpatch, zaphfc, qozap, cwain, ztgsm: + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + +debian/patches/zaphfc-florz.dpatch: + * Copyright (C) 2004, 2005, 2006 Florian Zumbiehl + +vzaphfc is derived from bristuff and since enhanced by: + * Copyright (C) 2004-2006, Daniele "Vihai" Orlandi + * Copyright (C) 2006, headissue GmbH; Jens Wilke + +Files in the xpp/ subdirectory: + +* Written by Oron Peled +* Copyright (C) 2004-2006, Xorcom + +opvxa1200.c: +* OpenVox A1200P FXS/FXO Interface Driver for Zapata Telephony interface +* +* Modify from wctdm.c by MiaoLin + +opvxa1200.c is available for download from +http://www.openvox.com.cn/downloadsFile/a1200p-a800p_driver_with_dahdi.c + +wcopenpci.c is from the Voicetronix zaptel distribution at +http://www.voicetronix.com/Downloads/asterisk/ +Copyright (C) 2001, Linux Support Services, Inc. +Copyright (C) 2005 - 2007, Voicetronix + + +Patch oslec_kernelorg is based on the Linux Kernel distribution (see +README.Debian). Part of the Open Source Line Echo Canceller (OSLEC) project. + +echo.c: * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe +oslec.h: Copyright (C) 2007 David Rowe +oslec_wrap.c: Copyright (C) 2007 David Rowe +fir.h: * Copyright (C) 2002 Steve Underwood +bit_operations.h: * Copyright (C) 2006 Steve Underwood +echo.h: * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + +mmx.h: +* mmx.h + * Copyright (C) 1997-2001 H. Dietz and R. Fisher + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/watch +++ dahdi-linux-2.2.0~dfsg~rc5/debian/watch @@ -0,0 +1,4 @@ +version=3 +opts=dversionmangle=s/\~dfsg//;s/\~rc/-rc/; \ + http://downloads.asterisk.org/pub/telephony/dahdi-linux/releases/dahdi-linux-(.*)\.tar\.gz\ + debian svn-upgrade --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/zaptel.postinst +++ dahdi-linux-2.2.0~dfsg~rc5/debian/zaptel.postinst @@ -0,0 +1,68 @@ +#! /bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# + +mknod_safe() { + if [ -c $1 ]; then return; fi + mknod "$@" || true +} + +# stolen from udev +chrooted() { + if [ "$(stat -c %d/%i /)" = "$(stat -Lc %d/%i /proc/1/root 2>/dev/null)" ]; + then + # the devicenumber/inode pair of / is the same as that of /sbin/init's + # root, so we're *not* in a chroot and hence return false. + return 1 + fi + return 0 +} + +case "$1" in + configure) + + #MAKEDEV zaptel + + if [ ! chrooted -a ! -e /dev/.devfsd -a ! -e /dev/.udevdb -a ! -e /dev/.udev ]; then + mkdir -p /dev/zap + mknod_safe /dev/zap/ctl c 196 0 + mknod_safe /dev/zap/transcode c 196 250 + mknod_safe /dev/zap/timer c 196 253 + mknod_safe /dev/zap/channel c 196 254 + mknod_safe /dev/zap/pseudo c 196 255 + for N in `seq 249`; do + mknod_safe /dev/zap/$N c 196 $N + done + chown 0:dialout /dev/zap/ -R + chmod 0660 /dev/zap/* + fi + + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/control.modules.in +++ dahdi-linux-2.2.0~dfsg~rc5/debian/control.modules.in @@ -0,0 +1,21 @@ +Source: dahdi-linux +Section: comm +Priority: extra +Maintainer: Debian VoIP Team +Uploaders: Kilian Krause , Jose Carlos Garcia Sogo , Mark Purcell , Santiago Garcia Mantinan , Santiago Ruano Rincon +Build-Depends: debhelper (>> 3.0.0), bzip2 +Standards-Version: 3.8.0.0 + +Package: dahdi-modules-_KVERS_ +Architecture: any +Provides: dahdi-modules +Replaces: zaptel-modules-_KVERS_ +Conflicts: zaptel-modules-_KVERS_ +Description: DAHDI modules for Linux (kernel _KVERS_). + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment. + . + This package contains the set of loadable kernel modules for the + zapata telephony API. + This package contains the compiled kernel modules for _KVERS_ --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/control +++ dahdi-linux-2.2.0~dfsg~rc5/debian/control @@ -0,0 +1,39 @@ +Source: dahdi-linux +Section: comm +Priority: optional +Maintainer: Debian VoIP Team +Uploaders: Tzafrir Cohen , Mark Purcell +Build-Depends: debhelper (>= 7), quilt, bzip2, asciidoc +Standards-Version: 3.8.0 +Homepage: http://www.asterisk.org/ +Vcs-Svn: svn://svn.debian.org/pkg-voip/dahdi-linux/trunk/ +Vcs-Browser: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/?op=log + +Package: dahdi-linux +Section: comm +Architecture: all +# Xorcom packages depend on dahdi-firmware. Debian zaptel will probably +# just recommend it. +Depends: ${shlibs:Depends}, ${misc:Depends}, procps, fxload +Replaces: zaptel +Conflicts: zaptel +Description: DAHDI telephony interface - Linux userspace parts + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment. + . + This package includes the minimal Linux-specific userspace parts: udev + configuration, xpp initialization scripts. + +Package: dahdi-source +Section: devel +Architecture: all +Depends: ${misc:Depends}, debhelper (>> 4.0), module-assistant (>= 0.8.1), bzip2 +Recommends: dahdi-linux +Description: DAHDI telephony interface - source code for kernel driver + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment, as well as + basic headers for building DAHDI modules and utilities. + . + It is normally used to build kernel modules package: m-a a-i dahdi-linux --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/docs +++ dahdi-linux-2.2.0~dfsg~rc5/debian/docs @@ -0,0 +1,4 @@ +README +README.html +UPGRADE.txt +drivers/dahdi/xpp/Changelog_xpp --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/dahdi-linux.examples +++ dahdi-linux-2.2.0~dfsg~rc5/debian/dahdi-linux.examples @@ -0,0 +1 @@ +debian/make_static_nodes --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/README.Debian +++ dahdi-linux-2.2.0~dfsg~rc5/debian/README.Debian @@ -0,0 +1,111 @@ +Building kernel modules +----------------------- +First, install dahdi-source package if you have not yet done so. + +You can build and install the modules package (as root) with the +following command: +# module-assistant a-i dahdi + +It may be handy (for e.g., testing purposes) to build the module packages +for all of the kernel-header packages installed on your system. Something +in the lines of: + + m-a -u . -t -i -f -k "`echo usr/src/kernel-headers-*`" build dahdi + +You can also use the environment variable TARBALL to build the modules +with a custom zaptel.tar.bz2 tarball with some local modifications. + + +Using udev +---------- +FIXME: write about the necessary permission rules (/dev/dahdi/* files +are created by udev, but no longer need any special udev rules file) + +If you can't generate device files with udev, use the script +make_static_nodes in the examples directory of dahdi-linux. Please also +let us know that generating static file is actually needed. + + +Removed VPMADT032 Support +------------------------- +VPMADT032 is a hardware echo canceller module. It is an optional addon +in various Digium cards. Currently in those serviced by wcte12xp and +wctdm24xxp. As of DAHDI 2.2 using it requires a binary-only object file +to be linked with DAHDI at build time. This object file is downloaded at +build time from downloads.digium.com . This is naturally not something we +can support. And thus we disabled building it. + +Unlike the Digium firmwares, this is not something that can be reverted at +run-time. + + +Bristuff +-------- +This version includes the bri_dchan patch as adapted from the bristuff +distribution. It also includes kernel modules from the same distribution +however those were not yet fully adapted to work with DAHDI and are not +built by default at the moment. This means that xpd_bri.ko gets built. + +Right now the only Zaptel module of bristuff that has been adapted for +usage with DAHDI is zaphfc. + +qozap is not included. Please use wcb4xxp . Note that this driver uses +'hardhdlc' rather than 'dchan' in system.conf for the D-channel. + +All of those modules should work with either bristuffed Asterisk (if it +is Asterisk >= 1.4.22) or with Asterisk 1.6.x (libpri >= 1.4.4). + + +Echo Canceller +-------------- +Open Source Line Echo Canceller (OSLEC) is an alternative echo canceller +developed outside the main Zaptel tree. It is currently labelled "Beta". +For more information see http://www.rowetel.com/ucasterisk/oslec.html . +It generally works much better than the default upstream echo canceller +(MG2). On the downside, it has a higher CPU consumption. + +The version of OSLEC included is currently one from the staging directory. +This is done temporarily until that driver will find its way into the +mainline tree and to help test it. + +According to early tests by OSLEC users, the default Asterisk echo +canceller size of 128 taps (16ms) should be good enough for most +installations, and 256 taps (32 ms) should cover just about any case. +For phones connected to FXS ports you can use substatially lower values +in order to reduce CPU consumption. e.g: 8ms or even 4 ms (64 taps or 32 +taps). + +Setting the number of taps for a channel in Asterisk's chan_zap is done +using the following in Asterisk's zapata.conf: + + echocancell=NNN + +where NNN is the number of taps. See Asterisk sample zapata.conf . + + +Build-time Tests +---------------- +One sanity check to run when making changes in the package is to make +(besides the usual lintian/linda) is to make sure that the modules +packages still builds. + +For this to work you should first have a version of zaptel-source +installed (any version. modules-assitant needs a few files from it in +place). This allows you to use the script debian/modulestest as a +post-build hook: + + svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new -uc -us + +This will rebuild the zaptel-modules for your current kernel, or spend +much more time with: + + svn-buildpackage --svn-postbuild='debian/modulestest -a' --svn-ignore-new -uc -us + +to build dahdi-modules for all of your currently-installed +zaptel-modules packages. The log from the build will go to the file +zaptel-modules-build-.log in the build-area directory. e.g: +zaptel-modules-build-1.4.7~dfsg-1.log . + +The script should also run ls for the module packages built. You should +chack that all packages were indeed built and that their size looks sane +(no driver was left unbuilt). --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/dahdi-linux.doc-base.readme +++ dahdi-linux-2.2.0~dfsg~rc5/debian/dahdi-linux.doc-base.readme @@ -0,0 +1,13 @@ +Document: dahdi-linux +Title: DAHDI Telephony Interface Driver +Author: Tzafrir Cohen +Abstract: Basic documentation of the DAHDI telephony interface +Section: Network/Communication + +Format: HTML +Index: /usr/share/doc/dahdi-linux/README.html +Files: /usr/share/doc/dahdi-linux/README.html + +Format: text +Files: /usr/share/doc/dahdi-linux/README.gz + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/zaptel.lintian +++ dahdi-linux-2.2.0~dfsg~rc5/debian/zaptel.lintian @@ -0,0 +1 @@ +zaptel: mknod-in-maintainer-script postinst:16 --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/dahdi-source.dirs +++ dahdi-linux-2.2.0~dfsg~rc5/debian/dahdi-source.dirs @@ -0,0 +1 @@ +usr/src --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/zaptel.init +++ dahdi-linux-2.2.0~dfsg~rc5/debian/zaptel.init @@ -0,0 +1,150 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: zaptel +# Required-Start: +# Required-Stop: +# Should-Start: $local_fs hotplug module-init-tools +# Should-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Configures zaptel kernel modules. +# Description: Configures zaptel kernel modules. Waits until +# they are fully loaded or loads ztdummy (if modules +# are availble). +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/sbin/ztcfg +ZAPCONF_FILE=/etc/zaptel.conf +NAME=zaptel +DESC="Zaptel telephony kernel driver" +FXOTUNE=/sbin/fxotune + +# Include am defaults if available +if [ -f /etc/default/zaptel ] ; then + . /etc/default/zaptel +fi + +if [ ! -x $DAEMON ] ; then + echo >&2 $NAME ":" $DAEMON "fails test for exists and executable" ; + exit 0 +fi + +if [ ! -f $ZAPCONF_FILE ] ; then + echo >&2 $NAME ":" $ZAPCONF_FILE "fails test for exists and readable"; + exit 0 +fi + +# defined in /etc/default/zaptel +# ZAPTEL_MODS is a list of modules to be loaded at startup + +set -e + +# Initialize the Xorcom Astribank (xpp/) using perl utiliites: +# intended to replace all the the three functions below if user has +# installed the zaptel-perl utilities. +xpp_startup() { + # do nothing if there are no astribank devices: + if ! grep -q connected /proc/xpp/xbuses 2>/dev/null; then return 0; fi + + echo "Waiting for Astribank devices to initialize:" + cat /proc/xpp/XBUS-[0-9]*/waitfor_xpds 2>/dev/null || true + + # overriding locales for the above two, as perl can be noisy + # when locales are missing. + # No register all the devices if they didn't auto-register: + LC_ALL=C zt_registration on + + # this one could actually be run after ztcfg: + LC_ALL=C xpp_sync "$XPP_SYNC" +} + +# recursively unload a module and its dependencies, if possible. +# where's modprobe -r when you need it? +# inputs: module to unload. +unload_module() { + set +e + module="$1" + line=`lsmod 2>/dev/null | grep "^$module "` + if [ "$line" = '' ]; then return; fi # module was not loaded + + set -- $line + # $1: the original module, $2: size, $3: refcount, $4: deps list + mods=`echo $4 | tr , ' '` + # xpp_usb keeps the xpds below busy if an xpp hardware is + # connected. Hence must be removed before them: + case "$module" in xpd_*) mods="xpp_usb $mods";; esac + for mod in $mods; do + # run in a subshell, so it won't step over our vars: + (unload_module $mod) + done + rmmod $module || true + set -e +} + +# this checks that: +# A. /dev/zap/pseudo exists (if devfs/udev is in use, it implies zaptel loaded) +# B. Readable to the user running this script. No poblem if run by root. +# C. There is an active zaptel timing source. Either a zaptel hardware +# device or ztdummy. +test_zaptel_timing() { + head -c 0 /dev/zap/pseudo 2>/dev/null +} + +case "$1" in + start|reload) + echo -n "$DESC: " + #for module in $ZAPTEL_MODS + #do + # modprobe $module + #done + xpp_startup + + # If there is no zaptel timing source, load + # ztdummy. Other modules should have been loaded by + # now. + if ! test_zaptel_timing + then modprobe ztdummy || true # will fail if there is no module package + fi + + # configure existing modules: + + #If we got here and we have no zaptel modules, there's + # no point to fail if zaptel-modules-version> is not + # installed. + if [ -d /proc/zaptel ]; then + $DAEMON $DAEMON_OPTS + echo "$NAME." + fi + + if [ -r /etc/fxotune.conf ] && [ -x $FXOTUNE ]; then + # fxotune will fail if e.g. Asterisk is already running. + $FXOTUNE -s || : + fi + ;; + stop) + : # do nothing + ;; + unload) + unload_module zaptel + # if oslec is the echo canceller, loading zaptel will + # also load oslec. Thus we need to be able to unload it + # as well: + unload_module oslec + ;; + status) + check_zaptel_timing + ;; + force-reload|restart) + # there's no 'stop' + $0 start + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|force-reload|status|unload}" >&2 + exit 1 + ;; +esac + +exit 0 --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/changelog +++ dahdi-linux-2.2.0~dfsg~rc5/debian/changelog @@ -0,0 +1,1113 @@ +dahdi-linux (1:2.2.0~dfsg~rc5-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell Sun, 31 May 2009 10:18:49 +1000 + +dahdi-linux (1:2.2.0~dfsg~rc4-1) unstable; urgency=low + + * New upstream release + + [ Tzafrir Cohen ] + * Dropped qozap as wcb4xxp provides that functionality. + * New upstream RC. + * Actually build OpenVox drivers. + * opvxa1200.c: rev. 1.4.12.4 (battery fixes and such) + * Fix '${match}' in udev rules file (hardwire). + * no_firmware_download: Disable downloading a binary kernel module at + build time. + + [ Victor Seva ] + * fix debian/watch. + + -- Mark Purcell Wed, 20 May 2009 07:22:46 +1000 + +dahdi-linux (1:2.1.0.4~dfsg-1) experimental; urgency=low + + [ Tzafrir Cohen ] + * Zaptel renamed DAHDI and split to dahdi-linux and dahdi-tools. + * DAHDI is incompatible (ABI and API) with Zaptel. All modules need to + be adapted. + * OSLEC wrapper included upstream. Includeing a version of the OSLEC code + from the kernel staging directory. + - Patch oslec_kernelorg: Upstream kernel.org OSLEC code. + * Dropping some aparantly unused external Zaptel drivers. + * Extra drivers moved from kernel/ to drivers/dahdi/ to fit the kernel tree. + * UDEV rules are handled here as well. No need to change device names, + only set permissions. + * Patch fix_readme_astribank: Fix an asciidoc formatting issue. + * Patch oslec_kbuild: Make oslec's build unconditional. + * Fix for zaphfc with DAHDI (by Timo Teräs ) + * qozap no longer included: Use wcb4xxp instead. + * ztgsm no longer built: nobody really used it. + * cwain and vzaphfc currently included but not built. + * Don't generate static device files automatically. Provide a script + to do that. Until we know if there is an actual use for the non-udev + setup. + + [ Mark Purcell ] + * Update Description: fixes description-starts-with-package-name + * Add ${misc:Depends} - debhelper-but-no-misc-depends + * Add myself to uploaders + * debian/compat -> 7 + - Fixes:package-uses-deprecated-debhelper-compat-version + * Upload to experimental while we await NEW & debian-release + + -- Mark Purcell Sun, 29 Mar 2009 17:58:23 +1100 + +zaptel (1:1.4.11~dfsg-3) unstable; urgency=low + + * zaptel.init: exit on a different condition: Nicer handling of ztdummy. + * zaptel.init: Give a sane default to XPP_SYNC. + + -- Tzafrir Cohen Mon, 03 Nov 2008 09:35:23 +0200 + +zaptel (1:1.4.11~dfsg-2) unstable; urgency=low + + * Patch xpp_fxs_power: Fixed an issue with hook detection of the Astribank + FXS module. + * Don't fail init.d script if fxotune fails. This may happen if running it + when Asterisk is already running. + * Bump standards version to 3.8.0.0 . + * Ignore false lintian warning ("m-a a-i" has "a a"). + * Patch xpp_fxo_cid_always: do always pass PCM if that's what the user + asked. + * Patch vzaphfc_proc_root_dir: fix vzaphfc on 2.6.26. + * Patch wcte12xp_flags: Proper time for irq save flags. + * Patch headers_2627: Fix location of semaphore.h for 2.6.27 . + * Patch xpp_fxs_dtmf_leak: Don't play DTMFs to the wrong channel. + * Patch wctdm_fix_alarm: Fix sending channel alarms. + * Patch device_class_2626: Fix building 2.6.26 (Closes: #493397). + * Using dh_lintian for lintian overrides, hence requiring debhelper 6.0.7. + * Lintian: we know we have direct changes. Too bad we're half-upstream :-( + * Fix doc-base section names. + + -- Tzafrir Cohen Thu, 28 Aug 2008 22:58:23 +0300 + +zaptel (1:1.4.11~dfsg-1) unstable; urgency=medium + + [ Faidon Liambotis ] + * Update qozap from bristuff-0.4.0-RC1. + - fixed duoBRI miniPCI detection in qozap. + - added support for PCIe variants of duoBRI and quadBRI. + * Update cwain from bristuff-0.4.0-RC1. + * Update ztgsm from bristuff-0.4.0-RC1. + - reduced baudrate (serial interface to gsm modules) to be more robust + against irq misses in loaded systems + - improved serial debug output + - added module parameter "baudrate" (the default is 19200) + - added AT commands for SIM card selection + - added AT commands for shutting down/starting GSM modules + + [ Tzafrir Cohen ] + * New upstream release. + * Patch xpp_fix_t1 dropped: merged upstream. + * Finally removing 00list. + * Patch sigcap_dacs: tell zaphfc and vzaphfc to report support for + ZT_SIG_DACS. Makes ztscan report then as digital spans. + * Patch bri_dchan: change ZT_FLAG_BRIDCHAN as ZT_FLAG_MTP2 is also + defined to be 19 as of zaptel 1.4.11 . + * Patch chan_release_check: fixes a regression from 1.4.10.1 that causes + a panic if disconnecting an Astribank device with a channel still open. + * Patch florz-vmalloc: proper includes for zaphfc on non-x86 platforms. + (Closes: #488513) + * oslec_wrap.c: cycles code for powerpc by Stelios Koroneos. + * And dummy cycles code for other architectures. + + [ Mark Purcell ] + * Build-Depends: asciidoc (>= 8.2.6-1.1) + - asciidoc 8.2.6-1 is broken + - FTBFS: ERROR: unsafe: include file: + /etc/asciidoc/./javascripts/toc.js (Closes: #487011) + * Urgency medium as we fix RC bug + + -- Mark Purcell Mon, 30 Jun 2008 20:22:37 +1000 + +zaptel (1:1.4.10.1~dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream bugfix release. + * Note the buid "error" from http://bugs.digium.com/12426 . + * Fix the generation of tonezone.txt. + * Patch xpp_fix_t1: allow the xpp pri module to initialize as T1. + * Set OSLEC as the default echo canceller. + * The "unload" init.d script operation will now also attempt to unload + oslec. + + -- Tzafrir Cohen Thu, 8 May 2008 02:03:34 +0300 + +zaptel (1:1.4.10~dfsg-1) unstable; urgency=low + + [ Mark Purcell ] + * New upstream release + + [ Tzafrir Cohen ] + * Added NEWS.Debian about zaptel modules incompatibility. + * Do run 'make dist-clean' on modules build. It prints an ugly error, but + only after the actualy relevant clean. + * Watching downloads.digium.com directly again. + + -- Tzafrir Cohen Mon, 14 Apr 2008 14:07:54 +0300 + +zaptel (1:1.4.9.2~dfsg-1) unstable; urgency=low + + * New upstream release (Closes: #464900). + - All kernel modules moved to under kernel/ + * Converting patches to quilt (because bristuff uses them). + * Include the separate xpp changelog file. + * Fix a possible crash with oslec when a fax tone is detected: + http://sourceforge.net/mailarchive/message.php?msg_name=20080217212421.GT15415%40xorcom.com + (Closes: #447245). + * Adjusted lintian overrides: mknod is now called from a function. + * Adjust vzaphfc to netdevice API changes in kernel 2.6.24. + * Once again ignoring 'make distclean' errors: it will fail if we don't + have kernel sources / headers for current kernel :-( . + * Remove some unnecessary changes from the florz zaphfc patch - fixes zaphfc + warning. + + -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 + +zaptel (1:1.4.8~dfsg-1) unstable; urgency=low + + * New upstream release + * bristuff.dpatch broken to smaller patches, as in bristuff 0.4.0-test6. + - ztpty.c is added as a source file. + - bri_dchan.dpatch, proc_read.dpatch, ztcfg-start_stop.dpatch and + zt_alarm_notify_no_master_change.dpatch . + * beronet.dpatch: Support for Bero.net cards (Closes: #453496). + * Adapted ztcfg-start_stop.dpatch to zaptel 1.4.8 . + * Removing xpp_m_subdirs.dpatch: merged upstream. + * tones.h is no longer generated. + * kbuild_src.dpatch: A small build issue already fixed upstream. + * oslec_zaptel.dpatch: Add a compatibility function for the old + echo_can_create() of zaptel < 1.4.8 . + * Also copy the new wcte12xp/ to the source tarball. + * Delete old created files on purge (Closes: #454388). + + -- Tzafrir Cohen Tue, 29 Jan 2008 13:32:11 +0200 + +zaptel (1:1.4.7.1~dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream release. + - New upstream version 1.4.6 (Closes: #452315, #442414). + * Add Voicetronix OpenPCI Zaptel driver (wcopenpci.c) . + * Explicitly delete depmod results (modules.dep at el.) + * ztdiag.dpatch: removed (applied by upstream). + * xpp_m_subdirs.dpatch: removed (applied by upstream). + * Makefile_fix_clean.dpatch: removed (applied by upstream). + * Makefile_opt_level.dpatch: removed (applied by upstream, except for 2.4 + kernel modules). + * Call 'make dist-clean' rather than 'make clean'. + * Don't run 'make dist-clean' on zaptel-modules build. + * Document zaptel-modules build test. + * Don't run ztcfg if zaptel was not loaded. But do fail if ztcfg has failed. + (Closes: #407996). + * Don't build wcopenpci on big endian platforms (Module gives #error there). + * Actually fix building xpp with M instead of SUBDIRS. + * Updates to bristuff zap modules from bristuff-0.3.0-1y-l: + - Fix build warnings. + - Allow sharing interrupts. + * Update cwain from recent bristuff. + + [ Faidon Liambotis ] + * Don't delete old device nodes on installations since it's needed only for + upgrades from <= sarge which isn't supported. Shuts up lintian error. + * Correctly detect udev/devfsd and chrooted environments at postinst time. + * Fix debian/watch by using a pkg-voip wrapper to avoid upstream's silly + redirections. (Closes: #449673) + * Fix OSLEC so that audio works again on x86_64 systems. + * Update Standards-Version to 3.7.3, no changes needed. + * Refresh Uploaders: add myself, remove Jose Carlos Garcia Sogo, Santiago + Garcia Mantinan and Santiago Ruano Rincon. + + -- Faidon Liambotis Fri, 28 Dec 2007 17:59:57 +0200 + +zaptel (1:1.4.5.1~dfsg-2) unstable; urgency=low + + [ Kilian Krause ] + * Update oslec to r942. Add mmx.h (disabled for now). + * Do export oslec_echo_can_identify from oslec.ko (Closes: #439814). + * Add Homepage field as added in dpkg-dev 1.14.6. + * Fix debian-rules-uses-pwd + + [ Mark Purcell ] + * Remove Build-Depends restrictions for packages in stable + + [ Tzafrir Cohen ] + * vzaphfc: update to 1.44, and apply our fixes. + * vzaphfc: fix building with Sarge 2.6 kernels. + * Fix asciidoc buildign in Sarge (temporary fix, until we get upstream fix + in 1.4.6). + * Don't build oslec if it is not the Zaptel echo canceller. + * Adjust line numbers in Lintian ignore file. + * Include oslec-ctrl-panel.sh as an example (Closes: #443363). + + -- Kilian Krause Sat, 22 Sep 2007 12:08:52 +0200 + +zaptel (1:1.4.5.1~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Kilian Krause ] + * Add dpkg-dev (>= 1.13.19) to Build-Depends for binary:Version + * Correct the zaptel.init to point to correct path of fxotune + (Closes: #439310) + * Put opt level back to -O2 in accordance with Debian Policy. + + [ Tzafrir Cohen ] + * Pass extra modules / subdirs to modules make. + * Re-add zaptel_perl.dpatch. Clarified patch's description in hope to aviod + re-re-re-removal of this patch. + * There's now an HTML version of README.Astribank . And upstream Makefile is + better at cleaning. + * Don't install man pages. 'make install' does that already (Closes: #422943) + * Fixed building vzaphfc, opvx1200p and ds1x1f of kernel >=2.6.22 + (>= 2.6.19?). + * xpp_m_subdirs.dpatch: Fixed building of xpp modules with M= . + * modulestest -r: test current kernel. + + -- Kilian Krause Sun, 26 Aug 2007 12:08:10 +0200 + +zaptel (1:1.4.5~dfsg-1) unstable; urgency=low + + * New Upstream Maintenance Release + - support for Digium's new 32 channel hardware echo canceler + (VPMADT032) for the TDM800P and TDM2400P + + [ Tzafrir Cohen ] + * zaptel_perl.dpatch: install perl modules to vendorlib rather than to + sitelib. This fixes the zaptel-perl utilities (e.g: zaptel_hardware). + * Build-depend on asciidoc to produce some package documentation. + * Mark doc-base documentation. + * Simplify Astribank initialization with zaptel-perl. + * provide symlinks for headers files: compatibility with zaptel 1.2 + locations of zaptel.h and tonezone.h . + + [ Faidon Liambotis ] + * bristuff 0.4.0-test4 + - Add zaptel.patch as debian/patches/bristuff.dpatch. + - Update zaphfc, qozap, cwain and ztgsm. + * Add florz' patch to zaphfc; removes RTAI support. (Closes: #382227) + * Don't fail on mknod errors; helps on vserver setups. (Closes: #411850) + + [ Mark Purcell ] + * Remove echocan_env.dpatch - merged upstream + * Remove zaptel_perl.dpatch - merged upstream + * Add debian/patches/inlcude.dpatch - upstream typo + * debian/rules: upstream change INSTALL_PREFIX->DESTDIR + * debian/zaptel.doc-base.readme add Index: doc-base-file-no-index + * Ship new wctdm24xxp modules + + -- Mark Purcell Sat, 18 Aug 2007 13:20:31 +0100 + +zaptel (1:1.4.4~dfsg-1) unstable; urgency=low + + * New Upstream Release + + [ Tzafrir Cohen ] + * echocan_env.dpatch: set the echo canceller from the environment. + * oslec/ The bulk of the oslec files. + * oslec_zaptel.dpatch: Minimal changes to zaptel to add oslec, beyond + the oslec directory. + * oslec_zaptap.dpatch: The oslec zaptap echo sampling device (probably + still does not apply). + * man_fixes.dpatch: Documentation fixes from upstream. + * Removing unrequired/harmful and useless modprobe / modutils config. + * README.Debian updates. + + [ Mark Purcell ] + * debian/patches/zaptel_perl.dpatch now included upstream + * Switch to ${binary:Version} substvar-source-version-is-deprecated + + -- Mark Purcell Wed, 18 Jul 2007 21:41:51 +0100 + +zaptel (1:1.4.3~dfsg-2) unstable; urgency=low + + [ Tzafrir Cohen ] + * Fixed and re-added zaptel_perl. + * And added zaptel_hardware (zaptel hardware lister), just for fun. + + -- Tzafrir Cohen Sat, 09 Jun 2007 03:36:17 +0300 + +zaptel (1:1.4.3~dfsg-1) unstable; urgency=low + + * New upstream release + - A fix for the potential for a rare deadlock between zaptel and the + wct4xxp, wcte11xp, and wct1xxp drivers + - Fixes for the VPM450M module on FC6 to correct a potential stack + overflow scenario at load time. + - Many updates to the Astribank driver + * disable debian/patches/zaptel_perl as it doesnt apply cleanly + + -- Mark Purcell Sat, 09 Jun 2007 00:01:55 +0100 + +zaptel (1:1.4.2.1~dfsg-2) unstable; urgency=low + + * Include debian/compat in zaptel-sources + - missing debian/compat file in archive (Closes: #422153) + + -- Mark Purcell Sat, 02 Jun 2007 10:22:04 +0100 + +zaptel (1:1.4.2.1~dfsg-1) unstable; urgency=low + + * New upstream release + - Added the ability to monitor pre-echo cancellation audio with ztmonitor + - Fixed some places where there was the potential for memory corruption + on SMP systems + - FTBFS with 2.6.19-1 (Closes: #405562) + * zaptel 1.4 provides wcfxs->wctdm alias + - No mention of wcfxs -> wctdm module rename (Closes: #419161) + - Missing modutils/modprobe rules for wctdm (Closes: #419162) + * provide debian/compat fixes lintian: debian-rules-sets-DH_COMPAT + * Cleanup debian/patches/ + + -- Mark Purcell Thu, 26 Apr 2007 09:07:48 +1000 + +zaptel (1:1.4.1~dfsg-3) unstable; urgency=low + + [ TzafrirCohen ] + * Better shape for ztdiag.dpatch. Don't forget to free. + + -- Mark Purcell Wed, 04 Apr 2007 22:51:24 +0100 + +zaptel (1:1.4.1~dfsg-2) experimental; urgency=low + + * $(MAKE) install-libs needed to install libs to libtonezone + + -- Mark Purcell Sat, 24 Mar 2007 14:17:22 +0000 + +zaptel (1:1.4.1~dfsg-1) experimental; urgency=low + + [ Tzafrir Cohen ] + * New upstream release. + * Fix installation of tonezone.h . + * merges from trunk: + * Update standards version to 3.7.2 . + * ztdiag.dpatch: fix the ioctl ZT_CHANDIAG and eable it. + * Should fix modules building: restore generated files that were + removed by a clean (Closes: #415280). + * Update debian/watch for ~ versions + * zaptel_perl.dpatch: Do install perl modules and scripts. + * zaptel_perl.dpatch: a few other fixes (until next upstream release). + * Add Makefile.kernel26 and wctc4xxp/ to source tarball. + * Don't install /etc/zaptel.conf by default (Closes: #383081) . + * List the tonezones supported by ztcfg and libtonezone in tonezones.txt + (Closes: #379108). + * Remove man pages from debian/, as they are included in upstream. + + -- Tzafrir Cohen Sat, 24 Mar 2007 10:03:54 +0200 + +zaptel (1:1.4.0~dfsg-1) experimental; urgency=low + + * Upgrading to 1.4. + * Watch for 1.4.x tarballs, rather than 1.2.x tarballs. + * Disable most patches: + - ukcid fails to apply. + - bristuff fails to apply. + - No point in patching the Makefile. + * Saving my attempts to apply bristuff. + * .h files moved to /usr/include/zaptel + + -- Tzafrir Cohen Mon, 1 Jan 2007 21:31:18 +0200 + +zaptel (1:1.2.12~dfsg-2) UNRELEASED; urgency=low + + * NOT RELEASED YET + * Remove -O4 from all Makefiles (Closes: #391840) + * Remove gcc hardcoded as HOSTCC + + -- Kilian Krause Thu, 28 Dec 2006 13:12:16 +0100 + +zaptel (1:1.2.12~dfsg-1) unstable; urgency=low + + * New upstream release. (Closes: #403326) + + -- Kilian Krause Wed, 27 Dec 2006 23:23:40 +0100 + +zaptel (1:1.2.11.dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * Reverting my changes from 1:1.2.9.1.dfsg-2. Moved to the experimental + branch. + + [ Kilian Krause ] + * Remove bogus zaptel-modules from being Recommends (Closes: #387961) + * Update vzaphfc as proposed by Jens Wilke + + [ Mark Purcell ] + * New Upstream Release + - Fixes: Fails to build with pristine upstream kernel, very recent version + (Closes: #400705) + - Fixes: Please package version 1.2.11 (Closes: #399634) + - Fixes: vzaphfc: error: 'CHECKSUM_HW' undeclared (Closes: #386498) + * Cleanup debian/patches/wct4xxp-dfsg.dpatch + * debian/rules call dh_installmodules from binary_modules: + - Fixes: I had to do depmod -a manually after doing m-a a-i zaptel (Closes: + #332787) + * Update debian/patches/Makefile_uname.dpatch to force -O2 + - Fixes: Cannot initiate a call to BRI (Closes: #386052) + * Remove Depends: zaptel from debian/control.modules.in + - please don't depend on zaptel (Closes: #391826) + + -- Mark Purcell Sat, 2 Dec 2006 14:33:30 +0000 + +zaptel (1:1.2.10.dfsg-2) unstable; urgency=low + + * bristuff-0.3.0-PRE-1v + * Remove redundant GPL LICENCE text + + -- Mark Purcell Tue, 24 Oct 2006 22:41:01 +0100 + +zaptel (1:1.2.10.dfsg-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell Sun, 22 Oct 2006 20:27:19 +0100 + +zaptel (1:1.2.9.1.dfsg-2) unstable; urgency=low + + [ Tzafrir Cohen ] + * zaptel 1.4 compatibility changes: + - place zaptel.h and tonezone.h in /usr/include/zaptel (through symlinks) + - zaptelh_14.dpatch: declare some zaptel 1.4 interfaces (not implemented + anywhere, though). + + [ Mark Purcell ] + * debian/rules patch from Robert Millan + - the package doesn't compile (Closes: #390903) + * add debian/patches/dbug391840.dpatch + - ztcfg segfaults because of -O4 (Closes: #391840) + * add debian/patches/wct4xxp-dfsg.dpatch + - wct4xxp and other modules are not built anymore on zaptel- + 1.2.8.dfsg-1 (Closes: #388756) + + -- Mark Purcell Tue, 10 Oct 2006 09:36:58 +1000 + +zaptel (1:1.2.9.1.dfsg-1) unstable; urgency=low + + * New Upstream Release + * firmware removed from wct4xxp/OCT6114-128D.ima + * Lintian cleanup; spelling-error-in-copyright + + -- Mark Purcell Sat, 23 Sep 2006 13:58:15 +0100 + +zaptel (1:1.2.8.dfsg-1) unstable; urgency=low + + * New Upstream Release + + -- Mark Purcell Wed, 23 Aug 2006 07:30:22 +0100 + +zaptel (1:1.2.7.dfsg-4) unstable; urgency=low + + * Install zaptel.conf.sample as a confile under /etc + * Add Recommends: zaptel-modules + * Improve error handling and conf file checking in init.d. (Closes: + Bug#382604) + + -- Mark Purcell Thu, 17 Aug 2006 08:34:43 +0100 + +zaptel (1:1.2.7.dfsg-3) unstable; urgency=low + + [ Kilian Krause ] + * Simplified vzaphfc patch. + + [ Mark Purcell ] + * Build-Depends: debhelper (>= 5.0.37) and dh_installmodules makes + zaptel-source.postinst & zaptel-modules.post{inst,rm} obsolete + Fixes: postinst/postrm depmod -- update templates to use dh_installmodules + instead (Closes: #381754) + * postinst failure (Closes: #361312) + * zaptel-modules from testing don't compile on Sarge (Closes: #376719) + * pciradio.c:1810: error: syntax error before string constant (Closes: + #368145) + * Can't recompile zaptel modules on Sarge (Closes: #375581) + * zaptel-modules from testing don't compile on Sarge (Closes: #376719) + + -- Mark Purcell Thu, 10 Aug 2006 23:39:58 +0100 + +zaptel (1:1.2.7.dfsg-2) unstable; urgency=low + + * Fix get-orig-source target to make dfsg repacking work + * Fix zaptel-source to build without firmware again. Required dropping + wct4xxp module. Added vzaphfc to linux-2.6 modules. (Closes: #381123) + + -- Kilian Krause Thu, 3 Aug 2006 11:48:14 +0000 + +zaptel (1:1.2.7.dfsg-1) unstable; urgency=high + + * Urgency high as this is blocking a security fix for asterisk + * Remove non-modifiable firmware to make DFSG compliant. Does + anyone need this firmware? (Closes: #379458) + + -- Mark Purcell Tue, 1 Aug 2006 15:27:09 +0100 + +zaptel (1:1.2.7-2) unstable; urgency=low + + * Copying Makefile as before to the source package, + Copying some extra files now needed for building (Closes: #378864) + + -- Mark Purcell Tue, 1 Aug 2006 06:29:39 +0100 + +zaptel (1:1.2.7-1) unstable; urgency=low + + * New upstream release + + [ Kilian Krause ] + * Add vzaphfc driver (enhanced zaphfc) by Jens Wilke. + + [ Tzafrir Cohen ] + * Separating ZapBRI modules to directories, rather than patches + * Example configs moved from zaptel-source to zaptel + * Removing some unneeded dirs from zaptel-source + * debian/patches/Makefile_kbuild: a small part of the original one. + Fixes building on Sarge + * genzaptelconf is now in zaptel + * xpp/utils/Makefile has a decent install target + * debian/rules: Use CURDIR + * debian/modulestest: Building modules for -3 kernels + * fix x bit of files in /usr/share/zaptel + * removed genzaptelconf from debian/ + * Added support for the OpenVox A1200P card (http://www.openvox.com.cn/) + * debian/control: require libusb-dev for building xpp firmware loader. + * debian/control: Recommend package xpp-firmware (should be added to + non-free) + * bristuff_local_zaptelh.dpatch: Build bristuff modules with correct + zaptel.conf (in Sarge) + * Makefile_uname.dpatch: Updated. Note: watch for PWD issues. + * Makefile_bristuff.dpatch: updated to reflect Makefile change. + + -- Mark Purcell Mon, 17 Jul 2006 21:48:21 +0100 + +zaptel (1:1.2.6-2) unstable; urgency=high + + * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] + + * Add debian/libtonezone-dev.links + - Realy fix: missing libtonezone.so.1 symlink (Closes: #372887) + + -- Mark Purcell Wed, 14 Jun 2006 13:40:31 +1000 + +zaptel (1:1.2.6-1) unstable; urgency=high + + [ Mark Purcell ] + * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] + + * New upstream release. + - can't find zaptel.h during build (Closes: #330137) + - errors in fxotune.c (Closes: #370213) + - Cannot make zaptel-source: SUBDIR not found (Closes: #368561) + + [ Kilian Krause ] + * Weed out old unused patches. Add comments which patches have been included + upstream for next release. + + [ Lionel Elie Mamane ] + * Load ztdummy when needed, not when not needed. + + [ Tzafrir Cohen ] + * bristuff: 0.3.0-PRE1p + * We have another ZapBRI module: ztgsm + * Experimental support in genzaptelconf for ztgsm (from sample files) + * genzaptelconf: 0.5.1 (does not require restart of asterisk) + * zaptel.init: 'unload' operation. Better support for Astribank + * moduletest script fixes + * bristuff added ztpty + * genzaptelconf: wait for xpp (astribank) module to register after loadin it + * minor xpp driver fixes (already in 1.2 branch) + + [ Julien BLACHE ] + * debian/libtonezone1.links: + + Create the libtonezone.so.1 symlink (closes: #372887). + + -- Mark Purcell Wed, 14 Jun 2006 10:59:52 +1000 + +zaptel (1:1.2.5-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream version + * Only build xpp for i386, as it currently crashes on other arches. + * Fix compilation of xpp for 2.6.14 + + [ Kilian Krause ] + * Fix gendigits to write to stdout. + + -- Kilian Krause Thu, 30 Mar 2006 23:52:38 +0300 + +zaptel (1:1.2.4-1) unstable; urgency=low + + * New upstrream release (Closes: #353094) + * removing xpp.dpatch: merged in upstream + * removing dot_version: bug fixed upstream + * Makefile_kbuild.dpatch: modified, as it was not properly merged in + upstream + * Makefile_bristuff.dpatch: really build zaptel modules again + * Makefile_xpp: fixed. + * debian/modulestest: a script for postbuild of zaptel modules from a svn + build + * zaptel-source: removing unnecessary dependency on dpatch + + -- Tzafrir Cohen Thu, 23 Feb 2006 09:40:47 +0200 + +zaptel (1:1.2.3-2) unstable; urgency=low + + * bristuff 0.3.0-PRE1k. Also, renamed the dpatch to simply "bristuff" + * updated version in dot_version.dpatch. + * Include build_tools and .version in copied files + * newer versions of genzaptelconf and xpp.dpatch + + -- Tzafrir Cohen Mon, 6 Feb 2006 15:30:06 +0200 + +zaptel (1:1.2.3-1) unstable; urgency=low + + * new upstrream release + * ukcid.dpatch: for UK Caller ID support (Zaptel part, closes: #302380) + * newer versions of genzaptelconf and xpp.dpatch + * Makefile_pscmd.dpatch disabled due to a small upstream change. Revive it? + * dot_version.dpatch: the tarball is missing the .version file. Remove + it when it provides one + + -- Tzafrir Cohen Mon, 6 Feb 2006 14:02:04 +0200 + +zaptel (1:1.2.1-3) unstable; urgency=low + + * Fix compilation with binary-only mode. + * bristuff 0.3.0-PRE-1f + * make lintian override apply + + -- Tzafrir Cohen Sat, 7 Jan 2006 20:39:33 +0200 + +zaptel (1:1.2.1-2) unstable; urgency=low + + * Added bristuff 0.3.0-PRE1d patch. bristuff re-enabled. + (Closes: #340627, #344432) + * Documentation fixes (Closes: #316801) + * Makefile_targets.dpatch is ba its small self + * readded bristuff examples. with cwain this time + * zaptel.init: a slightly different test for a zaptel timing source + * Depend on procps due to using ps in postinst (Closes: #342699) + + -- Tzafrir Cohen Fri, 30 Dec 2005 19:12:54 +0200 + +zaptel (1:1.2.1-1) unstable; urgency=low + + * New upstream release + * Disable bristuff for new upstream + + -- Mark Purcell Wed, 7 Dec 2005 21:28:23 +0000 + +zaptel (1:1.2.0-2) unstable; urgency=low + + [ Kilian Krause ] + * Added bristuff 0.3.0-PRE1 for Asterisk 1.2.0 support. + + [Tzafrir Cohen] + * fix Makefile_deps_kern.dpatch + * remove .rej from Makefile.uname.dpatch + * do install genzaptelconf man page + * update genzaptelconf and its man page + * echocan_env.dpatch: allow changing the echo canceller at zaptel-modules + build time + * Makefile_kbuild.dpatch: use kbuild for 2.6 modules build. used for: + * Makefile_xpp.dpatch: (not applied by default) + a small patch to enable the build of: + * xpp.dpatch: drivers for Xorcom Asteribank + + [ Mark Purcell ] + * Build and package libtonezone.so + + -- Mark Purcell Wed, 30 Nov 2005 16:28:51 +0000 + +zaptel (1:1.2.0-1) unstable; urgency=low + + * New upstream release + * Remove Makefile_deps_kern.dpatch as it doesnt apply upstream + + -- Mark Purcell Thu, 17 Nov 2005 17:50:00 +0000 + +zaptel (1:1.2.0-rc2-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell Sun, 13 Nov 2005 18:24:17 +0000 + +zaptel (1:1.2.0-rc1-1) experimental; urgency=low + + * New upstream release + * Update Makefile_uname.dpatch + * FTBFS: missing or incorrect directory modexamples/ (Closes: #329084) + * debian/rules export DH_COMPAT=4 + + -- Mark Purcell Wed, 9 Nov 2005 21:37:47 +0000 + +zaptel (1:1.2.0-beta2-3) experimental; urgency=low + + * Not Released Yet + * Copyright audit to debian/copyright + + -- Mark Purcell Mon, 7 Nov 2005 19:19:27 +0000 + +zaptel (1:1.0.9.2-1) unstable; urgency=low + + * New Upstream Release + + -- Mark Purcell Tue, 8 Nov 2005 20:47:48 +0000 + +zaptel (1:1.2.0-beta2-2) experimental; urgency=low + + * Suggestions from Tzafrir Cohen + - Makefile_man.dpatch can be removed: fixed by upstream + - fxotune.tmpfile.dpatch can be removed: fixed by upstream + - a small manual fix to Makefile_targets.dpatch: s/ manpages$// + - debian/rules: dh_installman: 's/debian/doc/' a number of times + (don't use doc/*.8 as there is no reason to install the pathetic + torisatool.8 man page) + + -- Mark Purcell Tue, 1 Nov 2005 21:26:36 +0000 + +zaptel (1:1.2.0-beta2-1) experimental; urgency=low + + * New upstream release + * Update Makefile_targets.dpatch + * Update Makefile_man + * Update fxotune_tmpfile.dpatch + + -- Mark Purcell Tue, 1 Nov 2005 20:51:02 +0000 + +zaptel (1:1.2.0-0beta1-1) experimental; urgency=low + + * New upstream release + * Disable bristuff for experimental upload + * Apply patch from Tzafrir Cohen for 1.2.0 build + + -- Mark Purcell Sun, 18 Sep 2005 12:48:59 +0100 + +zaptel (1:1.0.9.1-4) unstable; urgency=low + + (NOT YET RELEASED - needs still some tweaking with florz' patches) + * debian/patches/ztcfg_init.dpatch: Make ztcfg not kill the channels when + executed twice. + * debian/patches/zaphfc_0.2.0-RC8n_florz-8.dpatch: Add florz' improved + zaphfc implementation as zaphfc-florz. This should reduce system IO load + among other improvements. Taken from http://zaphfc.florz.dyndns.org/ + + -- Kilian Krause Sat, 27 Aug 2005 21:32:50 +0200 + +zaptel (1:1.0.9.1-3) unstable; urgency=low + + * debian/control: fixed overrides disparity with zaptel-source belonging to + devel rather than comm. + + -- Kilian Krause Sat, 27 Aug 2005 14:35:48 +0200 + +zaptel (1:1.0.9.1-2) unstable; urgency=low + + * Closes: #302836: zaptel-source: zaphfc module missing after + compiling modules. + * Closes: #323753: zaptel-source: cannot compile zaphfc in unstable + with gcc-4.0.1. + + -- Santiago Ruano Rincon Fri, 19 Aug 2005 00:40:56 -0500 + +zaptel (1:1.0.9.1-1) unstable; urgency=low + + * New upstream release + * Update debian/watch + * Please package version 1.0.9.1 (Closes: #320600) + * FXO hardware stops working after 25 days (Closes: #321239) + + -- Mark Purcell Mon, 8 Aug 2005 18:34:10 +0100 + +zaptel (1:1.0.9-5) unstable; urgency=low + + * Import bristuff-0.2.0-RC8l.dpatch + + -- Santiago Ruano Rincon Sat, 30 Jul 2005 11:26:42 -0500 + +zaptel (1:1.0.9-4) unstable; urgency=low + + Santiago Ruano Rincon: + * Man pages are builded from sgml's using docbook-utils + Deleted the *.8 files + * Closes: #317297: Applied a patch to improve the ztdummy + accuracy on kernel 2.6 + + Mark Purcell: + * Reinstate debian/zaptel.install + - Closes: #318575: this package does not install ztcfg, ztmonitor, + ztspeed, zttest, zttool. + + -- Mark Purcell Sun, 17 Jul 2005 07:11:27 +1000 + +zaptel (1:1.0.9-3) unstable; urgency=low + + * Import bristuff-0.2.0-RC8j.dpatch + * Closes: #315251: zaptel should be in group comm + * Note that the cloned report is still active against ftp.debian.org + * Closes: #316800: zaptel package 1.0.9 ships headers + + -- Mark Purcell Thu, 14 Jul 2005 12:19:10 +0100 + +zaptel (1:1.0.9-2) unstable; urgency=low + + * Import bristuff-0.2.0-RC8h.dpatch + * Enable rtia.dpatch + + -- Mark Purcell Mon, 4 Jul 2005 02:35:37 +0100 + +zaptel (1:1.0.9-1) unstable; urgency=low + + * New upstream release + * Disable bristuff to allow 1.0.9 upload + * Disable rtia to allow 1.0.9 upload + + -- Mark Purcell Sun, 3 Jul 2005 15:51:32 +0100 + +zaptel (1:1.0.7-5) unstable; urgency=low + + * ACK NMUs. Thanks for helping with this. (Closes: #305731, #310150) + * Actually doesn't fail if dpatch is not installed when building modules. + * zaptel-modules.postinst: New. Run depmod -a on modules install + * zaptel: should build-dep on debhelper (>= 4.0.4). (Closes: #310788) + * zaptel: should build-dep on dpatch >= 2.0.9 (Closes: #314549) + * zaptel: bashism in postinst (Closes: #314552) + * zaptel-source: compilation error in zaphfc.c (Closes: #305193) + * zaptel-source Build-Depends on dpatch, should Depend on + it though. (Closes: #309258) + * zaptel-source: Fails to enable RTAI support (Closes: #304648) + + -- Kilian Krause Sun, 19 Jun 2005 15:38:25 +0200 + +zaptel (1:1.0.7-4.1) unstable; urgency=high + + * Non-maintainer upload. + * High-urgency upload for sarge-targetted RC bugfix + * Make sure directories are created mode 755 instead of mode 644, as + this otherwise causes problems for building (apparently on xfs + filesystems). Closes: #310150. + * Tweak debian/patches/Makefile.dpatch fix from the previous NMU so + that it isn't unnecessarily fragile: -fsigned-char is *always* + either a no-op or required, so lose the architecture checking and + enable it unconditionally. Closes: #305731. + + -- Steve Langasek Sun, 22 May 2005 02:48:44 -0700 + +zaptel (1:1.0.7-4) unstable; urgency=high + + * NMU as VOIP team taking so long. Fix compiler flags so that ztcfg + works. (Closes: #305731) + + -- Matthew Grant Fri, 22 Apr 2005 07:35:28 +1200 + +zaptel (1:1.0.7-3) unstable; urgency=medium + + * Closes: #302903: libtonezone1 package is empty + * Closes: #302833: binary files missing, e.g. /sbin/ztcfg + * Move debian/*.files -> debian/*.install + * Closes: #302847: zaptel command ztcfg freezes on Debian PowerPC + causing boot failure. + + -- Mark Purcell Sun, 3 Apr 2005 19:44:25 +0100 + +zaptel (1:1.0.7-2) unstable; urgency=medium + + * Debian VoIP Team upload. + * Jose Carlos: + + Working support for module-assistant included in zaptel-source. + Thanks to Eduard Bloch for his help (Closes: #301665) + + debian/control.modules.in: + - make generated modules package depend on zaptel binary package. + - updated description to refer to module-assistant. + + debian/control: + - build-depend on bzip2. + - zaptel-source depends on module-assistant tool and bzip2. + - updated and improved descriptions. + + debian/rules: + - remaked with the new m-a stuff. + - don't need dpatch installed for building the modules (Closes: #301666) + + debian/postinst: doesn't output garbage (Closes: #296958) + + debian/postrm: don't remove creeated devices. Only box admin can do + that, per Policy 10.6 + + Removed zaphfc and qozap examples from zaptel-source. Only ship them + in zaptel binary package. + + README.Debian: file added. Talk about how compile modules and + use them with udev (Closes: #163857) + + Don't install zaptel.h file in zaptel-modules packages. (Closes: #297306) + + * Kilian Krause: + + Increased urgency for fixing RC-bug and this is the last deb to + allow the whole Debian VoIP suit proceed to testing. + + -- Jose Carlos Garcia Sogo Sat, 2 Apr 2005 01:14:23 +0200 + +zaptel (1:1.0.7-1) unstable; urgency=low + + * New upstream version. + + -- Kilian Krause Sat, 19 Mar 2005 23:28:07 +0100 + +zaptel (1:1.0.6-1) unstable; urgency=low + + * New upstream version. (zaptel 1.0.6, bristuff RC7k) + * added zaphfc and qozap modules. + + -- Kilian Krause Sat, 5 Mar 2005 20:05:35 +0100 + +zaptel (1:1.0.4-3) unstable; urgency=low + + * Debian VoIP team upload. + * debian/rules, debian/zaptel-source.files: fixed zaptel.h includes problem + * debian/patches/Makefile.dpatch: imported from old package (now dpatch + instead directly in the diff). Fixed building on hosts with differring + userland and kernel arch. Now also including symlink for SONAME. + * debian/patches/bristuff.dpatch: imported bristuff patch to include zaphfc. + (Closes: #294183) + * debian/zaptel.postinst: Fixed permissions issue problem. + * debian/zaptel.modprobe.d: Added zaphfc RC7f. + + -- Kilian Krause Thu, 24 Feb 2005 01:42:36 +0100 + +zaptel (1:1.0.4-2) experimental; urgency=low + + * Better "use" of uname -r in Makefile for zaptel-source + + -- Santiago Ruano Rincon Mon, 21 Feb 2005 00:27:14 -0500 + +zaptel (1:1.0.4-1) experimental; urgency=low + + * New upstream release (zaptel-1.0.4) + * Included zttest and ztspeed binaries + * Added Depends on debhelper and Recomends zaptel for zaptel-source + * Added /etc/modprobe.d/zaptel and corrected the path for the binaries, + /sbin instead of /usr/sbin + + -- Santiago Ruano Rincon Wed, 26 Jan 2005 23:05:20 -0500 + +zaptel (1:1.0.2-2) unstable; urgency=low + + * libtonezone out of zaptel-source + * /dev/zap/ are now created by zaptel.postinst and deleted by + zaptel.postrm. Now, the zap devices match with the upstream version + (Closes: #274384). + * Added lintian overrides for mknod-in-maintainer-script warnings + * docbook-to-man out of the Build-Depends + + -- Santiago Ruano Rincon Wed, 24 Nov 2004 22:05:52 -0500 + +zaptel (1:1.0.2-1) unstable; urgency=low + + * New upstream release (zaptel-1.0.2) + + -- Santiago Ruano Rincon Sat, 30 Oct 2004 00:51:54 -0500 + +zaptel (1:1.0.0-2) unstable; urgency=low + + * New maintainer (Closes: #251938). + * Man pages created for ztcfg, ztmonitor and zttool + (Closes: #274632, #274633, #274634). + * Mark Purcell made the package for version 1.0 + (Closes: #273255, #251929). + * zaptel-modules can be build from zaptel-source with + make-kpkg (Closes: #274085). + * Now it compiles for 2.6 Kernels (Closes: #251930). + + -- Santiago Ruano Rincon Sun, 26 Sep 2004 02:05:44 -0500 + +zaptel (1:1.0.0-1) unstable; urgency=low + + * NMU (See Bug#251938) + * New upstream release + + -- Mark Purcell Fri, 24 Sep 2004 22:46:55 +1000 + +zaptel (1:0.8.1+1.0-RC2-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell Thu, 9 Sep 2004 19:17:28 +1000 + +zaptel (1:0.8.1+1.0-RC1-1) unstable; urgency=low + + * New upstream release + * Add a debian/watch file + + -- Mark Purcell Wed, 21 Jul 2004 17:51:22 +1000 + +zaptel (1:0.8.1-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Wed, 11 Feb 2004 15:29:20 -0800 + +zaptel (1:0.8.0-2) unstable; urgency=low + + * Create usr/include ahead of time so that tonezone.h is installed + correctly (Closes: #227795) + + -- Matt Zimmerman Wed, 14 Jan 2004 17:24:26 -0800 + +zaptel (1:0.8.0-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Tue, 13 Jan 2004 14:44:56 -0800 + +zaptel (1:0.6.0-2) unstable; urgency=low + + * Rebuild with new libnewt + + -- Matt Zimmerman Mon, 30 Jun 2003 22:51:18 -0400 + +zaptel (1:0.6.0-1) unstable; urgency=low + + * New upstream release, needed for new asterisk (Closes: #189661) + + -- Matt Zimmerman Sat, 19 Apr 2003 23:56:59 -0400 + +zaptel (1:0.4.0-2) unstable; urgency=low + + * Break out into {build,install,binary}-indep targets + (Closes: #184528) + * libtonezone-dev Section: libdevel + * Escape $ properly in instructions in postinst + + -- Matt Zimmerman Wed, 12 Mar 2003 19:16:10 -0500 + +zaptel (1:0.4.0-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Sun, 16 Feb 2003 15:12:02 -0500 + +zaptel (0.cvs.20021029-1) unstable; urgency=low + + * New upstream CVS + * Use MARK2 echo canceller + + -- Matt Zimmerman Tue, 29 Oct 2002 10:37:53 -0500 + +zaptel (0.cvs.20020729-1) unstable; urgency=low + + * New upstream CVS + * Include ztmonitor binary + + -- Matt Zimmerman Mon, 29 Jul 2002 12:36:58 -0400 + +zaptel (0.cvs.20020708-1) unstable; urgency=low + + * New upstream CVS + + -- Matt Zimmerman Mon, 8 Jul 2002 15:32:20 -0400 + +zaptel (0.cvs.20020624-2) unstable; urgency=low + + * Include Makefile in the -source package (oops, Closes: #152014) + + -- Matt Zimmerman Fri, 5 Jul 2002 11:00:08 -0400 + +zaptel (0.cvs.20020624-1) unstable; urgency=low + + * Initial Release (Closes: #150874) + + -- Matt Zimmerman Mon, 17 Jun 2002 10:31:21 -0400 + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/TODO.Debian +++ dahdi-linux-2.2.0~dfsg~rc5/debian/TODO.Debian @@ -0,0 +1,2 @@ +* Add some lintian ignore lines. +* Fix other modules. --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/make_static_nodes +++ dahdi-linux-2.2.0~dfsg~rc5/debian/make_static_nodes @@ -0,0 +1,25 @@ +#!/bin/sh + +# Generate static device file nodes for dahdi +# Most peopel won't need this, as the device file nodes are normally +# generated by udev. Also note that if you do use udev, /dev is a +# ramdisk, and thus any changes you do to it will not be preserved +# on next boot. + +mknod_safe() { + if [ -c $1 ]; then return; fi + mknod "$@" || true +} + +mkdir -p /dev/dahdi +mknod_safe /dev/dahdi/ctl c 196 0 +mknod_safe /dev/dahdi/transcode c 196 250 +mknod_safe /dev/dahdi/timer c 196 253 +mknod_safe /dev/dahdi/channel c 196 254 +mknod_safe /dev/dahdi/pseudo c 196 255 +for N in `seq 249`; do + mknod_safe /dev/dahdi/$N c 196 $N +done +chown -R 0:dialout /dev/dahdi/ +chmod 0660 /dev/dahdi/* + --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/preinst +++ dahdi-linux-2.2.0~dfsg~rc5/debian/preinst @@ -0,0 +1,3 @@ +#!/bin/sh + +#DEBHELPER# --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/rules +++ dahdi-linux-2.2.0~dfsg~rc5/debian/rules @@ -0,0 +1,197 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatibility version to use. +USE_BRISTUFF=0 +export HOTPLUG_FIRMWARE=1 + +-include /usr/share/quilt/quilt.make + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -g +endif + +## MODULE-ASSISTANT STUFF +# prefix of the target package name +PREFIX:=dahdi +SKPG:=$(PREFIX)-source +PACKAGE:=$(PREFIX)-modules +PACKAGE_SRC:=$(PREFIX)-linux +# modifieable for experiments or debugging m-a +MA_DIR ?= /usr/share/modass +# load generic variable handling +-include $(MA_DIR)/include/generic.make +# load default rules +-include $(MA_DIR)/include/common-rules.make + +DEBVERSION:=$(shell head -n 1 debian/changelog \ + | sed -e 's/^[^(]*(\([^)]*\)).*/\1/') +DEB_BASE_VERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9.]*$$//') +UPVERSION:=$(shell echo $(DEB_BASE_VERSION) | sed -e 's/~dfsg\(~\|$$\)/\1/' -e 's/~\(rc\|beta\)/-\1/') + +UPFILENAME := $(PACKAGE_SRC)_$(UPVERSION).orig.tar.gz +FILENAME := $(PACKAGE_SRC)_$(DEB_BASE_VERSION).orig.tar.gz +URL := http://downloads.digium.com/pub/telephony/$(PACKAGE_SRC)/releases/$(PACKAGE_SRC)-$(UPVERSION).tar.gz + +# If the makefile was properly-writen, there was a good separation +# between kernel and userspace. As things stand now I'd like to work +# around problems with bilding stuff with HOSTCC by simply providing +# the generated headers as part of the source: +GENERATED_SOURCES := include/dahdi/version.h + +EXTRA_MODS=opvxa1200 + +ifneq (,$(filter-out powerpc m68k armeb mips,$(shell dpkg-architecture -qDEB_HOST_ARCH))) +EXTRA_MODS += wcopenpci zaphfc +endif + +#BRISTUFF_MODULES=cwain qozap zaphfc ztgsm +MOD_EXAMPLES_DIR:=modexamples +ifeq (1,$(USE_BRISTUFF)) + BRISTUFF_EXAMPLES:=$(MOD_EXAMPLES_DIR)/* + # bristuff modules that are copied from a subdirectory to the current + # directory. + EXTRA_MODS+=$(BRISTUFF_MODULES) + EXTRA_SUBDIRS += vzaphfc +endif + +EXTRA_SUBDIRS += ../staging/echo +EXTRA_MODS += dahdi_echocan_oslec + +MOD_ARGS=MODULES_EXTRA="$(EXTRA_MODS)" SUBDIRS_EXTRA="$(EXTRA_SUBDIRS)" + +kdist_clean: clean-unpatched + +kdist_config: prep-deb-files + +binary-modules: prep-deb-files + dh_testdir + dh_testroot + dh_clean -k + #cp -a $(CURDIR)/debian/generated/* . + make $(MOD_ARGS) modules KERNEL_SOURCES=$(KSRC) MODVERSIONS=detect KERNEL=linux-$(KVERS) + make $(MOD_ARGS) install-modules KERNELRELEASE=$(KVERS) DESTDIR=$(CURDIR)/debian/$(PKGNAME) +ifeq (2.6,$(shell echo $(KVERS) | cut -d. -f1-2)) + # The 2.6 modules are way too big. This is only in kernel 2.6 + find debian/$(PKGNAME)/lib/modules -name '*.ko' |xargs strip -g + $(RM) -f debian/$(PKGNAME)/lib/modules/$(KVERS)/modules.* +endif + dh_installmodules + dh_installdebconf + dh_installchangelogs + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol -- -v$(VERSION) + dh_md5sums + dh_builddeb --destdir=$(DEB_DESTDIR) + +## END OF M-A SECTION + +build: patch build-stamp +build-stamp: + dh_testdir + + $(MAKE) docs + for mod in $(BRISTUFF_MODULES); do for i in c h; do \ + ln -s $$mod/$$mod.$$i drivers/dahdi/$$mod.$$i; \ + done; done + $(MAKE) $(GENERATED_SOURCES) + touch $@ + +clean: clean-unpatched unpatch +clean-unpatched: + dh_testdir + dh_testroot + rm -f *-stamp + + # Delete the generated bristuff symlinks: + $(RM) -f $(BRISTUFF_MODULES:%=%.[ch]) + # Add here commands to clean up after the build process. + rm -f $(GENERATED_SOURCES) + # * Makefile does not exist when running svn-buildpackage + # as the source tree is not there. + # FIXME: This will fail with an ugly warning on the clean of the + # modules build. However only fter the actuual clean. + rm -f dahdi/include/version.h + [ ! -f Makefile ] || $(MAKE) dist-clean || true + dh_clean + +TARPARDIR=$(CURDIR)/debian/tmp +TARDIR=$(TARPARDIR)/modules/$(PREFIX) +install: build-stamp + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + $(MAKE) install-include install-udev DESTDIR=$(CURDIR)/debian/$(SKPG) + dh_install + # driver source code + mkdir -p $(TARDIR)/debian/generated + cp Makefile $(TARDIR)/ + for dir in build_tools firmware include drivers; do \ + if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \ + done + + # Packaging infrastructure + cp -r debian/rules debian/changelog debian/copyright\ + debian/control debian/compat \ + debian/control.modules.in \ + $(TARDIR)/debian/ + + tar cjf debian/$(SKPG)/usr/src/$(PREFIX).tar.bz2 \ + -C $(TARPARDIR) modules +ifeq (1,$(USE_BRISTUFF)) + set -e; for module in cwain qozap zaphfc ztgsm; do \ + mkdir -p $(MOD_EXAMPLES_DIR)/$$module; \ + cp -a kernel/$$module/*.conf* $(MOD_EXAMPLES_DIR)/$$module; \ + done +else + mkdir -p $(MOD_EXAMPLES_DIR)/bristuff_stub +endif + dh_installudev --name=dahdi + +# Build architecture-independent files here. +binary: install + dh_testdir + dh_testroot + + dh_installdocs + dh_installchangelogs -i ChangeLog + #dh_installexamples -i $(BRISTUFF_EXAMPLES) + dh_perl + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +print-version: + @@echo "Debian version: $(DEBVERSION)" + @@echo "Upstream version: $(UPVERSION)" + +TARBALL_DIR=../tarballs/$(PACKAGE_SRC)-$(UPVERSION).tmp +get-orig-source: + @@dh_testdir + @@[ -d ../tarballs/. ]||mkdir -p ../tarballs + @@echo Downloading $(UPFILENAME) from $(URL) ... + @@wget -nv -T10 -t3 --verbose -O ../tarballs/$(UPFILENAME) $(URL) + @@echo Repacking as DFSG-free... + @@mkdir -p $(TARBALL_DIR)/ + @@cd $(TARBALL_DIR) ; \ + tar xfz ../$(UPFILENAME) + @@rm -rf $(TARBALL_DIR)/$(PACKAGE_SRC)-$(UPVERSION)/drivers/dahdi/xpp/firmwares/*.hex + @@cd $(TARBALL_DIR) ; \ + tar cfz ../$(FILENAME) * + @@echo Cleaning up... + @@$(RM) -rf $(TARBALL_DIR)/ + @@$(RM) -f ../tarballs/$(UPFILENAME) + +.PHONY: build clean binary install configure patch unpatch --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/oslec_kernelorg +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/oslec_kernelorg @@ -0,0 +1,10429 @@ +The oslec (echo) directory from from staging in linux-2.6.28-rc2 + +diff -Nurp base/drivers/staging/echo/bit_operations.h new/drivers/staging/echo/bit_operations.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/bit_operations.h 2008-10-30 17:13:48.000000000 +0200 +@@ -0,0 +1,228 @@ ++/* ++ * SpanDSP - a series of DSP components for telephony ++ * ++ * bit_operations.h - Various bit level operations, such as bit reversal ++ * ++ * Written by Steve Underwood ++ * ++ * Copyright (C) 2006 Steve Underwood ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * $Id: bit_operations.h,v 1.11 2006/11/28 15:37:03 steveu Exp $ ++ */ ++ ++/*! \file */ ++ ++#if !defined(_BIT_OPERATIONS_H_) ++#define _BIT_OPERATIONS_H_ ++ ++#if defined(__i386__) || defined(__x86_64__) ++/*! \brief Find the bit position of the highest set bit in a word ++ \param bits The word to be searched ++ \return The bit number of the highest set bit, or -1 if the word is zero. */ ++static __inline__ int top_bit(unsigned int bits) ++{ ++ int res; ++ ++ __asm__(" xorl %[res],%[res];\n" ++ " decl %[res];\n" ++ " bsrl %[bits],%[res]\n" ++ :[res] "=&r" (res) ++ :[bits] "rm"(bits) ++ ); ++ return res; ++} ++ ++/*! \brief Find the bit position of the lowest set bit in a word ++ \param bits The word to be searched ++ \return The bit number of the lowest set bit, or -1 if the word is zero. */ ++static __inline__ int bottom_bit(unsigned int bits) ++{ ++ int res; ++ ++ __asm__(" xorl %[res],%[res];\n" ++ " decl %[res];\n" ++ " bsfl %[bits],%[res]\n" ++ :[res] "=&r" (res) ++ :[bits] "rm"(bits) ++ ); ++ return res; ++} ++#else ++static __inline__ int top_bit(unsigned int bits) ++{ ++ int i; ++ ++ if (bits == 0) ++ return -1; ++ i = 0; ++ if (bits & 0xFFFF0000) { ++ bits &= 0xFFFF0000; ++ i += 16; ++ } ++ if (bits & 0xFF00FF00) { ++ bits &= 0xFF00FF00; ++ i += 8; ++ } ++ if (bits & 0xF0F0F0F0) { ++ bits &= 0xF0F0F0F0; ++ i += 4; ++ } ++ if (bits & 0xCCCCCCCC) { ++ bits &= 0xCCCCCCCC; ++ i += 2; ++ } ++ if (bits & 0xAAAAAAAA) { ++ bits &= 0xAAAAAAAA; ++ i += 1; ++ } ++ return i; ++} ++ ++static __inline__ int bottom_bit(unsigned int bits) ++{ ++ int i; ++ ++ if (bits == 0) ++ return -1; ++ i = 32; ++ if (bits & 0x0000FFFF) { ++ bits &= 0x0000FFFF; ++ i -= 16; ++ } ++ if (bits & 0x00FF00FF) { ++ bits &= 0x00FF00FF; ++ i -= 8; ++ } ++ if (bits & 0x0F0F0F0F) { ++ bits &= 0x0F0F0F0F; ++ i -= 4; ++ } ++ if (bits & 0x33333333) { ++ bits &= 0x33333333; ++ i -= 2; ++ } ++ if (bits & 0x55555555) { ++ bits &= 0x55555555; ++ i -= 1; ++ } ++ return i; ++} ++#endif ++ ++/*! \brief Bit reverse a byte. ++ \param data The byte to be reversed. ++ \return The bit reversed version of data. */ ++static inline uint8_t bit_reverse8(uint8_t x) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ /* If multiply is fast */ ++ return ((x * 0x0802U & 0x22110U) | (x * 0x8020U & 0x88440U)) * ++ 0x10101U >> 16; ++#else ++ /* If multiply is slow, but we have a barrel shifter */ ++ x = (x >> 4) | (x << 4); ++ x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2); ++ return ((x & 0xAA) >> 1) | ((x & 0x55) << 1); ++#endif ++} ++ ++/*! \brief Bit reverse a 16 bit word. ++ \param data The word to be reversed. ++ \return The bit reversed version of data. */ ++uint16_t bit_reverse16(uint16_t data); ++ ++/*! \brief Bit reverse a 32 bit word. ++ \param data The word to be reversed. ++ \return The bit reversed version of data. */ ++uint32_t bit_reverse32(uint32_t data); ++ ++/*! \brief Bit reverse each of the four bytes in a 32 bit word. ++ \param data The word to be reversed. ++ \return The bit reversed version of data. */ ++uint32_t bit_reverse_4bytes(uint32_t data); ++ ++/*! \brief Find the number of set bits in a 32 bit word. ++ \param x The word to be searched. ++ \return The number of set bits. */ ++int one_bits32(uint32_t x); ++ ++/*! \brief Create a mask as wide as the number in a 32 bit word. ++ \param x The word to be searched. ++ \return The mask. */ ++uint32_t make_mask32(uint32_t x); ++ ++/*! \brief Create a mask as wide as the number in a 16 bit word. ++ \param x The word to be searched. ++ \return The mask. */ ++uint16_t make_mask16(uint16_t x); ++ ++/*! \brief Find the least significant one in a word, and return a word ++ with just that bit set. ++ \param x The word to be searched. ++ \return The word with the single set bit. */ ++static __inline__ uint32_t least_significant_one32(uint32_t x) ++{ ++ return (x & (-(int32_t) x)); ++} ++ ++/*! \brief Find the most significant one in a word, and return a word ++ with just that bit set. ++ \param x The word to be searched. ++ \return The word with the single set bit. */ ++static __inline__ uint32_t most_significant_one32(uint32_t x) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ return 1 << top_bit(x); ++#else ++ x = make_mask32(x); ++ return (x ^ (x >> 1)); ++#endif ++} ++ ++/*! \brief Find the parity of a byte. ++ \param x The byte to be checked. ++ \return 1 for odd, or 0 for even. */ ++static __inline__ int parity8(uint8_t x) ++{ ++ x = (x ^ (x >> 4)) & 0x0F; ++ return (0x6996 >> x) & 1; ++} ++ ++/*! \brief Find the parity of a 16 bit word. ++ \param x The word to be checked. ++ \return 1 for odd, or 0 for even. */ ++static __inline__ int parity16(uint16_t x) ++{ ++ x ^= (x >> 8); ++ x = (x ^ (x >> 4)) & 0x0F; ++ return (0x6996 >> x) & 1; ++} ++ ++/*! \brief Find the parity of a 32 bit word. ++ \param x The word to be checked. ++ \return 1 for odd, or 0 for even. */ ++static __inline__ int parity32(uint32_t x) ++{ ++ x ^= (x >> 16); ++ x ^= (x >> 8); ++ x = (x ^ (x >> 4)) & 0x0F; ++ return (0x6996 >> x) & 1; ++} ++ ++#endif ++/*- End of file ------------------------------------------------------------*/ +diff -Nurp base/drivers/staging/echo/echo.c new/drivers/staging/echo/echo.c +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo.c 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,639 @@ ++/* ++ * SpanDSP - a series of DSP components for telephony ++ * ++ * echo.c - A line echo canceller. This code is being developed ++ * against and partially complies with G168. ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe ++ * ++ * Based on a bit from here, a bit from there, eye of toad, ear of ++ * bat, 15 years of failed attempts by David and a few fried brain ++ * cells. ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $ ++ */ ++ ++/*! \file */ ++ ++/* Implementation Notes ++ David Rowe ++ April 2007 ++ ++ This code started life as Steve's NLMS algorithm with a tap ++ rotation algorithm to handle divergence during double talk. I ++ added a Geigel Double Talk Detector (DTD) [2] and performed some ++ G168 tests. However I had trouble meeting the G168 requirements, ++ especially for double talk - there were always cases where my DTD ++ failed, for example where near end speech was under the 6dB ++ threshold required for declaring double talk. ++ ++ So I tried a two path algorithm [1], which has so far given better ++ results. The original tap rotation/Geigel algorithm is available ++ in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. ++ It's probably possible to make it work if some one wants to put some ++ serious work into it. ++ ++ At present no special treatment is provided for tones, which ++ generally cause NLMS algorithms to diverge. Initial runs of a ++ subset of the G168 tests for tones (e.g ./echo_test 6) show the ++ current algorithm is passing OK, which is kind of surprising. The ++ full set of tests needs to be performed to confirm this result. ++ ++ One other interesting change is that I have managed to get the NLMS ++ code to work with 16 bit coefficients, rather than the original 32 ++ bit coefficents. This reduces the MIPs and storage required. ++ I evaulated the 16 bit port using g168_tests.sh and listening tests ++ on 4 real-world samples. ++ ++ I also attempted the implementation of a block based NLMS update ++ [2] but although this passes g168_tests.sh it didn't converge well ++ on the real-world samples. I have no idea why, perhaps a scaling ++ problem. The block based code is also available in SVN ++ http://svn.rowetel.com/software/oslec/tags/before_16bit. If this ++ code can be debugged, it will lead to further reduction in MIPS, as ++ the block update code maps nicely onto DSP instruction sets (it's a ++ dot product) compared to the current sample-by-sample update. ++ ++ Steve also has some nice notes on echo cancellers in echo.h ++ ++ References: ++ ++ [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo ++ Path Models", IEEE Transactions on communications, COM-25, ++ No. 6, June ++ 1977. ++ http://www.rowetel.com/images/echo/dual_path_paper.pdf ++ ++ [2] The classic, very useful paper that tells you how to ++ actually build a real world echo canceller: ++ Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice ++ Echo Canceller with a TMS320020, ++ http://www.rowetel.com/images/echo/spra129.pdf ++ ++ [3] I have written a series of blog posts on this work, here is ++ Part 1: http://www.rowetel.com/blog/?p=18 ++ ++ [4] The source code http://svn.rowetel.com/software/oslec/ ++ ++ [5] A nice reference on LMS filters: ++ http://en.wikipedia.org/wiki/Least_mean_squares_filter ++ ++ Credits: ++ ++ Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan ++ Muthukrishnan for their suggestions and email discussions. Thanks ++ also to those people who collected echo samples for me such as ++ Mark, Pawel, and Pavel. ++*/ ++ ++#include /* We're doing kernel work */ ++#include ++#include ++#include ++ ++#include "bit_operations.h" ++#include "echo.h" ++ ++#define MIN_TX_POWER_FOR_ADAPTION 64 ++#define MIN_RX_POWER_FOR_ADAPTION 64 ++#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ ++#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ ++ ++/*-----------------------------------------------------------------------*\ ++ FUNCTIONS ++\*-----------------------------------------------------------------------*/ ++ ++/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ ++ ++#ifdef __bfin__ ++static void __inline__ lms_adapt_bg(struct oslec_state *ec, int clean, ++ int shift) ++{ ++ int i, j; ++ int offset1; ++ int offset2; ++ int factor; ++ int exp; ++ int16_t *phist; ++ int n; ++ ++ if (shift > 0) ++ factor = clean << shift; ++ else ++ factor = clean >> -shift; ++ ++ /* Update the FIR taps */ ++ ++ offset2 = ec->curr_pos; ++ offset1 = ec->taps - offset2; ++ phist = &ec->fir_state_bg.history[offset2]; ++ ++ /* st: and en: help us locate the assembler in echo.s */ ++ ++ //asm("st:"); ++ n = ec->taps; ++ for (i = 0, j = offset2; i < n; i++, j++) { ++ exp = *phist++ * factor; ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++ //asm("en:"); ++ ++ /* Note the asm for the inner loop above generated by Blackfin gcc ++ 4.1.1 is pretty good (note even parallel instructions used): ++ ++ R0 = W [P0++] (X); ++ R0 *= R2; ++ R0 = R0 + R3 (NS) || ++ R1 = W [P1] (X) || ++ nop; ++ R0 >>>= 15; ++ R0 = R0 + R1; ++ W [P1++] = R0; ++ ++ A block based update algorithm would be much faster but the ++ above can't be improved on much. Every instruction saved in ++ the loop above is 2 MIPs/ch! The for loop above is where the ++ Blackfin spends most of it's time - about 17 MIPs/ch measured ++ with speedtest.c with 256 taps (32ms). Write-back and ++ Write-through cache gave about the same performance. ++ */ ++} ++ ++/* ++ IDEAS for further optimisation of lms_adapt_bg(): ++ ++ 1/ The rounding is quite costly. Could we keep as 32 bit coeffs ++ then make filter pluck the MS 16-bits of the coeffs when filtering? ++ However this would lower potential optimisation of filter, as I ++ think the dual-MAC architecture requires packed 16 bit coeffs. ++ ++ 2/ Block based update would be more efficient, as per comments above, ++ could use dual MAC architecture. ++ ++ 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC ++ packing. ++ ++ 4/ Execute the whole e/c in a block of say 20ms rather than sample ++ by sample. Processing a few samples every ms is inefficient. ++*/ ++ ++#else ++static __inline__ void lms_adapt_bg(struct oslec_state *ec, int clean, ++ int shift) ++{ ++ int i; ++ ++ int offset1; ++ int offset2; ++ int factor; ++ int exp; ++ ++ if (shift > 0) ++ factor = clean << shift; ++ else ++ factor = clean >> -shift; ++ ++ /* Update the FIR taps */ ++ ++ offset2 = ec->curr_pos; ++ offset1 = ec->taps - offset2; ++ ++ for (i = ec->taps - 1; i >= offset1; i--) { ++ exp = (ec->fir_state_bg.history[i - offset1] * factor); ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++ for (; i >= 0; i--) { ++ exp = (ec->fir_state_bg.history[i + offset2] * factor); ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++} ++#endif ++ ++struct oslec_state *oslec_create(int len, int adaption_mode) ++{ ++ struct oslec_state *ec; ++ int i; ++ ++ ec = kzalloc(sizeof(*ec), GFP_KERNEL); ++ if (!ec) ++ return NULL; ++ ++ ec->taps = len; ++ ec->log2taps = top_bit(len); ++ ec->curr_pos = ec->taps - 1; ++ ++ for (i = 0; i < 2; i++) { ++ ec->fir_taps16[i] = ++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->fir_taps16[i]) ++ goto error_oom; ++ } ++ ++ fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps); ++ fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps); ++ ++ for (i = 0; i < 5; i++) { ++ ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; ++ } ++ ++ ec->cng_level = 1000; ++ oslec_adaption_mode(ec, adaption_mode); ++ ++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->snapshot) ++ goto error_oom; ++ ++ ec->cond_met = 0; ++ ec->Pstates = 0; ++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; ++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; ++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; ++ ec->Lbgn = ec->Lbgn_acc = 0; ++ ec->Lbgn_upper = 200; ++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; ++ ++ return ec; ++ ++ error_oom: ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ ++ kfree(ec); ++ return NULL; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_create); ++ ++void oslec_free(struct oslec_state *ec) ++{ ++ int i; ++ ++ fir16_free(&ec->fir_state); ++ fir16_free(&ec->fir_state_bg); ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ kfree(ec->snapshot); ++ kfree(ec); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_free); ++ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) ++{ ++ ec->adaption_mode = adaption_mode; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_adaption_mode); ++ ++void oslec_flush(struct oslec_state *ec) ++{ ++ int i; ++ ++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; ++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; ++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; ++ ++ ec->Lbgn = ec->Lbgn_acc = 0; ++ ec->Lbgn_upper = 200; ++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; ++ ++ ec->nonupdate_dwell = 0; ++ ++ fir16_flush(&ec->fir_state); ++ fir16_flush(&ec->fir_state_bg); ++ ec->fir_state.curr_pos = ec->taps - 1; ++ ec->fir_state_bg.curr_pos = ec->taps - 1; ++ for (i = 0; i < 2; i++) ++ memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t)); ++ ++ ec->curr_pos = ec->taps - 1; ++ ec->Pstates = 0; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_flush); ++ ++void oslec_snapshot(struct oslec_state *ec) ++{ ++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_snapshot); ++ ++/* Dual Path Echo Canceller ------------------------------------------------*/ ++ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) ++{ ++ int32_t echo_value; ++ int clean_bg; ++ int tmp, tmp1; ++ ++ /* Input scaling was found be required to prevent problems when tx ++ starts clipping. Another possible way to handle this would be the ++ filter coefficent scaling. */ ++ ++ ec->tx = tx; ++ ec->rx = rx; ++ tx >>= 1; ++ rx >>= 1; ++ ++ /* ++ Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required ++ otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta) ++ only real axis. Some chip sets (like Si labs) don't need ++ this, but something like a $10 X100P card does. Any DC really slows ++ down convergence. ++ ++ Note: removes some low frequency from the signal, this reduces ++ the speech quality when listening to samples through headphones ++ but may not be obvious through a telephone handset. ++ ++ Note that the 3dB frequency in radians is approx Beta, e.g. for ++ Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. ++ */ ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { ++ tmp = rx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. This can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; ++ ++ /* hard limit filter to prevent clipping. Note that at this stage ++ rx should be limited to +/- 16383 due to right shift above */ ++ tmp1 = ec->rx_1 >> 15; ++ if (tmp1 > 16383) ++ tmp1 = 16383; ++ if (tmp1 < -16383) ++ tmp1 = -16383; ++ rx = tmp1; ++ ec->rx_2 = tmp; ++ } ++ ++ /* Block average of power in the filter states. Used for ++ adaption power calculation. */ ++ ++ { ++ int new, old; ++ ++ /* efficient "out with the old and in with the new" algorithm so ++ we don't have to recalculate over the whole block of ++ samples. */ ++ new = (int)tx *(int)tx; ++ old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * ++ (int)ec->fir_state.history[ec->fir_state.curr_pos]; ++ ec->Pstates += ++ ((new - old) + (1 << ec->log2taps)) >> ec->log2taps; ++ if (ec->Pstates < 0) ++ ec->Pstates = 0; ++ } ++ ++ /* Calculate short term average levels using simple single pole IIRs */ ++ ++ ec->Ltxacc += abs(tx) - ec->Ltx; ++ ec->Ltx = (ec->Ltxacc + (1 << 4)) >> 5; ++ ec->Lrxacc += abs(rx) - ec->Lrx; ++ ec->Lrx = (ec->Lrxacc + (1 << 4)) >> 5; ++ ++ /* Foreground filter --------------------------------------------------- */ ++ ++ ec->fir_state.coeffs = ec->fir_taps16[0]; ++ echo_value = fir16(&ec->fir_state, tx); ++ ec->clean = rx - echo_value; ++ ec->Lcleanacc += abs(ec->clean) - ec->Lclean; ++ ec->Lclean = (ec->Lcleanacc + (1 << 4)) >> 5; ++ ++ /* Background filter --------------------------------------------------- */ ++ ++ echo_value = fir16(&ec->fir_state_bg, tx); ++ clean_bg = rx - echo_value; ++ ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; ++ ec->Lclean_bg = (ec->Lclean_bgacc + (1 << 4)) >> 5; ++ ++ /* Background Filter adaption ----------------------------------------- */ ++ ++ /* Almost always adap bg filter, just simple DT and energy ++ detection to minimise adaption in cases of strong double talk. ++ However this is not critical for the dual path algorithm. ++ */ ++ ec->factor = 0; ++ ec->shift = 0; ++ if ((ec->nonupdate_dwell == 0)) { ++ int P, logP, shift; ++ ++ /* Determine: ++ ++ f = Beta * clean_bg_rx/P ------ (1) ++ ++ where P is the total power in the filter states. ++ ++ The Boffins have shown that if we obey (1) we converge ++ quickly and avoid instability. ++ ++ The correct factor f must be in Q30, as this is the fixed ++ point format required by the lms_adapt_bg() function, ++ therefore the scaled version of (1) is: ++ ++ (2^30) * f = (2^30) * Beta * clean_bg_rx/P ++ factor = (2^30) * Beta * clean_bg_rx/P ----- (2) ++ ++ We have chosen Beta = 0.25 by experiment, so: ++ ++ factor = (2^30) * (2^-2) * clean_bg_rx/P ++ ++ (30 - 2 - log2(P)) ++ factor = clean_bg_rx 2 ----- (3) ++ ++ To avoid a divide we approximate log2(P) as top_bit(P), ++ which returns the position of the highest non-zero bit in ++ P. This approximation introduces an error as large as a ++ factor of 2, but the algorithm seems to handle it OK. ++ ++ Come to think of it a divide may not be a big deal on a ++ modern DSP, so its probably worth checking out the cycles ++ for a divide versus a top_bit() implementation. ++ */ ++ ++ P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; ++ logP = top_bit(P) + ec->log2taps; ++ shift = 30 - 2 - logP; ++ ec->shift = shift; ++ ++ lms_adapt_bg(ec, clean_bg, shift); ++ } ++ ++ /* very simple DTD to make sure we dont try and adapt with strong ++ near end speech */ ++ ++ ec->adapt = 0; ++ if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) ++ ec->nonupdate_dwell = DTD_HANGOVER; ++ if (ec->nonupdate_dwell) ++ ec->nonupdate_dwell--; ++ ++ /* Transfer logic ------------------------------------------------------ */ ++ ++ /* These conditions are from the dual path paper [1], I messed with ++ them a bit to improve performance. */ ++ ++ if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && ++ (ec->nonupdate_dwell == 0) && ++ (8 * ec->Lclean_bg < ++ 7 * ec->Lclean) /* (ec->Lclean_bg < 0.875*ec->Lclean) */ && ++ (8 * ec->Lclean_bg < ++ ec->Ltx) /* (ec->Lclean_bg < 0.125*ec->Ltx) */ ) { ++ if (ec->cond_met == 6) { ++ /* BG filter has had better results for 6 consecutive samples */ ++ ec->adapt = 1; ++ memcpy(ec->fir_taps16[0], ec->fir_taps16[1], ++ ec->taps * sizeof(int16_t)); ++ } else ++ ec->cond_met++; ++ } else ++ ec->cond_met = 0; ++ ++ /* Non-Linear Processing --------------------------------------------------- */ ++ ++ ec->clean_nlp = ec->clean; ++ if (ec->adaption_mode & ECHO_CAN_USE_NLP) { ++ /* Non-linear processor - a fancy way to say "zap small signals, to avoid ++ residual echo due to (uLaw/ALaw) non-linearity in the channel.". */ ++ ++ if ((16 * ec->Lclean < ec->Ltx)) { ++ /* Our e/c has improved echo by at least 24 dB (each factor of 2 is 6dB, ++ so 2*2*2*2=16 is the same as 6+6+6+6=24dB) */ ++ if (ec->adaption_mode & ECHO_CAN_USE_CNG) { ++ ec->cng_level = ec->Lbgn; ++ ++ /* Very elementary comfort noise generation. Just random ++ numbers rolled off very vaguely Hoth-like. DR: This ++ noise doesn't sound quite right to me - I suspect there ++ are some overlfow issues in the filtering as it's too ++ "crackly". TODO: debug this, maybe just play noise at ++ high level or look at spectrum. ++ */ ++ ++ ec->cng_rndnum = ++ 1664525U * ec->cng_rndnum + 1013904223U; ++ ec->cng_filter = ++ ((ec->cng_rndnum & 0xFFFF) - 32768 + ++ 5 * ec->cng_filter) >> 3; ++ ec->clean_nlp = ++ (ec->cng_filter * ec->cng_level * 8) >> 14; ++ ++ } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) { ++ /* This sounds much better than CNG */ ++ if (ec->clean_nlp > ec->Lbgn) ++ ec->clean_nlp = ec->Lbgn; ++ if (ec->clean_nlp < -ec->Lbgn) ++ ec->clean_nlp = -ec->Lbgn; ++ } else { ++ /* just mute the residual, doesn't sound very good, used mainly ++ in G168 tests */ ++ ec->clean_nlp = 0; ++ } ++ } else { ++ /* Background noise estimator. I tried a few algorithms ++ here without much luck. This very simple one seems to ++ work best, we just average the level using a slow (1 sec ++ time const) filter if the current level is less than a ++ (experimentally derived) constant. This means we dont ++ include high level signals like near end speech. When ++ combined with CNG or especially CLIP seems to work OK. ++ */ ++ if (ec->Lclean < 40) { ++ ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; ++ ec->Lbgn = (ec->Lbgn_acc + (1 << 11)) >> 12; ++ } ++ } ++ } ++ ++ /* Roll around the taps buffer */ ++ if (ec->curr_pos <= 0) ++ ec->curr_pos = ec->taps; ++ ec->curr_pos--; ++ ++ if (ec->adaption_mode & ECHO_CAN_DISABLE) ++ ec->clean_nlp = rx; ++ ++ /* Output scaled back up again to match input scaling */ ++ ++ return (int16_t) ec->clean_nlp << 1; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_update); ++ ++/* This function is seperated from the echo canceller is it is usually called ++ as part of the tx process. See rx HP (DC blocking) filter above, it's ++ the same design. ++ ++ Some soft phones send speech signals with a lot of low frequency ++ energy, e.g. down to 20Hz. This can make the hybrid non-linear ++ which causes the echo canceller to fall over. This filter can help ++ by removing any low frequency before it gets to the tx port of the ++ hybrid. ++ ++ It can also help by removing and DC in the tx signal. DC is bad ++ for LMS algorithms. ++ ++ This is one of the classic DC removal filters, adjusted to provide sufficient ++ bass rolloff to meet the above requirement to protect hybrids from things that ++ upset them. The difference between successive samples produces a lousy HPF, and ++ then a suitably placed pole flattens things out. The final result is a nicely ++ rolled off bass end. The filtering is implemented with extended fractional ++ precision, which noise shapes things, giving very clean DC removal. ++*/ ++ ++int16_t oslec_hpf_tx(struct oslec_state * ec, int16_t tx) ++{ ++ int tmp, tmp1; ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { ++ tmp = tx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. The first can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2; ++ tmp1 = ec->tx_1 >> 15; ++ if (tmp1 > 32767) ++ tmp1 = 32767; ++ if (tmp1 < -32767) ++ tmp1 = -32767; ++ tx = tmp1; ++ ec->tx_2 = tmp; ++ } ++ ++ return tx; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_hpf_tx); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Rowe"); ++MODULE_DESCRIPTION("Open Source Line Echo Canceller"); ++MODULE_VERSION("0.3.0"); +diff -Nurp base/drivers/staging/echo/echo.h new/drivers/staging/echo/echo.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,172 @@ ++/* ++ * SpanDSP - a series of DSP components for telephony ++ * ++ * echo.c - A line echo canceller. This code is being developed ++ * against and partially complies with G168. ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001 Steve Underwood and 2007 David Rowe ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * $Id: echo.h,v 1.9 2006/10/24 13:45:28 steveu Exp $ ++ */ ++ ++#ifndef __ECHO_H ++#define __ECHO_H ++ ++/*! \page echo_can_page Line echo cancellation for voice ++ ++\section echo_can_page_sec_1 What does it do? ++This module aims to provide G.168-2002 compliant echo cancellation, to remove ++electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. ++ ++\section echo_can_page_sec_2 How does it work? ++The heart of the echo cancellor is FIR filter. This is adapted to match the ++echo impulse response of the telephone line. It must be long enough to ++adequately cover the duration of that impulse response. The signal transmitted ++to the telephone line is passed through the FIR filter. Once the FIR is ++properly adapted, the resulting output is an estimate of the echo signal ++received from the line. This is subtracted from the received signal. The result ++is an estimate of the signal which originated at the far end of the line, free ++from echos of our own transmitted signal. ++ ++The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and ++was introduced in 1960. It is the commonest form of filter adaption used in ++things like modem line equalisers and line echo cancellers. There it works very ++well. However, it only works well for signals of constant amplitude. It works ++very poorly for things like speech echo cancellation, where the signal level ++varies widely. This is quite easy to fix. If the signal level is normalised - ++similar to applying AGC - LMS can work as well for a signal of varying ++amplitude as it does for a modem signal. This normalised least mean squares ++(NLMS) algorithm is the commonest one used for speech echo cancellation. Many ++other algorithms exist - e.g. RLS (essentially the same as Kalman filtering), ++FAP, etc. Some perform significantly better than NLMS. However, factors such ++as computational complexity and patents favour the use of NLMS. ++ ++A simple refinement to NLMS can improve its performance with speech. NLMS tends ++to adapt best to the strongest parts of a signal. If the signal is white noise, ++the NLMS algorithm works very well. However, speech has more low frequency than ++high frequency content. Pre-whitening (i.e. filtering the signal to flatten its ++spectrum) the echo signal improves the adapt rate for speech, and ensures the ++final residual signal is not heavily biased towards high frequencies. A very ++low complexity filter is adequate for this, so pre-whitening adds little to the ++compute requirements of the echo canceller. ++ ++An FIR filter adapted using pre-whitened NLMS performs well, provided certain ++conditions are met: ++ ++ - The transmitted signal has poor self-correlation. ++ - There is no signal being generated within the environment being ++ cancelled. ++ ++The difficulty is that neither of these can be guaranteed. ++ ++If the adaption is performed while transmitting noise (or something fairly ++noise like, such as voice) the adaption works very well. If the adaption is ++performed while transmitting something highly correlative (typically narrow ++band energy such as signalling tones or DTMF), the adaption can go seriously ++wrong. The reason is there is only one solution for the adaption on a near ++random signal - the impulse response of the line. For a repetitive signal, ++there are any number of solutions which converge the adaption, and nothing ++guides the adaption to choose the generalised one. Allowing an untrained ++canceller to converge on this kind of narrowband energy probably a good thing, ++since at least it cancels the tones. Allowing a well converged canceller to ++continue converging on such energy is just a way to ruin its generalised ++adaption. A narrowband detector is needed, so adapation can be suspended at ++appropriate times. ++ ++The adaption process is based on trying to eliminate the received signal. When ++there is any signal from within the environment being cancelled it may upset ++the adaption process. Similarly, if the signal we are transmitting is small, ++noise may dominate and disturb the adaption process. If we can ensure that the ++adaption is only performed when we are transmitting a significant signal level, ++and the environment is not, things will be OK. Clearly, it is easy to tell when ++we are sending a significant signal. Telling, if the environment is generating ++a significant signal, and doing it with sufficient speed that the adaption will ++not have diverged too much more we stop it, is a little harder. ++ ++The key problem in detecting when the environment is sourcing significant ++energy is that we must do this very quickly. Given a reasonably long sample of ++the received signal, there are a number of strategies which may be used to ++assess whether that signal contains a strong far end component. However, by the ++time that assessment is complete the far end signal will have already caused ++major mis-convergence in the adaption process. An assessment algorithm is ++needed which produces a fairly accurate result from a very short burst of far ++end energy. ++ ++\section echo_can_page_sec_3 How do I use it? ++The echo cancellor processes both the transmit and receive streams sample by ++sample. The processing function is not declared inline. Unfortunately, ++cancellation requires many operations per sample, so the call overhead is only ++a minor burden. ++*/ ++ ++#include "fir.h" ++#include "oslec.h" ++ ++/*! ++ G.168 echo canceller descriptor. This defines the working state for a line ++ echo canceller. ++*/ ++struct oslec_state { ++ int16_t tx, rx; ++ int16_t clean; ++ int16_t clean_nlp; ++ ++ int nonupdate_dwell; ++ int curr_pos; ++ int taps; ++ int log2taps; ++ int adaption_mode; ++ ++ int cond_met; ++ int32_t Pstates; ++ int16_t adapt; ++ int32_t factor; ++ int16_t shift; ++ ++ /* Average levels and averaging filter states */ ++ int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; ++ int Ltx, Lrx; ++ int Lclean; ++ int Lclean_bg; ++ int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; ++ ++ /* foreground and background filter states */ ++ fir16_state_t fir_state; ++ fir16_state_t fir_state_bg; ++ int16_t *fir_taps16[2]; ++ ++ /* DC blocking filter states */ ++ int tx_1, tx_2, rx_1, rx_2; ++ ++ /* optional High Pass Filter states */ ++ int32_t xvtx[5], yvtx[5]; ++ int32_t xvrx[5], yvrx[5]; ++ ++ /* Parameters for the optional Hoth noise generator */ ++ int cng_level; ++ int cng_rndnum; ++ int cng_filter; ++ ++ /* snapshot sample of coeffs used for development */ ++ int16_t *snapshot; ++}; ++ ++#endif /* __ECHO_H */ +הזמ הז םינוש `base/drivers/staging/echo/echo.o'-ו `new/drivers/staging/echo/echo.o' םיירניב םיצבק +diff -Nurp base/drivers/staging/echo/echo-user.c new/drivers/staging/echo/echo-user.c +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo-user.c 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,639 @@ ++/* ++ * SpanDSP - a series of DSP components for telephony ++ * ++ * echo.c - A line echo canceller. This code is being developed ++ * against and partially complies with G168. ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe ++ * ++ * Based on a bit from here, a bit from there, eye of toad, ear of ++ * bat, 15 years of failed attempts by David and a few fried brain ++ * cells. ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $ ++ */ ++ ++/*! \file */ ++ ++/* Implementation Notes ++ David Rowe ++ April 2007 ++ ++ This code started life as Steve's NLMS algorithm with a tap ++ rotation algorithm to handle divergence during double talk. I ++ added a Geigel Double Talk Detector (DTD) [2] and performed some ++ G168 tests. However I had trouble meeting the G168 requirements, ++ especially for double talk - there were always cases where my DTD ++ failed, for example where near end speech was under the 6dB ++ threshold required for declaring double talk. ++ ++ So I tried a two path algorithm [1], which has so far given better ++ results. The original tap rotation/Geigel algorithm is available ++ in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. ++ It's probably possible to make it work if some one wants to put some ++ serious work into it. ++ ++ At present no special treatment is provided for tones, which ++ generally cause NLMS algorithms to diverge. Initial runs of a ++ subset of the G168 tests for tones (e.g ./echo_test 6) show the ++ current algorithm is passing OK, which is kind of surprising. The ++ full set of tests needs to be performed to confirm this result. ++ ++ One other interesting change is that I have managed to get the NLMS ++ code to work with 16 bit coefficients, rather than the original 32 ++ bit coefficents. This reduces the MIPs and storage required. ++ I evaulated the 16 bit port using g168_tests.sh and listening tests ++ on 4 real-world samples. ++ ++ I also attempted the implementation of a block based NLMS update ++ [2] but although this passes g168_tests.sh it didn't converge well ++ on the real-world samples. I have no idea why, perhaps a scaling ++ problem. The block based code is also available in SVN ++ http://svn.rowetel.com/software/oslec/tags/before_16bit. If this ++ code can be debugged, it will lead to further reduction in MIPS, as ++ the block update code maps nicely onto DSP instruction sets (it's a ++ dot product) compared to the current sample-by-sample update. ++ ++ Steve also has some nice notes on echo cancellers in echo.h ++ ++ References: ++ ++ [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo ++ Path Models", IEEE Transactions on communications, COM-25, ++ No. 6, June ++ 1977. ++ http://www.rowetel.com/images/echo/dual_path_paper.pdf ++ ++ [2] The classic, very useful paper that tells you how to ++ actually build a real world echo canceller: ++ Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice ++ Echo Canceller with a TMS320020, ++ http://www.rowetel.com/images/echo/spra129.pdf ++ ++ [3] I have written a series of blog posts on this work, here is ++ Part 1: http://www.rowetel.com/blog/?p=18 ++ ++ [4] The source code http://svn.rowetel.com/software/oslec/ ++ ++ [5] A nice reference on LMS filters: ++ http://en.wikipedia.org/wiki/Least_mean_squares_filter ++ ++ Credits: ++ ++ Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan ++ Muthukrishnan for their suggestions and email discussions. Thanks ++ also to those people who collected echo samples for me such as ++ Mark, Pawel, and Pavel. ++*/ ++ ++#include /* We're doing kernel work */ ++#include ++#include ++#include ++ ++#include "bit_operations.h" ++#include "echo.h" ++ ++#define MIN_TX_POWER_FOR_ADAPTION 64 ++#define MIN_RX_POWER_FOR_ADAPTION 64 ++#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ ++#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ ++ ++/*-----------------------------------------------------------------------*\ ++ FUNCTIONS ++\*-----------------------------------------------------------------------*/ ++ ++/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ ++ ++#ifdef __bfin__ ++static void __inline__ lms_adapt_bg(struct oslec_state *ec, int clean, ++ int shift) ++{ ++ int i, j; ++ int offset1; ++ int offset2; ++ int factor; ++ int exp; ++ int16_t *phist; ++ int n; ++ ++ if (shift > 0) ++ factor = clean << shift; ++ else ++ factor = clean >> -shift; ++ ++ /* Update the FIR taps */ ++ ++ offset2 = ec->curr_pos; ++ offset1 = ec->taps - offset2; ++ phist = &ec->fir_state_bg.history[offset2]; ++ ++ /* st: and en: help us locate the assembler in echo.s */ ++ ++ //asm("st:"); ++ n = ec->taps; ++ for (i = 0, j = offset2; i < n; i++, j++) { ++ exp = *phist++ * factor; ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++ //asm("en:"); ++ ++ /* Note the asm for the inner loop above generated by Blackfin gcc ++ 4.1.1 is pretty good (note even parallel instructions used): ++ ++ R0 = W [P0++] (X); ++ R0 *= R2; ++ R0 = R0 + R3 (NS) || ++ R1 = W [P1] (X) || ++ nop; ++ R0 >>>= 15; ++ R0 = R0 + R1; ++ W [P1++] = R0; ++ ++ A block based update algorithm would be much faster but the ++ above can't be improved on much. Every instruction saved in ++ the loop above is 2 MIPs/ch! The for loop above is where the ++ Blackfin spends most of it's time - about 17 MIPs/ch measured ++ with speedtest.c with 256 taps (32ms). Write-back and ++ Write-through cache gave about the same performance. ++ */ ++} ++ ++/* ++ IDEAS for further optimisation of lms_adapt_bg(): ++ ++ 1/ The rounding is quite costly. Could we keep as 32 bit coeffs ++ then make filter pluck the MS 16-bits of the coeffs when filtering? ++ However this would lower potential optimisation of filter, as I ++ think the dual-MAC architecture requires packed 16 bit coeffs. ++ ++ 2/ Block based update would be more efficient, as per comments above, ++ could use dual MAC architecture. ++ ++ 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC ++ packing. ++ ++ 4/ Execute the whole e/c in a block of say 20ms rather than sample ++ by sample. Processing a few samples every ms is inefficient. ++*/ ++ ++#else ++static __inline__ void lms_adapt_bg(struct oslec_state *ec, int clean, ++ int shift) ++{ ++ int i; ++ ++ int offset1; ++ int offset2; ++ int factor; ++ int exp; ++ ++ if (shift > 0) ++ factor = clean << shift; ++ else ++ factor = clean >> -shift; ++ ++ /* Update the FIR taps */ ++ ++ offset2 = ec->curr_pos; ++ offset1 = ec->taps - offset2; ++ ++ for (i = ec->taps - 1; i >= offset1; i--) { ++ exp = (ec->fir_state_bg.history[i - offset1] * factor); ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++ for (; i >= 0; i--) { ++ exp = (ec->fir_state_bg.history[i + offset2] * factor); ++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); ++ } ++} ++#endif ++ ++struct oslec_state *oslec_create(int len, int adaption_mode) ++{ ++ struct oslec_state *ec; ++ int i; ++ ++ ec = kzalloc(sizeof(*ec), GFP_KERNEL); ++ if (!ec) ++ return NULL; ++ ++ ec->taps = len; ++ ec->log2taps = top_bit(len); ++ ec->curr_pos = ec->taps - 1; ++ ++ for (i = 0; i < 2; i++) { ++ ec->fir_taps16[i] = ++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->fir_taps16[i]) ++ goto error_oom; ++ } ++ ++ fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps); ++ fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps); ++ ++ for (i = 0; i < 5; i++) { ++ ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; ++ } ++ ++ ec->cng_level = 1000; ++ oslec_adaption_mode(ec, adaption_mode); ++ ++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->snapshot) ++ goto error_oom; ++ ++ ec->cond_met = 0; ++ ec->Pstates = 0; ++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; ++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; ++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; ++ ec->Lbgn = ec->Lbgn_acc = 0; ++ ec->Lbgn_upper = 200; ++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; ++ ++ return ec; ++ ++ error_oom: ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ ++ kfree(ec); ++ return NULL; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_create); ++ ++void oslec_free(struct oslec_state *ec) ++{ ++ int i; ++ ++ fir16_free(&ec->fir_state); ++ fir16_free(&ec->fir_state_bg); ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ kfree(ec->snapshot); ++ kfree(ec); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_free); ++ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) ++{ ++ ec->adaption_mode = adaption_mode; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_adaption_mode); ++ ++void oslec_flush(struct oslec_state *ec) ++{ ++ int i; ++ ++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; ++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; ++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; ++ ++ ec->Lbgn = ec->Lbgn_acc = 0; ++ ec->Lbgn_upper = 200; ++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; ++ ++ ec->nonupdate_dwell = 0; ++ ++ fir16_flush(&ec->fir_state); ++ fir16_flush(&ec->fir_state_bg); ++ ec->fir_state.curr_pos = ec->taps - 1; ++ ec->fir_state_bg.curr_pos = ec->taps - 1; ++ for (i = 0; i < 2; i++) ++ memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t)); ++ ++ ec->curr_pos = ec->taps - 1; ++ ec->Pstates = 0; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_flush); ++ ++void oslec_snapshot(struct oslec_state *ec) ++{ ++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_snapshot); ++ ++/* Dual Path Echo Canceller ------------------------------------------------*/ ++ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) ++{ ++ int32_t echo_value; ++ int clean_bg; ++ int tmp, tmp1; ++ ++ /* Input scaling was found be required to prevent problems when tx ++ starts clipping. Another possible way to handle this would be the ++ filter coefficent scaling. */ ++ ++ ec->tx = tx; ++ ec->rx = rx; ++ tx >>= 1; ++ rx >>= 1; ++ ++ /* ++ Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required ++ otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta) ++ only real axis. Some chip sets (like Si labs) don't need ++ this, but something like a $10 X100P card does. Any DC really slows ++ down convergence. ++ ++ Note: removes some low frequency from the signal, this reduces ++ the speech quality when listening to samples through headphones ++ but may not be obvious through a telephone handset. ++ ++ Note that the 3dB frequency in radians is approx Beta, e.g. for ++ Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. ++ */ ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { ++ tmp = rx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. This can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; ++ ++ /* hard limit filter to prevent clipping. Note that at this stage ++ rx should be limited to +/- 16383 due to right shift above */ ++ tmp1 = ec->rx_1 >> 15; ++ if (tmp1 > 16383) ++ tmp1 = 16383; ++ if (tmp1 < -16383) ++ tmp1 = -16383; ++ rx = tmp1; ++ ec->rx_2 = tmp; ++ } ++ ++ /* Block average of power in the filter states. Used for ++ adaption power calculation. */ ++ ++ { ++ int new, old; ++ ++ /* efficient "out with the old and in with the new" algorithm so ++ we don't have to recalculate over the whole block of ++ samples. */ ++ new = (int)tx *(int)tx; ++ old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * ++ (int)ec->fir_state.history[ec->fir_state.curr_pos]; ++ ec->Pstates += ++ ((new - old) + (1 << ec->log2taps)) >> ec->log2taps; ++ if (ec->Pstates < 0) ++ ec->Pstates = 0; ++ } ++ ++ /* Calculate short term average levels using simple single pole IIRs */ ++ ++ ec->Ltxacc += abs(tx) - ec->Ltx; ++ ec->Ltx = (ec->Ltxacc + (1 << 4)) >> 5; ++ ec->Lrxacc += abs(rx) - ec->Lrx; ++ ec->Lrx = (ec->Lrxacc + (1 << 4)) >> 5; ++ ++ /* Foreground filter --------------------------------------------------- */ ++ ++ ec->fir_state.coeffs = ec->fir_taps16[0]; ++ echo_value = fir16(&ec->fir_state, tx); ++ ec->clean = rx - echo_value; ++ ec->Lcleanacc += abs(ec->clean) - ec->Lclean; ++ ec->Lclean = (ec->Lcleanacc + (1 << 4)) >> 5; ++ ++ /* Background filter --------------------------------------------------- */ ++ ++ echo_value = fir16(&ec->fir_state_bg, tx); ++ clean_bg = rx - echo_value; ++ ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; ++ ec->Lclean_bg = (ec->Lclean_bgacc + (1 << 4)) >> 5; ++ ++ /* Background Filter adaption ----------------------------------------- */ ++ ++ /* Almost always adap bg filter, just simple DT and energy ++ detection to minimise adaption in cases of strong double talk. ++ However this is not critical for the dual path algorithm. ++ */ ++ ec->factor = 0; ++ ec->shift = 0; ++ if ((ec->nonupdate_dwell == 0)) { ++ int P, logP, shift; ++ ++ /* Determine: ++ ++ f = Beta * clean_bg_rx/P ------ (1) ++ ++ where P is the total power in the filter states. ++ ++ The Boffins have shown that if we obey (1) we converge ++ quickly and avoid instability. ++ ++ The correct factor f must be in Q30, as this is the fixed ++ point format required by the lms_adapt_bg() function, ++ therefore the scaled version of (1) is: ++ ++ (2^30) * f = (2^30) * Beta * clean_bg_rx/P ++ factor = (2^30) * Beta * clean_bg_rx/P ----- (2) ++ ++ We have chosen Beta = 0.25 by experiment, so: ++ ++ factor = (2^30) * (2^-2) * clean_bg_rx/P ++ ++ (30 - 2 - log2(P)) ++ factor = clean_bg_rx 2 ----- (3) ++ ++ To avoid a divide we approximate log2(P) as top_bit(P), ++ which returns the position of the highest non-zero bit in ++ P. This approximation introduces an error as large as a ++ factor of 2, but the algorithm seems to handle it OK. ++ ++ Come to think of it a divide may not be a big deal on a ++ modern DSP, so its probably worth checking out the cycles ++ for a divide versus a top_bit() implementation. ++ */ ++ ++ P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; ++ logP = top_bit(P) + ec->log2taps; ++ shift = 30 - 2 - logP; ++ ec->shift = shift; ++ ++ lms_adapt_bg(ec, clean_bg, shift); ++ } ++ ++ /* very simple DTD to make sure we dont try and adapt with strong ++ near end speech */ ++ ++ ec->adapt = 0; ++ if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) ++ ec->nonupdate_dwell = DTD_HANGOVER; ++ if (ec->nonupdate_dwell) ++ ec->nonupdate_dwell--; ++ ++ /* Transfer logic ------------------------------------------------------ */ ++ ++ /* These conditions are from the dual path paper [1], I messed with ++ them a bit to improve performance. */ ++ ++ if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && ++ (ec->nonupdate_dwell == 0) && ++ (8 * ec->Lclean_bg < ++ 7 * ec->Lclean) /* (ec->Lclean_bg < 0.875*ec->Lclean) */ && ++ (8 * ec->Lclean_bg < ++ ec->Ltx) /* (ec->Lclean_bg < 0.125*ec->Ltx) */ ) { ++ if (ec->cond_met == 6) { ++ /* BG filter has had better results for 6 consecutive samples */ ++ ec->adapt = 1; ++ memcpy(ec->fir_taps16[0], ec->fir_taps16[1], ++ ec->taps * sizeof(int16_t)); ++ } else ++ ec->cond_met++; ++ } else ++ ec->cond_met = 0; ++ ++ /* Non-Linear Processing --------------------------------------------------- */ ++ ++ ec->clean_nlp = ec->clean; ++ if (ec->adaption_mode & ECHO_CAN_USE_NLP) { ++ /* Non-linear processor - a fancy way to say "zap small signals, to avoid ++ residual echo due to (uLaw/ALaw) non-linearity in the channel.". */ ++ ++ if ((16 * ec->Lclean < ec->Ltx)) { ++ /* Our e/c has improved echo by at least 24 dB (each factor of 2 is 6dB, ++ so 2*2*2*2=16 is the same as 6+6+6+6=24dB) */ ++ if (ec->adaption_mode & ECHO_CAN_USE_CNG) { ++ ec->cng_level = ec->Lbgn; ++ ++ /* Very elementary comfort noise generation. Just random ++ numbers rolled off very vaguely Hoth-like. DR: This ++ noise doesn't sound quite right to me - I suspect there ++ are some overlfow issues in the filtering as it's too ++ "crackly". TODO: debug this, maybe just play noise at ++ high level or look at spectrum. ++ */ ++ ++ ec->cng_rndnum = ++ 1664525U * ec->cng_rndnum + 1013904223U; ++ ec->cng_filter = ++ ((ec->cng_rndnum & 0xFFFF) - 32768 + ++ 5 * ec->cng_filter) >> 3; ++ ec->clean_nlp = ++ (ec->cng_filter * ec->cng_level * 8) >> 14; ++ ++ } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) { ++ /* This sounds much better than CNG */ ++ if (ec->clean_nlp > ec->Lbgn) ++ ec->clean_nlp = ec->Lbgn; ++ if (ec->clean_nlp < -ec->Lbgn) ++ ec->clean_nlp = -ec->Lbgn; ++ } else { ++ /* just mute the residual, doesn't sound very good, used mainly ++ in G168 tests */ ++ ec->clean_nlp = 0; ++ } ++ } else { ++ /* Background noise estimator. I tried a few algorithms ++ here without much luck. This very simple one seems to ++ work best, we just average the level using a slow (1 sec ++ time const) filter if the current level is less than a ++ (experimentally derived) constant. This means we dont ++ include high level signals like near end speech. When ++ combined with CNG or especially CLIP seems to work OK. ++ */ ++ if (ec->Lclean < 40) { ++ ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; ++ ec->Lbgn = (ec->Lbgn_acc + (1 << 11)) >> 12; ++ } ++ } ++ } ++ ++ /* Roll around the taps buffer */ ++ if (ec->curr_pos <= 0) ++ ec->curr_pos = ec->taps; ++ ec->curr_pos--; ++ ++ if (ec->adaption_mode & ECHO_CAN_DISABLE) ++ ec->clean_nlp = rx; ++ ++ /* Output scaled back up again to match input scaling */ ++ ++ return (int16_t) ec->clean_nlp << 1; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_update); ++ ++/* This function is seperated from the echo canceller is it is usually called ++ as part of the tx process. See rx HP (DC blocking) filter above, it's ++ the same design. ++ ++ Some soft phones send speech signals with a lot of low frequency ++ energy, e.g. down to 20Hz. This can make the hybrid non-linear ++ which causes the echo canceller to fall over. This filter can help ++ by removing any low frequency before it gets to the tx port of the ++ hybrid. ++ ++ It can also help by removing and DC in the tx signal. DC is bad ++ for LMS algorithms. ++ ++ This is one of the classic DC removal filters, adjusted to provide sufficient ++ bass rolloff to meet the above requirement to protect hybrids from things that ++ upset them. The difference between successive samples produces a lousy HPF, and ++ then a suitably placed pole flattens things out. The final result is a nicely ++ rolled off bass end. The filtering is implemented with extended fractional ++ precision, which noise shapes things, giving very clean DC removal. ++*/ ++ ++int16_t oslec_hpf_tx(struct oslec_state * ec, int16_t tx) ++{ ++ int tmp, tmp1; ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { ++ tmp = tx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. The first can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2; ++ tmp1 = ec->tx_1 >> 15; ++ if (tmp1 > 32767) ++ tmp1 = 32767; ++ if (tmp1 < -32767) ++ tmp1 = -32767; ++ tx = tmp1; ++ ec->tx_2 = tmp; ++ } ++ ++ return tx; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_hpf_tx); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Rowe"); ++MODULE_DESCRIPTION("Open Source Line Echo Canceller"); ++MODULE_VERSION("0.3.0"); +הזמ הז םינוש `base/drivers/staging/echo/echo-user.o'-ו `new/drivers/staging/echo/echo-user.o' םיירניב םיצבק +diff -Nurp base/drivers/staging/echo/fir.h new/drivers/staging/echo/fir.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/fir.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,295 @@ ++/* ++ * SpanDSP - a series of DSP components for telephony ++ * ++ * fir.h - General telephony FIR routines ++ * ++ * Written by Steve Underwood ++ * ++ * Copyright (C) 2002 Steve Underwood ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * $Id: fir.h,v 1.8 2006/10/24 13:45:28 steveu Exp $ ++ */ ++ ++/*! \page fir_page FIR filtering ++\section fir_page_sec_1 What does it do? ++???. ++ ++\section fir_page_sec_2 How does it work? ++???. ++*/ ++ ++#if !defined(_FIR_H_) ++#define _FIR_H_ ++ ++/* ++ Blackfin NOTES & IDEAS: ++ ++ A simple dot product function is used to implement the filter. This performs ++ just one MAC/cycle which is inefficient but was easy to implement as a first ++ pass. The current Blackfin code also uses an unrolled form of the filter ++ history to avoid 0 length hardware loop issues. This is wasteful of ++ memory. ++ ++ Ideas for improvement: ++ ++ 1/ Rewrite filter for dual MAC inner loop. The issue here is handling ++ history sample offsets that are 16 bit aligned - the dual MAC needs ++ 32 bit aligmnent. There are some good examples in libbfdsp. ++ ++ 2/ Use the hardware circular buffer facility tohalve memory usage. ++ ++ 3/ Consider using internal memory. ++ ++ Using less memory might also improve speed as cache misses will be ++ reduced. A drop in MIPs and memory approaching 50% should be ++ possible. ++ ++ The foreground and background filters currenlty use a total of ++ about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo ++ can. ++*/ ++ ++#if defined(USE_MMX) || defined(USE_SSE2) ++#include "mmx.h" ++#endif ++ ++/*! ++ 16 bit integer FIR descriptor. This defines the working state for a single ++ instance of an FIR filter using 16 bit integer coefficients. ++*/ ++typedef struct { ++ int taps; ++ int curr_pos; ++ const int16_t *coeffs; ++ int16_t *history; ++} fir16_state_t; ++ ++/*! ++ 32 bit integer FIR descriptor. This defines the working state for a single ++ instance of an FIR filter using 32 bit integer coefficients, and filtering ++ 16 bit integer data. ++*/ ++typedef struct { ++ int taps; ++ int curr_pos; ++ const int32_t *coeffs; ++ int16_t *history; ++} fir32_state_t; ++ ++/*! ++ Floating point FIR descriptor. This defines the working state for a single ++ instance of an FIR filter using floating point coefficients and data. ++*/ ++typedef struct { ++ int taps; ++ int curr_pos; ++ const float *coeffs; ++ float *history; ++} fir_float_state_t; ++ ++static __inline__ const int16_t *fir16_create(fir16_state_t * fir, ++ const int16_t * coeffs, int taps) ++{ ++ fir->taps = taps; ++ fir->curr_pos = taps - 1; ++ fir->coeffs = coeffs; ++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) ++ fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL); ++#else ++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++#endif ++ return fir->history; ++} ++ ++static __inline__ void fir16_flush(fir16_state_t * fir) ++{ ++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) ++ memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t)); ++#else ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++#endif ++} ++ ++static __inline__ void fir16_free(fir16_state_t * fir) ++{ ++ kfree(fir->history); ++} ++ ++#ifdef __bfin__ ++static inline int32_t dot_asm(short *x, short *y, int len) ++{ ++ int dot; ++ ++ len--; ++ ++ __asm__("I0 = %1;\n\t" ++ "I1 = %2;\n\t" ++ "A0 = 0;\n\t" ++ "R0.L = W[I0++] || R1.L = W[I1++];\n\t" ++ "LOOP dot%= LC0 = %3;\n\t" ++ "LOOP_BEGIN dot%=;\n\t" ++ "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t" ++ "LOOP_END dot%=;\n\t" ++ "A0 += R0.L*R1.L (IS);\n\t" ++ "R0 = A0;\n\t" ++ "%0 = R0;\n\t" ++ :"=&d"(dot) ++ :"a"(x), "a"(y), "a"(len) ++ :"I0", "I1", "A1", "A0", "R0", "R1" ++ ); ++ ++ return dot; ++} ++#endif ++ ++static __inline__ int16_t fir16(fir16_state_t * fir, int16_t sample) ++{ ++ int32_t y; ++#if defined(USE_MMX) ++ int i; ++ mmx_t *mmx_coeffs; ++ mmx_t *mmx_hist; ++ ++ fir->history[fir->curr_pos] = sample; ++ fir->history[fir->curr_pos + fir->taps] = sample; ++ ++ mmx_coeffs = (mmx_t *) fir->coeffs; ++ mmx_hist = (mmx_t *) & fir->history[fir->curr_pos]; ++ i = fir->taps; ++ pxor_r2r(mm4, mm4); ++ /* 8 samples per iteration, so the filter must be a multiple of 8 long. */ ++ while (i > 0) { ++ movq_m2r(mmx_coeffs[0], mm0); ++ movq_m2r(mmx_coeffs[1], mm2); ++ movq_m2r(mmx_hist[0], mm1); ++ movq_m2r(mmx_hist[1], mm3); ++ mmx_coeffs += 2; ++ mmx_hist += 2; ++ pmaddwd_r2r(mm1, mm0); ++ pmaddwd_r2r(mm3, mm2); ++ paddd_r2r(mm0, mm4); ++ paddd_r2r(mm2, mm4); ++ i -= 8; ++ } ++ movq_r2r(mm4, mm0); ++ psrlq_i2r(32, mm0); ++ paddd_r2r(mm0, mm4); ++ movd_r2m(mm4, y); ++ emms(); ++#elif defined(USE_SSE2) ++ int i; ++ xmm_t *xmm_coeffs; ++ xmm_t *xmm_hist; ++ ++ fir->history[fir->curr_pos] = sample; ++ fir->history[fir->curr_pos + fir->taps] = sample; ++ ++ xmm_coeffs = (xmm_t *) fir->coeffs; ++ xmm_hist = (xmm_t *) & fir->history[fir->curr_pos]; ++ i = fir->taps; ++ pxor_r2r(xmm4, xmm4); ++ /* 16 samples per iteration, so the filter must be a multiple of 16 long. */ ++ while (i > 0) { ++ movdqu_m2r(xmm_coeffs[0], xmm0); ++ movdqu_m2r(xmm_coeffs[1], xmm2); ++ movdqu_m2r(xmm_hist[0], xmm1); ++ movdqu_m2r(xmm_hist[1], xmm3); ++ xmm_coeffs += 2; ++ xmm_hist += 2; ++ pmaddwd_r2r(xmm1, xmm0); ++ pmaddwd_r2r(xmm3, xmm2); ++ paddd_r2r(xmm0, xmm4); ++ paddd_r2r(xmm2, xmm4); ++ i -= 16; ++ } ++ movdqa_r2r(xmm4, xmm0); ++ psrldq_i2r(8, xmm0); ++ paddd_r2r(xmm0, xmm4); ++ movdqa_r2r(xmm4, xmm0); ++ psrldq_i2r(4, xmm0); ++ paddd_r2r(xmm0, xmm4); ++ movd_r2m(xmm4, y); ++#elif defined(__bfin__) ++ fir->history[fir->curr_pos] = sample; ++ fir->history[fir->curr_pos + fir->taps] = sample; ++ y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos], ++ fir->taps); ++#else ++ int i; ++ int offset1; ++ int offset2; ++ ++ fir->history[fir->curr_pos] = sample; ++ ++ offset2 = fir->curr_pos; ++ offset1 = fir->taps - offset2; ++ y = 0; ++ for (i = fir->taps - 1; i >= offset1; i--) ++ y += fir->coeffs[i] * fir->history[i - offset1]; ++ for (; i >= 0; i--) ++ y += fir->coeffs[i] * fir->history[i + offset2]; ++#endif ++ if (fir->curr_pos <= 0) ++ fir->curr_pos = fir->taps; ++ fir->curr_pos--; ++ return (int16_t) (y >> 15); ++} ++ ++static __inline__ const int16_t *fir32_create(fir32_state_t * fir, ++ const int32_t * coeffs, int taps) ++{ ++ fir->taps = taps; ++ fir->curr_pos = taps - 1; ++ fir->coeffs = coeffs; ++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++ return fir->history; ++} ++ ++static __inline__ void fir32_flush(fir32_state_t * fir) ++{ ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++} ++ ++static __inline__ void fir32_free(fir32_state_t * fir) ++{ ++ kfree(fir->history); ++} ++ ++static __inline__ int16_t fir32(fir32_state_t * fir, int16_t sample) ++{ ++ int i; ++ int32_t y; ++ int offset1; ++ int offset2; ++ ++ fir->history[fir->curr_pos] = sample; ++ offset2 = fir->curr_pos; ++ offset1 = fir->taps - offset2; ++ y = 0; ++ for (i = fir->taps - 1; i >= offset1; i--) ++ y += fir->coeffs[i] * fir->history[i - offset1]; ++ for (; i >= 0; i--) ++ y += fir->coeffs[i] * fir->history[i + offset2]; ++ if (fir->curr_pos <= 0) ++ fir->curr_pos = fir->taps; ++ fir->curr_pos--; ++ return (int16_t) (y >> 15); ++} ++ ++#endif ++/*- End of file ------------------------------------------------------------*/ +diff -Nurp base/drivers/staging/echo/Kconfig new/drivers/staging/echo/Kconfig +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/Kconfig 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,9 @@ ++config ECHO ++ tristate "Line Echo Canceller support" ++ default n ++ ---help--- ++ This driver provides line echo cancelling support for mISDN and ++ Zaptel drivers. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called echo. +diff -Nurp base/drivers/staging/echo/kernel_compat.h new/drivers/staging/echo/kernel_compat.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/kernel_compat.h 2008-10-30 18:26:22.000000000 +0200 +@@ -0,0 +1,17 @@ ++#include ++#include ++#include ++ ++#define kmalloc(a,b) malloc((a)) ++#define kzalloc(a,b) calloc(1,(a)) ++#define kcalloc(a,b,c) calloc((a),(b)) ++#define kfree(a) free((a)) ++#define GFP_KERNEL ++#define EXPORT_SYMBOL_GPL(a) ++#define MODULE_LICENSE(a) ++#define MODULE_AUTHOR(a) ++#define MODULE_DESCRIPTION(a) ++#define MODULE_VERSION(a) ++ ++/* FIXME: get rid of this typedef? */ ++typedef struct oslec_state echo_can_state_t; +diff -Nurp base/drivers/staging/echo/Makefile new/drivers/staging/echo/Makefile +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/Makefile 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1 @@ ++obj-$(CONFIG_ECHO) += echo.o +diff -Nurp base/drivers/staging/echo/mmx.h new/drivers/staging/echo/mmx.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/mmx.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * mmx.h ++ * Copyright (C) 1997-2001 H. Dietz and R. Fisher ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#ifndef AVCODEC_I386MMX_H ++#define AVCODEC_I386MMX_H ++ ++/* ++ * The type of an value that fits in an MMX register (note that long ++ * long constant values MUST be suffixed by LL and unsigned long long ++ * values by ULL, lest they be truncated by the compiler) ++ */ ++ ++typedef union { ++ long long q; /* Quadword (64-bit) value */ ++ unsigned long long uq; /* Unsigned Quadword */ ++ int d[2]; /* 2 Doubleword (32-bit) values */ ++ unsigned int ud[2]; /* 2 Unsigned Doubleword */ ++ short w[4]; /* 4 Word (16-bit) values */ ++ unsigned short uw[4]; /* 4 Unsigned Word */ ++ char b[8]; /* 8 Byte (8-bit) values */ ++ unsigned char ub[8]; /* 8 Unsigned Byte */ ++ float s[2]; /* Single-precision (32-bit) value */ ++} mmx_t; /* On an 8-byte (64-bit) boundary */ ++ ++/* SSE registers */ ++typedef union { ++ char b[16]; ++} xmm_t; ++ ++#define mmx_i2r(op,imm,reg) \ ++ __asm__ __volatile__ (#op " %0, %%" #reg \ ++ : /* nothing */ \ ++ : "i" (imm) ) ++ ++#define mmx_m2r(op,mem,reg) \ ++ __asm__ __volatile__ (#op " %0, %%" #reg \ ++ : /* nothing */ \ ++ : "m" (mem)) ++ ++#define mmx_r2m(op,reg,mem) \ ++ __asm__ __volatile__ (#op " %%" #reg ", %0" \ ++ : "=m" (mem) \ ++ : /* nothing */ ) ++ ++#define mmx_r2r(op,regs,regd) \ ++ __asm__ __volatile__ (#op " %" #regs ", %" #regd) ++ ++#define emms() __asm__ __volatile__ ("emms") ++ ++#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) ++#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) ++#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) ++ ++#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) ++#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) ++#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) ++ ++#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) ++#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) ++#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) ++#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) ++ ++#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) ++#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) ++ ++#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) ++#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) ++#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) ++#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) ++#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) ++#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) ++ ++#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) ++#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) ++#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) ++#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) ++ ++#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) ++#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) ++#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) ++#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) ++ ++#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) ++#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) ++ ++#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) ++#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) ++ ++#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) ++#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) ++#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) ++#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) ++#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) ++#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) ++ ++#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) ++#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) ++#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) ++#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) ++#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) ++#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) ++ ++#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) ++#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) ++ ++#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) ++#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) ++ ++#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) ++#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) ++ ++#define por_m2r(var,reg) mmx_m2r (por, var, reg) ++#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) ++ ++#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) ++#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) ++#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) ++#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) ++#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) ++#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) ++#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) ++#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) ++#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) ++ ++#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) ++#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) ++#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) ++#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) ++#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) ++#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) ++ ++#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) ++#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) ++#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) ++#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) ++#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) ++#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) ++#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) ++#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) ++#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) ++ ++#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) ++#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) ++#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) ++#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) ++#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) ++#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) ++ ++#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) ++#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) ++#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) ++#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) ++ ++#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) ++#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) ++#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) ++#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) ++ ++#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) ++#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) ++#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) ++#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) ++#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) ++#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) ++ ++#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) ++#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) ++#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) ++#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) ++#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) ++#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) ++ ++#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) ++#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) ++ ++/* 3DNOW extensions */ ++ ++#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) ++#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) ++ ++/* AMD MMX extensions - also available in intel SSE */ ++ ++#define mmx_m2ri(op,mem,reg,imm) \ ++ __asm__ __volatile__ (#op " %1, %0, %%" #reg \ ++ : /* nothing */ \ ++ : "m" (mem), "i" (imm)) ++#define mmx_r2ri(op,regs,regd,imm) \ ++ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ ++ : /* nothing */ \ ++ : "i" (imm) ) ++ ++#define mmx_fetch(mem,hint) \ ++ __asm__ __volatile__ ("prefetch" #hint " %0" \ ++ : /* nothing */ \ ++ : "m" (mem)) ++ ++#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) ++ ++#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) ++ ++#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) ++#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) ++#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) ++#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) ++ ++#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) ++ ++#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) ++ ++#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) ++#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) ++ ++#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) ++#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) ++ ++#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) ++#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) ++ ++#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) ++#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) ++ ++#define pmovmskb(mmreg,reg) \ ++ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) ++ ++#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) ++#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) ++ ++#define prefetcht0(mem) mmx_fetch (mem, t0) ++#define prefetcht1(mem) mmx_fetch (mem, t1) ++#define prefetcht2(mem) mmx_fetch (mem, t2) ++#define prefetchnta(mem) mmx_fetch (mem, nta) ++ ++#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) ++#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) ++ ++#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) ++#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) ++ ++#define sfence() __asm__ __volatile__ ("sfence\n\t") ++ ++/* SSE2 */ ++#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm) ++#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm) ++#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm) ++#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm) ++ ++#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm) ++ ++#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg) ++#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var) ++#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd) ++#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg) ++#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var) ++#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd) ++ ++#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var) ++ ++#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg) ++#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg) ++ ++#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd) ++#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd) ++ ++#endif /* AVCODEC_I386MMX_H */ +diff -Nurp base/drivers/staging/echo/oslec.h new/drivers/staging/echo/oslec.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/oslec.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * OSLEC - A line echo canceller. This code is being developed ++ * against and partially complies with G168. Using code from SpanDSP ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001 Steve Underwood and 2007-2008 David Rowe ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef __OSLEC_H ++#define __OSLEC_H ++ ++/* TODO: document interface */ ++ ++/* Mask bits for the adaption mode */ ++#define ECHO_CAN_USE_ADAPTION 0x01 ++#define ECHO_CAN_USE_NLP 0x02 ++#define ECHO_CAN_USE_CNG 0x04 ++#define ECHO_CAN_USE_CLIP 0x08 ++#define ECHO_CAN_USE_TX_HPF 0x10 ++#define ECHO_CAN_USE_RX_HPF 0x20 ++#define ECHO_CAN_DISABLE 0x40 ++ ++/*! ++ G.168 echo canceller descriptor. This defines the working state for a line ++ echo canceller. ++*/ ++struct oslec_state; ++ ++/*! Create a voice echo canceller context. ++ \param len The length of the canceller, in samples. ++ \return The new canceller context, or NULL if the canceller could not be created. ++*/ ++struct oslec_state *oslec_create(int len, int adaption_mode); ++ ++/*! Free a voice echo canceller context. ++ \param ec The echo canceller context. ++*/ ++void oslec_free(struct oslec_state *ec); ++ ++/*! Flush (reinitialise) a voice echo canceller context. ++ \param ec The echo canceller context. ++*/ ++void oslec_flush(struct oslec_state *ec); ++ ++/*! Set the adaption mode of a voice echo canceller context. ++ \param ec The echo canceller context. ++ \param adapt The mode. ++*/ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode); ++ ++void oslec_snapshot(struct oslec_state *ec); ++ ++/*! Process a sample through a voice echo canceller. ++ \param ec The echo canceller context. ++ \param tx The transmitted audio sample. ++ \param rx The received audio sample. ++ \return The clean (echo cancelled) received sample. ++*/ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx); ++ ++/*! Process to high pass filter the tx signal. ++ \param ec The echo canceller context. ++ \param tx The transmitted auio sample. ++ \return The HP filtered transmit sample, send this to your D/A. ++*/ ++int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx); ++ ++#endif /* __OSLEC_H */ +diff -Nurp base/drivers/staging/echo/out.txt new/drivers/staging/echo/out.txt +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/out.txt 2008-10-30 18:26:45.000000000 +0200 +@@ -0,0 +1,8000 @@ ++210 ++98 ++194 ++198 ++226 ++48 ++82 ++192 ++68 ++138 ++118 ++156 ++90 ++128 ++238 ++228 ++-36 ++-6 ++-68 ++40 ++-46 ++8 ++2 ++76 ++-34 ++12 ++-22 ++-8 ++100 ++-38 ++6 ++48 ++-12 ++-36 ++16 ++-10 ++-6 ++46 ++-62 ++28 ++-14 ++20 ++-34 ++38 ++-48 ++-8 ++52 ++38 ++-72 ++54 ++-12 ++-54 ++-8 ++44 ++50 ++-34 ++-58 ++-14 ++36 ++-12 ++2 ++78 ++26 ++8 ++-64 ++-6 ++-8 ++30 ++-10 ++4 ++-8 ++-54 ++24 ++56 ++16 ++-16 ++-56 ++30 ++24 ++-12 ++22 ++-36 ++6 ++64 ++26 ++-28 ++-34 ++54 ++-14 ++18 ++50 ++-6 ++10 ++26 ++-20 ++40 ++-28 ++24 ++10 ++26 ++-18 ++-40 ++54 ++42 ++-58 ++42 ++8 ++-12 ++16 ++-22 ++40 ++-16 ++-4 ++-12 ++-16 ++10 ++34 ++2 ++-12 ++58 ++-36 ++-22 ++30 ++42 ++58 ++32 ++-4 ++-24 ++26 ++-16 ++-20 ++-16 ++28 ++-12 ++-16 ++50 ++-28 ++52 ++-24 ++46 ++-26 ++68 ++-38 ++20 ++-14 ++-4 ++54 ++28 ++10 ++38 ++-6 ++20 ++-52 ++-30 ++28 ++-26 ++-22 ++10 ++24 ++64 ++12 ++24 ++-46 ++-16 ++92 ++-28 ++56 ++-38 ++60 ++-46 ++44 ++-44 ++-36 ++70 ++22 ++-34 ++-12 ++10 ++68 ++36 ++-26 ++12 ++12 ++6 ++26 ++-50 ++18 ++40 ++-18 ++60 ++-2 ++30 ++14 ++-4 ++-56 ++44 ++34 ++36 ++14 ++14 ++26 ++0 ++20 ++20 ++-16 ++-22 ++-38 ++40 ++36 ++-62 ++16 ++-20 ++26 ++-12 ++32 ++-10 ++-4 ++-14 ++-14 ++38 ++-8 ++34 ++12 ++50 ++-26 ++10 ++26 ++-38 ++10 ++26 ++52 ++-10 ++-18 ++44 ++-8 ++-46 ++10 ++34 ++46 ++-16 ++26 ++10 ++4 ++-6 ++36 ++-32 ++32 ++-38 ++20 ++34 ++24 ++12 ++-16 ++-26 ++36 ++28 ++26 ++-22 ++26 ++-22 ++56 ++14 ++4 ++-26 ++14 ++-30 ++2 ++64 ++18 ++-44 ++-24 ++22 ++36 ++24 ++24 ++-20 ++40 ++-12 ++0 ++-20 ++64 ++-48 ++48 ++-10 ++-24 ++18 ++60 ++34 ++8 ++6 ++-38 ++28 ++-6 ++32 ++40 ++20 ++20 ++-48 ++28 ++0 ++-30 ++48 ++36 ++24 ++-16 ++0 ++-42 ++-14 ++52 ++4 ++-10 ++46 ++-6 ++-22 ++-24 ++34 ++36 ++-40 ++26 ++6 ++42 ++22 ++-46 ++-20 ++24 ++-20 ++-12 ++16 ++12 ++70 ++44 ++-2 ++24 ++20 ++-10 ++-18 ++0 ++32 ++14 ++-28 ++-28 ++-24 ++-6 ++52 ++2 ++-6 ++8 ++-12 ++28 ++72 ++8 ++-22 ++28 ++36 ++-4 ++-36 ++50 ++10 ++-2 ++-28 ++16 ++4 ++44 ++-12 ++-22 ++2 ++50 ++22 ++48 ++-30 ++44 ++-38 ++4 ++-34 ++48 ++34 ++-26 ++-32 ++62 ++24 ++-48 ++6 ++62 ++20 ++-30 ++2 ++34 ++-48 ++-34 ++-4 ++-18 ++18 ++20 ++-38 ++-38 ++-24 ++16 ++38 ++10 ++-12 ++-38 ++12 ++14 ++-10 ++-6 ++-20 ++-16 ++36 ++-18 ++-36 ++8 ++-8 ++4 ++8 ++-34 ++26 ++-48 ++-30 ++50 ++-40 ++-2 ++-16 ++26 ++-16 ++-20 ++28 ++-26 ++16 ++6 ++-58 ++-12 ++30 ++-2 ++14 ++-34 ++-26 ++-22 ++8 ++-30 ++18 ++14 ++8 ++-32 ++-10 ++-20 ++-2 ++-18 ++0 ++2 ++-24 ++10 ++-36 ++-6 ++0 ++-12 ++26 ++-26 ++12 ++8 ++26 ++6 ++0 ++-12 ++-30 ++4 ++-46 ++-2 ++32 ++-6 ++-4 ++-12 ++-18 ++-16 ++12 ++12 ++4 ++-42 ++-42 ++-26 ++30 ++-26 ++8 ++12 ++-6 ++-40 ++-36 ++-8 ++-8 ++22 ++32 ++-38 ++-16 ++20 ++26 ++-4 ++-34 ++24 ++-36 ++-36 ++12 ++28 ++0 ++18 ++-48 ++-16 ++20 ++24 ++-12 ++2 ++16 ++-38 ++20 ++-14 ++8 ++-22 ++-20 ++20 ++4 ++-6 ++-20 ++-18 ++20 ++8 ++-46 ++12 ++-20 ++-8 ++0 ++-6 ++0 ++-48 ++-10 ++-12 ++-42 ++24 ++20 ++12 ++-8 ++-34 ++-4 ++-6 ++-6 ++-12 ++-50 ++6 ++-38 ++12 ++-2 ++14 ++-12 ++-44 ++0 ++4 ++-42 ++30 ++-22 ++12 ++-4 ++-40 ++-4 ++-20 ++-6 ++-34 ++22 ++10 ++20 ++-4 ++-22 ++-12 ++36 ++-54 ++-36 ++20 ++6 ++-24 ++-24 ++2 ++38 ++4 ++-20 ++-20 ++20 ++24 ++14 ++2 ++-16 ++-40 ++-16 ++-40 ++-10 ++30 ++-24 ++18 ++16 ++-58 ++-14 ++12 ++4 ++14 ++-42 ++8 ++26 ++-44 ++-22 ++-6 ++-16 ++0 ++-44 ++-6 ++-12 ++8 ++24 ++-46 ++-2 ++26 ++-52 ++-8 ++-28 ++10 ++10 ++2 ++0 ++-50 ++0 ++10 ++-30 ++-40 ++-8 ++-30 ++56 ++6 ++10 ++20 ++16 ++-52 ++-14 ++-16 ++22 ++0 ++14 ++-54 ++-28 ++24 ++-28 ++20 ++6 ++-24 ++-24 ++28 ++-46 ++28 ++-10 ++-6 ++22 ++-52 ++-34 ++-26 ++-30 ++22 ++-30 ++-34 ++-6 ++48 ++-46 ++-26 ++-22 ++12 ++26 ++36 ++-48 ++6 ++-38 ++-8 ++6 ++-8 ++-18 ++-12 ++36 ++2 ++-38 ++-38 ++-2 ++40 ++18 ++-24 ++36 ++-54 ++2 ++-24 ++8 ++12 ++-28 ++24 ++6 ++-36 ++-38 ++14 ++18 ++8 ++0 ++14 ++-36 ++6 ++-34 ++8 ++-40 ++2 ++-42 ++-30 ++-18 ++-6 ++-24 ++20 ++-30 ++48 ++38 ++-50 ++36 ++-18 ++-34 ++14 ++-36 ++10 ++-2 ++10 ++22 ++-30 ++-14 ++10 ++-40 ++-16 ++12 ++-4 ++-44 ++42 ++-14 ++-32 ++6 ++-12 ++-16 ++22 ++4 ++-12 ++-24 ++8 ++-14 ++-30 ++20 ++-32 ++16 ++-46 ++30 ++26 ++-14 ++-28 ++10 ++-16 ++28 ++-40 ++-24 ++-32 ++10 ++38 ++-4 ++-14 ++18 ++22 ++4 ++-36 ++-30 ++-26 ++-18 ++-16 ++30 ++6 ++40 ++32 ++10 ++14 ++-10 ++6 ++22 ++52 ++-6 ++-24 ++2 ++16 ++16 ++-26 ++36 ++30 ++-22 ++10 ++26 ++16 ++-32 ++8 ++24 ++22 ++26 ++-28 ++12 ++36 ++-34 ++-8 ++-12 ++28 ++-16 ++22 ++-10 ++-14 ++36 ++24 ++-30 ++34 ++24 ++-18 ++22 ++-4 ++-2 ++16 ++-20 ++10 ++-24 ++-26 ++16 ++-12 ++18 ++-14 ++40 ++22 ++-14 ++30 ++16 ++-12 ++40 ++10 ++-24 ++-30 ++14 ++4 ++-16 ++10 ++-10 ++4 ++14 ++-20 ++26 ++-20 ++14 ++-4 ++34 ++34 ++22 ++28 ++20 ++-40 ++-20 ++8 ++-10 ++36 ++-28 ++-4 ++22 ++14 ++18 ++0 ++-2 ++36 ++8 ++30 ++-24 ++20 ++-10 ++-8 ++-20 ++28 ++-8 ++20 ++30 ++18 ++6 ++8 ++0 ++0 ++6 ++-2 ++2 ++8 ++-36 ++20 ++8 ++-24 ++-16 ++-8 ++36 ++34 ++14 ++-20 ++14 ++-4 ++-20 ++36 ++-22 ++10 ++-8 ++-18 ++6 ++24 ++32 ++-24 ++2 ++32 ++36 ++-26 ++-12 ++10 ++18 ++30 ++-12 ++10 ++12 ++24 ++16 ++4 ++-44 ++12 ++18 ++20 ++14 ++-22 ++-28 ++34 ++-8 ++-16 ++0 ++2 ++6 ++22 ++-24 ++6 ++32 ++8 ++-28 ++30 ++22 ++-4 ++-2 ++-6 ++-8 ++16 ++-24 ++42 ++0 ++24 ++20 ++-12 ++-2 ++-6 ++-14 ++-2 ++18 ++2 ++-14 ++32 ++8 ++8 ++-16 ++34 ++-16 ++-4 ++-8 ++-18 ++46 ++-8 ++26 ++14 ++12 ++-16 ++-22 ++46 ++26 ++-28 ++32 ++-22 ++44 ++6 ++4 ++-2 ++20 ++24 ++12 ++-14 ++-24 ++28 ++-14 ++14 ++0 ++-10 ++14 ++0 ++18 ++12 ++-8 ++6 ++-28 ++-2 ++-2 ++26 ++2 ++-10 ++18 ++-8 ++-2 ++14 ++2 ++-4 ++-20 ++-8 ++48 ++34 ++32 ++-6 ++-16 ++-8 ++4 ++4 ++26 ++14 ++18 ++-2 ++-22 ++4 ++-20 ++18 ++38 ++-28 ++34 ++-14 ++28 ++-8 ++-10 ++6 ++16 ++22 ++36 ++18 ++-32 ++-38 ++-22 ++36 ++38 ++-36 ++2 ++-6 ++-4 ++32 ++6 ++-26 ++14 ++10 ++12 ++20 ++26 ++-2 ++-20 ++28 ++24 ++-28 ++28 ++-2 ++-8 ++0 ++32 ++24 ++-36 ++22 ++34 ++-26 ++32 ++22 ++-36 ++28 ++32 ++-10 ++-40 ++-18 ++0 ++26 ++-12 ++26 ++-28 ++14 ++4 ++36 ++-14 ++10 ++14 ++32 ++2 ++12 ++-10 ++-30 ++36 ++-14 ++28 ++-24 ++-10 ++32 ++-18 ++-14 ++24 ++0 ++-18 ++38 ++30 ++-28 ++34 ++-24 ++26 ++-16 ++36 ++16 ++16 ++-32 ++2 ++-22 ++18 ++-20 ++-22 ++6 ++20 ++6 ++-6 ++18 ++14 ++-8 ++28 ++-24 ++-12 ++-16 ++2 ++22 ++8 ++-20 ++12 ++-10 ++-6 ++12 ++2 ++-28 ++12 ++6 ++8 ++8 ++14 ++-22 ++8 ++8 ++-2 ++8 ++-12 ++-10 ++-12 ++6 ++-10 ++-14 ++-24 ++-6 ++6 ++-10 ++12 ++22 ++-14 ++-16 ++-2 ++24 ++12 ++2 ++-12 ++-6 ++4 ++34 ++10 ++-26 ++-4 ++16 ++0 ++-22 ++2 ++24 ++20 ++6 ++-14 ++-14 ++-12 ++10 ++-6 ++0 ++8 ++32 ++-8 ++28 ++2 ++-6 ++20 ++2 ++-22 ++0 ++-10 ++4 ++2 ++6 ++-6 ++2 ++-28 ++28 ++-14 ++-4 ++-22 ++20 ++8 ++24 ++-6 ++2 ++-20 ++10 ++-10 ++6 ++-28 ++18 ++-4 ++0 ++16 ++-24 ++-26 ++18 ++22 ++0 ++-14 ++-12 ++-10 ++-18 ++-10 ++18 ++4 ++-12 ++18 ++30 ++10 ++14 ++0 ++-18 ++10 ++-20 ++-16 ++18 ++18 ++-18 ++-8 ++16 ++-18 ++22 ++-10 ++22 ++20 ++-10 ++10 ++20 ++-30 ++-26 ++-12 ++-12 ++-14 ++-6 ++-16 ++16 ++8 ++14 ++8 ++-12 ++2 ++2 ++8 ++-12 ++-4 ++10 ++26 ++-10 ++22 ++-22 ++-18 ++-20 ++-14 ++12 ++-16 ++-16 ++-6 ++28 ++36 ++-14 ++16 ++-14 ++14 ++26 ++-2 ++-2 ++0 ++18 ++-14 ++-34 ++-18 ++14 ++-6 ++8 ++20 ++20 ++-6 ++12 ++-20 ++-10 ++28 ++-8 ++2 ++-22 ++26 ++4 ++-28 ++-16 ++-4 ++-6 ++0 ++-4 ++-8 ++32 ++24 ++6 ++-6 ++-8 ++8 ++14 ++-14 ++14 ++8 ++6 ++-8 ++-18 ++-2 ++10 ++-26 ++6 ++-14 ++-30 ++-22 ++14 ++-18 ++22 ++-4 ++-16 ++-14 ++6 ++-8 ++-8 ++8 ++6 ++-8 ++4 ++-20 ++12 ++-30 ++4 ++2 ++-8 ++-2 ++-20 ++-22 ++28 ++-4 ++18 ++8 ++-4 ++-4 ++26 ++-8 ++-6 ++8 ++-4 ++-4 ++-36 ++4 ++-10 ++-2 ++-30 ++14 ++-6 ++2 ++-24 ++-24 ++14 ++18 ++-24 ++-2 ++-12 ++-4 ++-14 ++-4 ++6 ++24 ++4 ++-18 ++2 ++-16 ++0 ++8 ++12 ++-28 ++0 ++-2 ++-2 ++-8 ++-16 ++16 ++-22 ++-10 ++12 ++-4 ++12 ++10 ++-14 ++6 ++-6 ++-6 ++-22 ++14 ++14 ++-24 ++-16 ++-8 ++-22 ++-18 ++10 ++14 ++4 ++-12 ++-2 ++2 ++-4 ++14 ++-28 ++-28 ++4 ++-4 ++22 ++-10 ++-8 ++6 ++14 ++-8 ++-6 ++-12 ++-28 ++-16 ++-20 ++-6 ++-12 ++-24 ++-4 ++-2 ++6 ++8 ++6 ++-12 ++2 ++-12 ++20 ++-28 ++14 ++-8 ++-28 ++10 ++-8 ++-4 ++-20 ++-10 ++30 ++8 ++-26 ++22 ++-22 ++-2 ++8 ++-12 ++6 ++4 ++-6 ++12 ++-2 ++-24 ++-18 ++-22 ++10 ++10 ++-16 ++-16 ++16 ++-14 ++-16 ++12 ++-4 ++-6 ++0 ++18 ++14 ++-4 ++-24 ++4 ++-10 ++-22 ++16 ++0 ++8 ++-22 ++-36 ++8 ++12 ++2 ++-12 ++-10 ++4 ++-4 ++-8 ++-12 ++-16 ++16 ++4 ++-18 ++-18 ++14 ++14 ++2 ++-22 ++-10 ++4 ++-30 ++-16 ++-22 ++12 ++10 ++-18 ++0 ++-12 ++-4 ++18 ++-12 ++12 ++10 ++10 ++-18 ++-22 ++0 ++8 ++-6 ++-22 ++-26 ++6 ++6 ++-20 ++8 ++6 ++-20 ++8 ++-18 ++6 ++-4 ++-22 ++16 ++-2 ++12 ++-8 ++-2 ++-4 ++4 ++-24 ++-8 ++-22 ++-24 ++0 ++-6 ++0 ++8 ++22 ++-18 ++30 ++20 ++-18 ++8 ++8 ++-8 ++-2 ++6 ++-6 ++14 ++-4 ++-18 ++-6 ++6 ++18 ++10 ++-4 ++-12 ++10 ++-12 ++-12 ++6 ++-2 ++-16 ++14 ++16 ++-14 ++-22 ++-16 ++-12 ++-14 ++-8 ++16 ++-16 ++6 ++-2 ++-4 ++16 ++-14 ++12 ++18 ++0 ++18 ++-10 ++-16 ++6 ++-22 ++-14 ++-12 ++10 ++-16 ++-4 ++8 ++14 ++4 ++-2 ++14 ++14 ++-6 ++-22 ++14 ++2 ++-24 ++2 ++10 ++6 ++-10 ++-22 ++6 ++-6 ++-22 ++10 ++-14 ++-18 ++-16 ++14 ++10 ++-14 ++12 ++-14 ++-14 ++-12 ++8 ++12 ++-20 ++-12 ++-10 ++14 ++-8 ++8 ++6 ++-20 ++2 ++-8 ++12 ++-2 ++-26 ++-8 ++6 ++10 ++-12 ++6 ++4 ++-8 ++22 ++4 ++-12 ++18 ++-30 ++-12 ++-18 ++-12 ++-8 ++8 ++-20 ++0 ++-8 ++-12 ++0 ++2 ++-8 ++2 ++2 ++10 ++2 ++-2 ++-14 ++0 ++-18 ++-18 ++-6 ++-16 ++22 ++-16 ++4 ++18 ++6 ++16 ++12 ++6 ++-10 ++-18 ++-16 ++10 ++-24 ++-16 ++-14 ++-10 ++-4 ++14 ++12 ++4 ++-14 ++-4 ++10 ++14 ++18 ++14 ++-10 ++18 ++12 ++8 ++-26 ++2 ++12 ++-4 ++2 ++-6 ++2 ++0 ++0 ++8 ++-2 ++6 ++0 ++6 ++6 ++4 ++-22 ++-20 ++0 ++10 ++-22 ++-2 ++-22 ++-6 ++-6 ++-18 ++-2 ++2 ++-22 ++-6 ++2 ++-12 ++-10 ++-2 ++12 ++6 ++-14 ++4 ++0 ++-2 ++6 ++-12 ++-6 ++0 ++-8 ++-4 ++-16 ++-8 ++6 ++10 ++-18 ++2 ++2 ++-10 ++-10 ++16 ++-2 ++4 ++4 ++-2 ++8 ++-12 ++2 ++8 ++-6 ++-4 ++4 ++2 ++-16 ++-10 ++-2 ++6 ++0 ++10 ++-16 ++0 ++-10 ++-6 ++20 ++4 ++-12 ++-16 ++-6 ++4 ++-10 ++-6 ++-10 ++6 ++-16 ++-12 ++-6 ++10 ++-10 ++12 ++10 ++4 ++-6 ++-4 ++-8 ++-2 ++10 ++8 ++-4 ++-8 ++6 ++0 ++-22 ++10 ++8 ++12 ++2 ++6 ++-26 ++-16 ++0 ++-6 ++0 ++10 ++-16 ++-2 ++10 ++-10 ++-6 ++-20 ++-16 ++-4 ++12 ++-4 ++2 ++-12 ++-18 ++4 ++16 ++-2 ++10 ++6 ++-16 ++10 ++6 ++12 ++0 ++-8 ++10 ++8 ++-4 ++-12 ++-22 ++12 ++-10 ++-18 ++10 ++-14 ++-6 ++18 ++-10 ++-6 ++-16 ++-12 ++-12 ++16 ++-12 ++-14 ++-10 ++-16 ++14 ++-14 ++16 ++6 ++6 ++-2 ++6 ++0 ++12 ++-6 ++-10 ++-2 ++6 ++6 ++-8 ++-18 ++0 ++16 ++-14 ++-10 ++14 ++-6 ++4 ++-20 ++-2 ++6 ++16 ++2 ++2 ++-18 ++4 ++2 ++-20 ++0 ++-10 ++-20 ++-10 ++10 ++6 ++-12 ++-12 ++-16 ++12 ++-14 ++2 ++20 ++-14 ++-10 ++24 ++-10 ++-12 ++14 ++0 ++6 ++16 ++-14 ++-10 ++12 ++2 ++-12 ++12 ++-4 ++10 ++-12 ++-16 ++12 ++-16 ++-6 ++12 ++6 ++6 ++-18 ++12 ++-6 ++-18 ++-4 ++-8 ++-12 ++-2 ++-12 ++-6 ++4 ++-18 ++-8 ++6 ++-14 ++4 ++20 ++18 ++-12 ++-16 ++16 ++0 ++2 ++-10 ++8 ++8 ++-2 ++-14 ++0 ++-10 ++12 ++0 ++-14 ++-10 ++12 ++0 ++-8 ++-8 ++-14 ++4 ++6 ++8 ++2 ++0 ++-10 ++4 ++-14 ++12 ++-2 ++-20 ++-14 ++-2 ++10 ++6 ++-8 ++-8 ++2 ++10 ++4 ++-14 ++-14 ++6 ++12 ++-12 ++6 ++4 ++2 ++-10 ++6 ++2 ++0 ++-8 ++-6 ++-2 ++-14 ++2 ++10 ++-10 ++-8 ++-2 ++-8 ++4 ++-16 ++-14 ++-4 ++4 ++8 ++12 ++-10 ++-14 ++-16 ++0 ++16 ++12 ++-6 ++0 ++2 ++-12 ++8 ++-16 ++10 ++-6 ++-2 ++-4 ++8 ++2 ++2 ++0 ++12 ++4 ++-12 ++-10 ++-18 ++-8 ++-8 ++0 ++6 ++12 ++-4 ++-12 ++-18 ++6 ++2 ++-18 ++-2 ++-14 ++14 ++-4 ++-14 ++-8 ++-6 ++-10 ++-8 ++-16 ++-4 ++12 ++16 ++-10 ++-2 ++10 ++12 ++6 ++-18 ++8 ++-10 ++0 ++-4 ++6 ++-6 ++14 ++-16 ++4 ++-2 ++-2 ++0 ++-14 ++0 ++6 ++-6 ++4 ++-16 ++2 ++10 ++-12 ++8 ++-16 ++4 ++8 ++-6 ++-8 ++-6 ++-8 ++-6 ++4 ++-12 ++-10 ++-14 ++2 ++-10 ++6 ++4 ++-6 ++-14 ++-6 ++8 ++12 ++0 ++-8 ++-6 ++0 ++-14 ++-2 ++-4 ++10 ++4 ++-6 ++-8 ++4 ++8 ++0 ++8 ++-8 ++14 ++-8 ++14 ++14 ++-10 ++-14 ++4 ++2 ++6 ++-12 ++2 ++6 ++-8 ++-2 ++4 ++2 ++2 ++-6 ++-10 ++2 ++6 ++0 ++0 ++-12 ++2 ++2 ++6 ++-8 ++-18 ++6 ++8 ++6 ++-14 ++2 ++6 ++-2 ++4 ++-10 ++-14 ++0 ++4 ++4 ++-2 ++-18 ++-10 ++-12 ++6 ++10 ++-10 ++10 ++0 ++10 ++-4 ++-6 ++-8 ++-14 ++4 ++-10 ++-4 ++4 ++-20 ++-10 ++2 ++-12 ++10 ++8 ++2 ++6 ++-12 ++0 ++-2 ++4 ++-6 ++4 ++6 ++0 ++12 ++-2 ++-6 ++-10 ++-2 ++-8 ++-8 ++-14 ++-2 ++-2 ++-4 ++-8 ++4 ++2 ++-6 ++14 ++10 ++14 ++6 ++4 ++-6 ++-2 ++-2 ++-2 ++-18 ++0 ++2 ++-4 ++0 ++-12 ++-18 ++0 ++8 ++0 ++8 ++-8 ++6 ++-8 ++-6 ++-6 ++2 ++2 ++-2 ++-4 ++-12 ++0 ++-8 ++-18 ++-2 ++-8 ++6 ++-4 ++6 ++-6 ++8 ++14 ++-8 ++-8 ++-2 ++-12 ++4 ++0 ++-2 ++-6 ++-14 ++2 ++10 ++4 ++4 ++-8 ++-12 ++-2 ++12 ++-4 ++8 ++-14 ++-6 ++-6 ++-14 ++-10 ++-6 ++-14 ++0 ++0 ++-2 ++0 ++-6 ++2 ++8 ++8 ++8 ++2 ++-4 ++-6 ++8 ++-6 ++6 ++6 ++-10 ++2 ++-12 ++0 ++-18 ++-16 ++-8 ++14 ++-6 ++0 ++-10 ++-8 ++6 ++8 ++-4 ++-12 ++-12 ++8 ++2 ++-2 ++-2 ++-12 ++-4 ++-10 ++0 ++10 ++0 ++-2 ++-6 ++4 ++-2 ++10 ++-10 ++-2 ++-10 ++0 ++4 ++-4 ++-4 ++6 ++6 ++2 ++14 ++-6 ++-6 ++-8 ++4 ++-2 ++8 ++-12 ++6 ++-6 ++-8 ++0 ++-2 ++-12 ++-6 ++-6 ++4 ++2 ++8 ++-18 ++-2 ++-4 ++6 ++4 ++-6 ++-12 ++14 ++10 ++10 ++2 ++-12 ++0 ++-4 ++2 ++12 ++-18 ++6 ++10 ++-14 ++6 ++-4 ++-4 ++-2 ++6 ++4 ++-6 ++-12 ++-6 ++-6 ++-12 ++-4 ++4 ++-6 ++10 ++-4 ++-16 ++-12 ++-10 ++10 ++2 ++-8 ++-4 ++2 ++4 ++8 ++-2 ++0 ++12 ++2 ++14 ++8 ++0 ++-2 ++-8 ++4 ++0 ++4 ++12 ++-2 ++-10 ++-8 ++-8 ++8 ++4 ++-10 ++0 ++2 ++-6 ++6 ++-4 ++6 ++-6 ++6 ++6 ++4 ++-2 ++-12 ++0 ++-2 ++4 ++-4 ++-8 ++-4 ++10 ++-2 ++12 ++-8 ++12 ++12 ++10 ++-4 ++10 ++6 ++6 ++6 ++6 ++-2 ++12 ++8 ++-14 ++-2 ++6 ++-8 ++-10 ++0 ++4 ++6 ++2 ++-4 ++-4 ++-14 ++6 ++4 ++-2 ++2 ++8 ++-6 ++6 ++6 ++-8 ++-4 ++2 ++-4 ++-6 ++8 ++8 ++-12 ++-10 ++6 ++10 ++-8 ++-4 ++10 ++-2 ++0 ++2 ++-4 ++-8 ++12 ++4 ++-2 ++12 ++-6 ++10 ++0 ++2 ++2 ++2 ++2 ++2 ++8 ++6 ++-6 ++-8 ++14 ++-8 ++8 ++-8 ++-4 ++2 ++-8 ++-4 ++6 ++8 ++8 ++-8 ++-10 ++14 ++-6 ++8 ++-2 ++2 ++14 ++2 ++0 ++-8 ++-6 ++-12 ++14 ++2 ++2 ++8 ++-4 ++10 ++16 ++0 ++-8 ++6 ++6 ++-4 ++4 ++0 ++-4 ++-2 ++0 ++-10 ++0 ++2 ++8 ++4 ++-2 ++-6 ++-4 ++0 ++2 ++4 ++0 ++12 ++8 ++2 ++-4 ++-4 ++-6 ++-10 ++-2 ++-4 ++0 ++-4 ++-8 ++12 ++4 ++4 ++8 ++-10 ++-2 ++14 ++-6 ++4 ++-4 ++10 ++-2 ++8 ++12 ++0 ++-4 ++4 ++-6 ++4 ++6 ++8 ++-6 ++-12 ++0 ++-2 ++-6 ++6 ++4 ++6 ++-2 ++12 ++2 ++8 ++-6 ++-8 ++10 ++0 ++10 ++10 ++-14 ++-8 ++10 ++-4 ++-10 ++8 ++8 ++-2 ++0 ++-8 ++-10 ++10 ++12 ++4 ++-8 ++0 ++10 ++-6 ++-8 ++-4 ++8 ++-4 ++-8 ++-4 ++-8 ++0 ++-2 ++10 ++10 ++-8 ++14 ++-2 ++-4 ++6 ++-2 ++-6 ++4 ++2 ++2 ++-14 ++2 ++6 ++-6 ++0 ++-2 ++-6 ++6 ++12 ++-6 ++14 ++0 ++-4 ++0 ++-6 ++4 ++-4 ++6 ++12 ++-8 ++6 ++-6 ++6 ++14 ++-4 ++4 ++-2 ++10 ++0 ++4 ++0 ++-8 ++12 ++0 ++0 ++-2 ++-2 ++4 ++-8 ++6 ++8 ++6 ++6 ++-12 ++6 ++6 ++-2 ++-10 ++0 ++-4 ++-10 ++2 ++-2 ++-14 ++-4 ++2 ++2 ++0 ++0 ++6 ++-2 ++-4 ++-6 ++-8 ++-6 ++0 ++-4 ++2 ++-4 ++-2 ++-4 ++2 ++-4 ++6 ++10 ++-4 ++4 ++0 ++-6 ++-2 ++6 ++-4 ++-10 ++0 ++-2 ++-6 ++-6 ++-6 ++-10 ++4 ++-6 ++-6 ++-8 ++-6 ++-4 ++0 ++4 ++4 ++0 ++-6 ++-8 ++0 ++-8 ++-2 ++-4 ++-8 ++2 ++-2 ++0 ++-10 ++-6 ++2 ++0 ++2 ++4 ++-8 ++4 ++-6 ++-6 ++6 ++-6 ++-4 ++-6 ++-6 ++0 ++2 ++0 ++0 ++0 ++6 ++2 ++-2 ++-4 ++-2 ++-8 ++-2 ++0 ++-10 ++4 ++-14 ++-4 ++-12 ++-2 ++8 ++-2 ++-4 ++8 ++-2 ++-2 ++6 ++-4 ++-2 ++-4 ++-2 ++-2 ++4 ++2 ++-8 ++-6 ++0 ++-4 ++4 ++8 ++0 ++-2 ++-6 ++6 ++-8 ++-6 ++4 ++-8 ++-2 ++6 ++2 ++-8 ++-4 ++-8 ++-8 ++-4 ++-2 ++6 ++0 ++-4 ++-8 ++-10 ++2 ++8 ++4 ++4 ++-4 ++-6 ++6 ++-8 ++-10 ++2 ++4 ++-4 ++2 ++4 ++-2 ++2 ++4 ++-8 ++-8 ++0 ++-2 ++-2 ++2 ++-8 ++6 ++-10 ++-4 ++6 ++-8 ++-2 ++4 ++6 ++-2 ++2 ++2 ++0 ++2 ++2 ++-10 ++-10 ++-4 ++0 ++-4 ++-12 ++-4 ++6 ++2 ++-6 ++-8 ++4 ++8 ++-4 ++-6 ++-2 ++-2 ++-10 ++6 ++4 ++-2 ++-6 ++-6 ++-4 ++-14 ++6 ++-8 ++0 ++6 ++-8 ++-4 ++2 ++-8 ++6 ++2 ++2 ++-2 ++6 ++-2 ++0 ++-10 ++0 ++0 ++-8 ++6 ++-4 ++0 ++0 ++-2 ++0 ++0 ++-4 ++-2 ++-12 ++-14 ++-4 ++-16 ++-10 ++-4 ++8 ++-4 ++4 ++0 ++0 ++2 ++-4 ++-2 ++-12 ++-8 ++2 ++-2 ++-6 ++-6 ++-8 ++2 ++-4 ++-8 ++2 ++6 ++4 ++-10 ++-4 ++-6 ++0 ++0 ++-4 ++-6 ++-4 ++6 ++0 ++-6 ++-8 ++-2 ++-14 ++2 ++0 ++-10 ++4 ++0 ++-6 ++4 ++-2 ++4 ++-4 ++2 ++-2 ++-2 ++0 ++-12 ++-6 ++-4 ++-10 ++-10 ++8 ++2 ++-2 ++0 ++6 ++-6 ++-10 ++-2 ++-2 ++-8 ++-8 ++2 ++0 ++2 ++0 ++2 ++-6 ++-6 ++2 ++0 ++-10 ++6 ++-8 ++0 ++-6 ++-12 ++4 ++-12 ++0 ++8 ++-8 ++-2 ++6 ++-2 ++-6 ++0 ++0 ++-6 ++-6 ++-4 ++-6 ++-2 ++2 ++4 ++-2 ++2 ++0 ++2 ++6 ++-8 ++0 ++-6 ++-6 ++2 ++2 ++0 ++-4 ++4 ++2 ++-4 ++2 ++2 ++4 ++-6 ++-2 ++6 ++4 ++6 ++4 ++-6 ++-6 ++-10 ++4 ++4 ++4 ++-2 ++0 ++0 ++0 ++0 ++-4 ++-8 ++2 ++6 ++4 ++0 ++-2 ++-4 ++-10 ++-8 ++-6 ++-4 ++2 ++-6 ++2 ++4 ++-6 ++2 ++0 ++6 ++8 ++4 ++8 ++2 ++4 ++4 ++2 ++-2 ++-4 ++0 ++-10 ++-4 ++2 ++-4 ++-6 ++-4 ++-8 ++2 ++2 ++-8 ++2 ++-6 ++0 ++4 ++0 ++-8 ++-2 ++-2 ++-8 ++-4 ++-4 ++-8 ++-10 ++-2 ++4 ++-10 ++-4 ++4 ++2 ++-6 ++0 ++6 ++-2 ++0 ++0 ++2 ++-4 ++0 ++-2 ++2 ++0 ++-4 ++-6 ++-6 ++0 ++2 ++4 ++-4 ++2 ++4 ++2 ++-2 ++-6 ++4 ++4 ++6 ++-6 ++-2 ++4 ++2 ++2 ++-2 ++-2 ++-4 ++-6 ++-8 ++0 ++-8 ++0 ++-8 ++4 ++-2 ++-6 ++8 ++0 ++-2 ++4 ++0 ++0 ++-6 ++-12 ++-8 ++-2 ++-2 ++-4 ++-2 ++-8 ++-4 ++4 ++6 ++8 ++4 ++4 ++4 ++2 ++4 ++2 ++-2 ++4 ++-4 ++0 ++0 ++-4 ++0 ++-2 ++4 ++2 ++-2 ++2 ++-2 ++0 ++6 ++2 ++6 ++6 ++-2 ++2 ++-8 ++-2 ++-2 ++-10 ++-6 ++2 ++-2 ++0 ++-2 ++-10 ++-2 ++4 ++2 ++2 ++-8 ++-4 ++2 ++2 ++0 ++-6 ++-4 ++4 ++0 ++0 ++-4 ++-10 ++-4 ++-4 ++2 ++-2 ++-2 ++-8 ++-6 ++-6 ++-6 ++-2 ++2 ++2 ++0 ++-4 ++4 ++2 ++-4 ++-4 ++-4 ++-6 ++0 ++4 ++2 ++-6 ++-6 ++-6 ++4 ++-2 ++4 ++-4 ++0 ++8 ++-2 ++0 ++0 ++-6 ++2 ++0 ++-4 ++-2 ++-10 ++-6 ++-4 ++-2 ++-2 ++2 ++-6 ++0 ++-2 ++-4 ++6 ++0 ++-4 ++2 ++2 ++2 ++0 ++-2 ++4 ++0 ++6 ++-2 ++-2 ++-4 ++2 ++-2 ++-4 ++-6 ++-2 ++-4 ++2 ++4 ++-2 ++4 ++0 ++4 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-4 ++4 ++0 ++6 ++-4 ++-4 ++0 ++-4 ++6 ++-6 ++-4 ++-4 ++0 ++-4 ++0 ++2 ++-4 ++-2 ++6 ++6 ++-6 ++-6 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++4 ++-2 ++4 ++-6 ++0 ++0 ++-8 ++4 ++0 ++0 ++4 ++6 ++6 ++0 ++-2 ++-2 ++2 ++0 ++-4 ++4 ++-4 ++-6 ++2 ++-10 ++-4 ++-2 ++-4 ++2 ++2 ++-6 ++-6 ++2 ++0 ++4 ++2 ++-8 ++-2 ++2 ++4 ++-6 ++-6 ++2 ++-6 ++-2 ++-2 ++-6 ++2 ++2 ++0 ++4 ++2 ++0 ++-8 ++2 ++2 ++0 ++-2 ++2 ++4 ++-4 ++-6 ++-8 ++2 ++2 ++-6 ++0 ++0 ++-4 ++-2 ++0 ++0 ++0 ++0 ++4 ++-4 ++4 ++-4 ++4 ++4 ++4 ++6 ++0 ++-8 ++2 ++-2 ++0 ++-4 ++0 ++-2 ++-4 ++-4 ++-2 ++-2 ++-6 ++0 ++0 ++0 ++-4 ++-12 ++-12 ++-6 ++0 ++4 ++-2 ++2 ++2 ++0 ++-6 ++0 ++0 ++4 ++6 ++0 ++-8 ++-2 ++-10 ++-2 ++-4 ++-8 ++-2 ++0 ++-6 ++-8 ++-12 ++-10 ++0 ++2 ++-2 ++-6 ++0 ++-2 ++-8 ++-4 ++-8 ++-8 ++-4 ++-8 ++2 ++-2 ++-12 ++-4 ++-2 ++6 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++2 ++6 ++2 ++4 ++6 ++2 ++2 ++4 ++-4 ++0 ++0 ++-4 ++2 ++-8 ++-2 ++2 ++2 ++0 ++2 ++-4 ++-4 ++-2 ++-2 ++-2 ++0 ++-6 ++-4 ++2 ++-2 ++-6 ++-6 ++-4 ++-6 ++0 ++-6 ++-10 ++-2 ++-2 ++2 ++2 ++-6 ++-4 ++-6 ++4 ++0 ++2 ++2 ++0 ++6 ++0 ++-2 ++-4 ++2 ++4 ++6 ++4 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++2 ++-2 ++4 ++0 ++0 ++0 ++0 ++0 ++2 ++4 ++-2 ++-4 ++-4 ++2 ++2 ++-2 ++4 ++-2 ++0 ++0 ++-10 ++0 ++-4 ++-2 ++2 ++0 ++0 ++0 ++0 ++-4 ++-4 ++2 ++-2 ++-2 ++-2 ++-6 ++0 ++-6 ++-4 ++-8 ++4 ++2 ++4 ++2 ++-2 ++2 ++2 ++0 ++4 ++-2 ++-6 ++4 ++-6 ++-4 ++-4 ++2 ++-4 ++-2 ++-4 ++-2 ++2 ++-2 ++-4 ++0 ++4 ++-4 ++4 ++-2 ++-4 ++0 ++4 ++-6 ++-6 ++-2 ++-6 ++2 ++4 ++4 ++2 ++4 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-2 ++-4 ++-4 ++-4 ++0 ++6 ++-4 ++-4 ++2 ++2 ++-8 ++0 ++-4 ++-4 ++-4 ++-6 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-2 ++4 ++-4 ++6 ++-4 ++0 ++0 ++0 ++2 ++6 ++0 ++2 ++6 ++-6 ++6 ++6 ++-6 ++6 ++-2 ++-4 ++4 ++-2 ++4 ++0 ++2 ++-2 ++0 ++-6 ++-4 ++-4 ++-2 ++-4 ++-6 ++0 ++-8 ++-4 ++-8 ++-4 ++-2 ++4 ++-6 ++-8 ++0 ++-6 ++-2 ++-4 ++-4 ++-2 ++0 ++-2 ++2 ++2 ++4 ++4 ++6 ++4 ++6 ++6 ++4 ++6 ++8 ++2 ++8 ++4 ++2 ++6 ++4 ++2 ++-2 ++6 ++8 ++2 ++6 ++4 ++2 ++-2 ++0 ++0 ++2 ++4 ++6 ++0 ++8 ++2 ++2 ++6 ++2 ++-2 ++2 ++0 ++2 ++2 ++-2 ++2 ++6 ++2 ++8 ++6 ++2 ++4 ++2 ++2 ++8 ++6 ++-4 ++-2 ++-2 ++-4 ++0 ++-2 ++4 ++0 ++0 ++2 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++4 ++4 ++-2 ++2 ++-4 ++4 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++4 ++2 ++2 ++-2 ++2 ++0 ++0 ++0 ++0 ++-4 ++-4 ++-2 ++-6 ++2 ++2 ++2 ++4 ++6 ++0 ++-2 ++-4 ++-2 ++-2 ++2 ++2 ++-2 ++-4 ++-2 ++0 ++-2 ++-4 ++0 ++-2 ++-4 ++-2 ++-8 ++-6 ++-6 ++0 ++-4 ++-2 ++-4 ++0 ++2 ++-4 ++-2 ++0 ++2 ++0 ++-4 ++-4 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-4 ++-2 ++-4 ++-2 ++4 ++-2 ++2 ++0 ++-2 ++2 ++0 ++-4 ++4 ++0 ++-2 ++-2 ++4 ++-2 ++2 ++0 ++-2 ++-2 ++4 ++2 ++0 ++0 ++2 ++2 ++-4 ++-2 ++2 ++0 ++-2 ++-4 ++0 ++0 ++0 ++-2 ++0 ++0 ++4 ++0 ++0 ++0 ++0 ++6 ++4 ++2 ++-4 ++2 ++2 ++0 ++4 ++-2 ++4 ++2 ++6 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++-4 ++-6 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++4 ++-2 ++0 ++-4 ++0 ++-2 ++0 ++2 ++4 ++0 ++0 ++2 ++2 ++6 ++4 ++2 ++-2 ++-2 ++2 ++0 ++-4 ++-2 ++4 ++-4 ++2 ++2 ++-4 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++-4 ++0 ++-4 ++-8 ++-2 ++0 ++-4 ++-6 ++-2 ++-6 ++-4 ++-6 ++0 ++-2 ++-2 ++2 ++2 ++2 ++4 ++2 ++4 ++2 ++0 ++4 ++4 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-4 ++-2 ++-4 ++-2 ++2 ++2 ++0 ++-4 ++-2 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-4 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++-6 ++4 ++2 ++2 ++-4 ++-2 ++-2 ++-6 ++2 ++-2 ++-2 ++-2 ++2 ++0 ++-4 ++4 ++0 ++0 ++2 ++2 ++-2 ++0 ++4 ++-2 ++0 ++2 ++6 ++-2 ++2 ++-2 ++4 ++2 ++-4 ++0 ++-6 ++-2 ++4 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++0 ++2 ++2 ++0 ++-4 ++-2 ++2 ++-2 ++-4 ++2 ++0 ++4 ++2 ++0 ++2 ++6 ++2 ++2 ++4 ++0 ++2 ++4 ++0 ++0 ++4 ++4 ++4 ++-4 ++0 ++-4 ++2 ++2 ++-2 ++0 ++0 ++4 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++-4 ++0 ++2 ++-2 ++4 ++2 ++-2 ++-4 ++-2 ++0 ++2 ++2 ++2 ++0 ++-4 ++-2 ++-4 ++-6 ++0 ++2 ++2 ++-2 ++2 ++-4 ++-4 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++2 ++-4 ++-2 ++-4 ++-2 ++0 ++-2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-4 ++-4 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++0 ++0 ++-2 ++-4 ++-4 ++-2 ++2 ++2 ++4 ++-2 ++2 ++0 ++4 ++0 ++2 ++2 ++0 ++4 ++4 ++2 ++2 ++-2 ++4 ++0 ++6 ++8 ++4 ++0 ++-2 ++0 ++-2 ++2 ++2 ++-2 ++-2 ++2 ++-2 ++-6 ++-2 ++-4 ++-4 ++-2 ++-2 ++-6 ++-2 ++-4 ++-2 ++-6 ++0 ++6 ++2 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++-2 ++6 ++8 ++2 ++4 ++0 ++-2 ++0 ++-2 ++2 ++4 ++6 ++6 ++4 ++-4 ++-4 ++0 ++2 ++-2 ++-4 ++2 ++-2 ++-2 ++-4 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++-4 ++-2 ++2 ++-6 ++0 ++-4 ++-2 ++-2 ++2 ++2 ++-6 ++2 ++4 ++-4 ++-6 ++-2 ++-4 ++0 ++0 ++4 ++0 ++-2 ++4 ++2 ++-2 ++2 ++0 ++0 ++4 ++0 ++-2 ++0 ++4 ++0 ++-2 ++4 ++2 ++4 ++4 ++2 ++2 ++2 ++2 ++0 ++2 ++4 ++4 ++4 ++4 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++0 ++0 ++6 ++0 ++-4 ++4 ++-2 ++-2 ++-2 ++2 ++2 ++0 ++2 ++0 ++4 ++4 ++2 ++0 ++0 ++4 ++0 ++4 ++4 ++0 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++0 ++6 ++2 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++2 ++0 ++-2 ++0 ++4 ++0 ++-2 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++2 ++2 ++0 ++-2 ++-2 ++2 ++2 ++4 ++-2 ++2 ++0 ++0 ++4 ++0 ++0 ++4 ++4 ++0 ++4 ++4 ++2 ++0 ++4 ++6 ++2 ++0 ++4 ++6 ++0 ++4 ++4 ++4 ++6 ++4 ++8 ++0 ++2 ++0 ++2 ++2 ++-2 ++4 ++0 ++0 ++-2 ++2 ++0 ++4 ++4 ++4 ++4 ++2 ++4 ++2 ++2 ++2 ++4 ++0 ++-2 ++-4 ++0 ++2 ++2 ++4 ++2 ++6 ++4 ++2 ++-2 ++2 ++4 ++2 ++6 ++-2 ++2 ++-2 ++2 ++4 ++4 ++6 ++2 ++6 ++0 ++-4 ++0 ++2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++4 ++2 ++-2 ++4 ++2 ++4 ++0 ++0 ++4 ++2 ++-2 ++4 ++2 ++-2 ++4 ++6 ++4 ++6 ++0 ++2 ++4 ++2 ++6 ++2 ++4 ++0 ++4 ++4 ++-2 ++2 ++2 ++4 ++4 ++2 ++2 ++0 ++0 ++4 ++0 ++0 ++2 ++2 ++-2 ++-4 ++0 ++2 ++0 ++0 ++2 ++-2 ++4 ++2 ++0 ++4 ++4 ++6 ++4 ++0 ++0 ++-4 ++0 ++0 ++4 ++-2 ++0 ++0 ++-2 ++2 ++2 ++4 ++6 ++4 ++0 ++0 ++2 ++2 ++-2 ++4 ++0 ++2 ++0 ++-4 ++2 ++-4 ++8 ++6 ++2 ++4 ++0 ++2 ++0 ++2 ++2 ++4 ++8 ++4 ++2 ++0 ++0 ++-2 ++4 ++2 ++0 ++4 ++2 ++-2 ++2 ++0 ++0 ++4 ++0 ++2 ++2 ++2 ++0 ++2 ++-2 ++4 ++2 ++0 ++4 ++-2 ++0 ++-4 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++2 ++0 ++0 ++4 ++4 ++4 ++6 ++4 ++2 ++2 ++4 ++4 ++2 ++4 ++2 ++0 ++0 ++2 ++0 ++-4 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++6 ++4 ++-2 ++4 ++4 ++0 ++0 ++2 ++0 ++0 ++2 ++-4 ++4 ++2 ++4 ++2 ++0 ++4 ++0 ++6 ++8 ++0 ++6 ++2 ++2 ++0 ++0 ++2 ++6 ++0 ++2 ++0 ++-4 ++0 ++-2 ++2 ++0 ++6 ++0 ++4 ++0 ++4 ++4 ++0 ++6 ++4 ++6 ++2 ++2 ++-2 ++-2 ++0 ++-2 ++4 ++0 ++2 ++-2 ++0 ++-2 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++4 ++-2 ++2 ++4 ++0 ++2 ++2 ++2 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++4 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++0 ++-2 ++4 ++-2 ++0 ++4 ++0 ++2 ++4 ++2 ++-2 ++4 ++2 ++4 ++0 ++0 ++4 ++-2 ++2 ++0 ++2 ++2 ++6 ++4 ++4 ++2 ++0 ++6 ++2 ++2 ++0 ++6 ++4 ++2 ++4 ++2 ++0 ++-2 ++2 ++2 ++6 ++0 ++2 ++0 ++2 ++4 ++0 ++6 ++4 ++4 ++4 ++2 ++-2 ++0 ++2 ++2 ++4 ++0 ++2 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++-4 ++0 ++-4 ++-4 ++-2 ++2 ++2 ++0 ++2 ++0 ++-4 ++2 ++0 ++2 ++4 ++2 ++0 ++-4 ++2 ++0 ++4 ++2 ++4 ++4 ++4 ++4 ++2 ++4 ++6 ++8 ++2 ++6 ++0 ++0 ++6 ++0 ++-2 ++0 ++4 ++-2 ++-2 ++2 ++2 ++-2 ++4 ++4 ++0 ++4 ++2 ++2 ++-2 ++2 ++4 ++4 ++-2 ++0 ++0 ++0 ++4 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++2 ++4 ++4 ++4 ++4 ++6 ++4 ++2 ++2 ++0 ++0 ++2 ++0 ++-4 ++0 ++0 ++2 ++2 ++2 ++4 ++-2 ++2 ++0 ++4 ++0 ++4 ++0 ++2 ++0 ++6 ++4 ++0 ++2 ++4 ++4 ++0 ++2 ++-2 ++6 ++2 ++4 ++2 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++4 ++0 ++-4 ++0 ++0 ++2 ++2 ++2 ++-2 ++0 ++-2 ++-4 ++-4 ++-2 ++0 ++-4 ++-2 ++4 ++2 ++2 ++2 ++0 ++2 ++0 ++4 ++2 ++0 ++6 ++4 ++4 ++4 ++4 ++2 ++4 ++4 ++6 ++4 ++2 ++0 ++2 ++0 ++2 ++0 ++6 ++6 ++0 ++6 ++6 ++0 ++0 ++2 ++-2 ++0 ++4 ++-2 ++0 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++0 ++0 ++0 ++4 ++6 ++6 ++0 ++2 ++4 ++0 ++0 ++-2 ++-4 ++0 ++4 ++0 ++4 ++2 ++4 ++4 ++2 ++4 ++4 ++4 ++6 ++2 ++2 ++6 ++4 ++4 ++0 ++0 ++4 ++4 ++2 ++0 ++-2 ++2 ++4 ++-2 ++2 ++4 ++2 ++8 ++4 ++-2 ++2 ++-2 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++-2 ++-2 ++4 ++0 ++-2 ++0 ++2 ++0 ++2 ++0 ++-2 ++0 ++0 ++2 ++-2 ++2 ++2 ++0 ++-4 ++0 ++0 ++4 ++2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++-4 ++-2 ++-2 ++4 ++2 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-2 ++0 ++2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++2 ++-4 ++0 ++2 ++0 ++2 ++-2 ++-2 ++2 ++-2 ++2 ++-2 ++2 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++-6 ++-2 ++0 ++-2 ++0 ++-4 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-4 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++-2 ++2 ++-2 ++0 ++2 ++2 ++0 ++2 ++2 ++6 ++2 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++-4 ++-2 ++2 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-4 ++0 ++2 ++-2 ++0 ++-4 ++0 ++-4 ++0 ++-4 ++0 ++2 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++4 ++4 ++2 ++2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++2 ++0 ++2 ++0 ++4 ++-2 ++0 ++-2 ++-2 ++2 ++-4 ++-2 ++-4 ++-2 ++2 ++2 ++0 ++0 ++-2 ++-2 ++0 ++0 ++-4 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++2 ++2 ++2 ++-2 ++2 ++2 ++2 ++0 ++4 ++-4 ++2 ++4 ++2 ++2 ++0 ++-2 ++2 ++2 ++-2 ++-4 ++0 ++2 ++2 ++2 ++0 ++2 ++2 ++6 ++4 ++2 ++0 ++4 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++-4 ++2 ++-2 ++2 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++-4 ++-2 ++-4 ++0 ++-4 ++-2 ++-2 ++-6 ++0 ++-2 ++-6 ++-2 ++-2 ++-2 ++-4 ++0 ++0 ++4 ++2 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++2 ++-2 ++4 ++0 ++0 ++0 ++-2 ++0 ++-4 ++-2 ++0 ++2 ++2 ++4 ++0 ++0 ++4 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++2 ++-4 ++4 ++-2 ++0 ++2 ++2 ++0 ++-2 ++-2 ++0 ++-4 ++2 ++2 ++4 ++0 ++0 ++2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++0 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-4 ++-4 ++-2 ++0 ++-2 ++0 ++-4 ++-4 ++0 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++2 ++2 ++2 ++6 ++2 ++2 ++2 ++4 ++2 ++4 ++4 ++2 ++-2 ++2 ++4 ++2 ++6 ++2 ++6 ++0 ++4 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-4 ++-2 ++-4 ++2 ++0 ++4 ++0 ++0 ++2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++-2 ++0 ++2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-2 ++0 ++0 ++0 ++-4 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-4 ++-4 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-2 ++-4 ++-4 ++-4 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++4 ++0 ++2 ++-2 ++2 ++2 ++0 ++4 ++2 ++2 ++4 ++2 ++2 ++2 ++0 ++4 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++-4 ++2 ++-2 ++0 ++2 ++2 ++2 ++4 ++2 ++4 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++0 ++4 ++0 ++0 ++0 ++-4 ++0 ++-6 ++0 ++0 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-4 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++2 ++-4 ++0 ++-2 ++-4 ++0 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++-4 ++-2 ++2 ++-2 ++2 ++2 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++2 ++0 ++2 ++2 ++2 ++0 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++-4 ++2 ++2 ++-2 ++2 ++0 ++2 ++0 ++0 ++2 ++2 ++-2 ++-2 ++2 ++-6 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++-4 ++0 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++-4 ++4 ++0 ++4 ++0 ++2 ++4 ++2 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++2 ++-2 ++2 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-2 ++-4 ++-2 ++-4 ++-4 ++0 ++-4 ++-2 ++0 ++0 ++2 ++2 ++-2 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++2 ++2 ++2 ++0 ++2 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++-4 ++0 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++2 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++-4 ++0 ++0 ++0 ++0 ++2 ++2 ++-2 ++2 ++-2 ++-2 ++4 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++-4 ++0 ++-2 ++-4 ++0 ++-4 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++-2 ++0 ++4 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++2 ++0 ++2 ++0 ++2 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++2 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++4 ++2 ++0 ++-2 ++2 ++2 ++-2 ++0 ++-2 ++0 ++2 ++0 ++-2 ++2 ++0 ++2 ++0 ++0 ++4 ++4 ++0 ++0 ++-4 ++-2 ++-2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++0 ++2 ++0 ++2 ++0 ++2 ++-2 ++-4 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++-2 ++2 ++2 ++2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++0 ++0 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++2 ++2 ++2 ++0 ++-2 ++4 ++-2 ++2 ++-2 ++2 ++0 ++2 ++0 ++2 ++-2 ++0 ++0 ++-4 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++2 ++0 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++-2 ++-4 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++-4 ++2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++2 ++0 ++-2 ++-2 ++2 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++2 ++2 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++-2 ++4 ++-2 ++-2 ++2 ++0 ++0 ++-2 ++2 ++0 ++-2 ++0 ++2 ++0 ++-2 ++2 ++0 ++-4 ++0 ++-4 ++2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++-4 ++0 ++-4 ++0 ++-2 ++-2 ++4 ++-2 ++0 ++2 ++-4 ++2 ++2 ++0 ++4 ++2 ++0 ++4 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++-2 ++0 ++2 ++0 ++-2 ++0 ++2 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++0 ++2 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++2 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++4 ++0 ++2 ++-2 ++-2 ++2 ++2 ++2 ++2 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++2 ++-2 ++2 ++0 ++-4 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-4 ++0 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++-2 ++4 ++-2 ++2 ++2 ++-2 ++0 ++2 ++-2 ++4 ++-2 ++0 ++2 ++0 ++0 ++0 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++-2 ++2 ++-4 ++-2 ++-4 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++2 ++2 ++0 ++-2 ++0 ++2 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++2 ++2 ++-2 ++0 ++2 ++2 ++-2 ++-4 ++-2 ++0 ++-2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++-4 ++-2 ++-4 ++0 ++0 ++-4 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++2 ++4 ++0 ++-2 ++2 ++2 ++0 ++2 ++2 ++4 ++2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-4 ++0 ++-2 ++2 ++2 ++2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++0 ++0 ++4 ++-2 ++2 ++-2 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-4 ++-4 ++-4 ++-4 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++4 ++2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++2 ++-2 ++-4 ++0 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++-2 ++2 ++0 ++2 ++2 ++2 ++-2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++2 ++2 ++2 ++-2 ++-2 ++-4 ++-2 ++-4 ++-4 ++-6 ++-4 ++-2 ++0 ++2 ++-2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++2 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++-2 ++0 ++-4 ++0 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++-4 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++4 ++-2 ++0 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++-2 ++0 ++2 ++2 ++4 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-4 ++2 ++0 ++0 ++-4 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-2 ++0 ++2 ++4 ++2 ++4 ++2 ++0 ++2 ++-2 ++2 ++0 ++4 ++-2 ++0 ++2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++2 ++-4 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++2 ++0 ++2 ++2 ++-2 ++0 ++2 ++-2 ++0 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-4 ++2 ++0 ++0 ++4 ++-2 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++2 ++0 ++2 ++2 ++4 ++2 ++0 ++2 ++4 ++4 ++4 ++-2 ++0 ++2 ++0 ++0 ++0 ++-4 ++2 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++-2 ++2 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++0 ++2 ++2 ++0 ++2 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-4 ++-4 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++-4 ++0 ++-2 ++0 ++-4 ++-2 ++-2 ++2 ++-2 ++0 ++2 ++2 ++0 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-4 ++2 ++2 ++-2 ++2 ++0 ++2 ++4 ++2 ++2 ++2 ++4 ++4 ++4 ++4 ++4 ++2 ++2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-6 ++-4 ++-4 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++2 ++2 ++4 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++4 ++4 ++2 ++4 ++2 ++4 ++0 ++-2 ++-2 ++2 ++4 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++4 ++-2 ++2 ++0 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++2 ++2 ++-2 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++2 ++-2 ++2 ++0 ++0 ++2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++-2 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++-4 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++0 ++-4 ++-2 ++-2 ++0 ++-2 ++-4 ++-4 ++0 ++-2 ++0 ++-2 ++2 ++0 ++4 ++2 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-2 ++0 ++4 ++0 ++2 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++-2 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++2 ++2 ++0 ++2 ++2 ++2 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++-2 ++2 ++4 ++2 ++6 ++4 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-4 ++-2 ++-2 ++-4 ++-4 ++-4 ++0 ++2 ++-2 ++-2 ++-2 ++-4 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++0 ++2 ++4 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++4 ++2 ++2 ++2 ++2 ++2 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++-4 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-4 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++2 ++-2 ++4 ++2 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++2 ++-2 ++-4 ++0 ++0 ++-2 ++-4 ++0 ++0 ++-2 ++0 ++2 ++2 ++0 ++4 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-4 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-4 ++-2 ++-2 ++-4 ++-2 ++0 ++2 ++-2 ++-2 ++-4 ++0 ++2 ++0 ++-2 ++-2 ++2 ++2 ++-2 ++0 ++0 ++0 ++4 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++0 ++2 ++4 ++4 ++4 ++6 ++2 ++6 ++0 ++0 ++4 ++2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-4 ++0 ++0 ++-2 ++2 ++-4 ++-2 ++-4 ++-4 ++-4 ++-2 ++-4 ++-4 ++-2 ++-4 ++0 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++-2 ++0 ++2 ++2 ++4 ++0 ++-2 ++0 ++0 ++2 ++0 ++2 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++4 ++2 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++-4 ++0 ++-4 ++-2 ++-4 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++0 ++2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++-2 ++-4 ++0 ++0 ++2 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++-4 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++2 ++-2 ++2 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++4 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++2 ++2 ++2 ++4 ++2 ++4 ++2 ++2 ++0 ++0 ++2 ++4 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++-2 ++2 ++2 ++0 ++0 ++2 ++-2 ++2 ++2 ++2 ++0 ++0 ++2 ++4 ++2 ++4 ++2 ++0 ++2 ++4 ++2 ++2 ++2 ++2 ++2 ++0 ++2 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++2 ++0 ++-2 ++0 ++2 +diff -Nurp base/drivers/staging/echo/TODO new/drivers/staging/echo/TODO +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/TODO 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,10 @@ ++TODO: ++ - checkpatch.pl cleanups ++ - Lindent ++ - typedef removals ++ - handle bit_operations.h (merge in or make part of common code?) ++ - remove proc interface, only use echo.h interface (proc interface is ++ racy and not correct.) ++ ++Please send patches to Greg Kroah-Hartman and Cc: Steve ++Underwood and David Rowe --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/no_firmware_download +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/no_firmware_download @@ -0,0 +1,21 @@ +The firmware downloaders are extra kernel objects that are now required for +the Hardware echo canceller support in some Digium cards (wctdm24xxp, +wcte12xp). They are downloaded at build time. The makefile will build +with support for them if they were indeed downloaded. + +This patch removes this downloading and thus keeps those modules +DFSG-compliant. Unlike the Digium firmwares, this is a compile-time +decision and hence cannot be reverted once a dahdi-modules package is +built. + +--- a/Makefile ++++ b/Makefile +@@ -98,7 +98,7 @@ include/dahdi/version.h: FORCE + fi + @rm -f $@.tmp + +-prereq: include/dahdi/version.h firmware-loaders ++prereq: include/dahdi/version.h + + stackcheck: checkstack modules + ./checkstack kernel/*.ko kernel/*/*.ko --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/oslec_zaptap +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/oslec_zaptap @@ -0,0 +1,264 @@ +## oslec_zaptap by Tzafrir Cohen +## +## The zaptap device for sampling echo. Part of the oslec echo canceller. + +diff -urNad zaptel-1.2.17.1.xpp.r3965~/zaptel-base.c zaptel-1.2.17.1.xpp.r3965/zaptel-base.c +--- zaptel-1.2.17.1.xpp.r3965~/kernel/zaptel-base.c 2007-06-16 07:10:17.000000000 +0300 ++++ zaptel-1.2.17.1.xpp.r3965/kernel/zaptel-base.c 2007-06-16 07:55:33.000000000 +0300 +@@ -5708,6 +5708,74 @@ + spin_unlock_irqrestore(&chan->lock, flags); + } + ++#ifdef USE_ZAPTAP ++/* Zaptap code -----------------------------------------------------------*/ ++ ++#define SAMPLE_BUF_SZ 1000 ++#define SAMPLE_IDLE 0 ++#define SAMPLE_PING 1 ++#define SAMPLE_PONG 2 ++ ++DECLARE_WAIT_QUEUE_HEAD(sample_wait); ++static int sample_state = 0; ++static int samples = 0; ++static short *psample; ++static short ping[3*SAMPLE_BUF_SZ]; ++static short pong[3*SAMPLE_BUF_SZ]; ++static int sample_ch = 1; ++static int sample_impulse = 0; ++static int tmp1,tmp2; ++ ++static inline void sample_echo_before(int channo, short rxlin, short txlin) { ++ /* Sample echo canceller signals ++ * Notes: ++ * 1. Samples are multiplexed in buffer: ++ * tx sample ++ * rx sample ++ * ec sample ++ * 2. We needs to sample rx here before echo can as it is ++ * overwritten. ++ */ ++ tmp1++; ++ tmp2 = channo; ++ if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { ++ *psample++ = txlin; ++ *psample++ = rxlin; ++ } ++} ++ ++static inline void sample_echo_after(int channo, short rxlin) { ++ ++ if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { ++ ++ *psample++ = rxlin; ++ ++ /* sample collection ping-pong buffer logic */ ++ ++ samples++; ++ if (samples >= SAMPLE_BUF_SZ) { ++ /* time to swap buffers */ ++ samples = 0; ++ ++ if (sample_state == SAMPLE_PING) { ++ sample_state = SAMPLE_PONG; ++ psample = pong; ++ } ++ else { ++ sample_state = SAMPLE_PING; ++ psample = ping; ++ } ++ wake_up_interruptible(&sample_wait); ++ } ++ } ++} ++ ++/* end Zaptap code -----------------------------------------------------*/ ++#else /* USE_ZAPTAP */ ++#define sample_echo_before(a,b,c) ++#define sample_echo_after(a,b) ++#endif /* USE_ZAPTAP */ ++ + static inline void __zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk) + { + short rxlin, txlin; +@@ -5758,7 +5826,9 @@ + #if !defined(ZT_EC_ARRAY_UPDATE) + for (x=0;xchanno, rxlin, ZT_XLAW(txchunk[x], ss)); /* Zaptap code */ + rxlin = echo_can_update(ss->ec, ZT_XLAW(txchunk[x], ss), rxlin); ++ sample_echo_after(ss->channo, rxlin); /* Zaptap code */ + rxchunk[x] = ZT_LIN2X((int)rxlin, ss); + } + #else /* defined(ZT_EC_ARRAY_UPDATE) */ +@@ -6505,6 +6575,10 @@ + static void __zt_transmit_chunk(struct zt_chan *chan, unsigned char *buf) + { + unsigned char silly[ZT_CHUNKSIZE]; ++#ifdef USE_ZAPTAP ++ int x; ++#endif ++ + /* Called with chan->lock locked */ + if (!buf) + buf = silly; +@@ -6519,6 +6593,22 @@ + kernel_fpu_end(); + #endif + } ++ ++#ifdef USE_ZAPTAP ++ /* Start Zaptap code -----------------------------------------*/ ++ if (sample_impulse && (samples == 0)) { ++ ++ /* option impulse insertion, tx stream becomes one */ ++ /* impulse followed by SAMPLE_BUF_SZ-1 0's */ ++ ++ buf[0] = ZT_LIN2MU(10000); ++ for (x=1;x +## +## Support bero.net cards in qozap and cwain Zaptel drivers. +## Source: http://blog.eth0.cc/zaptel-patchwork/ + +diff -urNad zaptel-1.4.7.1~dfsg~/cwain/cwain.c zaptel-1.4.7.1~dfsg/cwain/cwain.c +--- zaptel-1.4.7.1~dfsg~/kernel/cwain/cwain.c 2007-12-30 10:26:20.000000000 +0200 ++++ zaptel-1.4.7.1~dfsg/kernel/cwain/cwain.c 2007-12-30 10:44:27.000000000 +0200 +@@ -12,6 +12,13 @@ + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * ++ * Patched to recognize and support BN1E1 and BN2E1(+) boards ++ * by Daniel Scheller or ++ * ++ * The patch is (C) 2007 LocaNet oHG. It is free software and ++ * may be modified and distributed under the terms of the GNU ++ * General Public License. ++ * + */ + #include + #include +@@ -153,7 +160,7 @@ + if (!(cwaintmp->span.flags & ZT_FLAG_RUNNING)) { + return; + } +- if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554)) { ++ if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554) || (cwaintmp->type == 0xb563) || (cwaintmp->type == 0xb564) || (cwaintmp->type == 0xb565)) { + /* sync status */ + if (((cwaintmp->sync_sta & 0x07) == 0x07) && cwaintmp->sync) { + cwaintmp->leds[0] = 1; +@@ -294,7 +301,7 @@ + } + spin_unlock(&cwain_span_registerlock); + +- if (cwainspan->type == 0xb553) { ++ if ((cwainspan->type == 0xb553) || (cwainspan->type == 0xb563)) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); +@@ -309,10 +316,20 @@ + cwaintmp->spans = 1; + cwaintmp->cardID = cwainspan->cardID; + cwain_register_card(cwaintmp); +- printk(KERN_INFO +- "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", +- (unsigned long) cwainspan->pci_io, +- cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ ++ switch(cwainspan->type) ++ { ++ case 0xb553 : printk(KERN_INFO ++ "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", ++ (unsigned long) cwainspan->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ break; ++ case 0xb563 : printk(KERN_INFO ++ "cwain: BeroNet BN1E1 (single HFC-E1) PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", ++ (unsigned long) cwainspan->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ break; ++ } + } else { + cwaintmp = cwain_get_card(cwainspan->pcibus); + if (!cwaintmp) { +@@ -325,13 +342,21 @@ + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; +- cwaintmp->spans = cwainspan->type - 46419; ++ cwaintmp->spans = 1; + cwaintmp->span[0] = cwainspan; + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->syncs[0] = -1; + cwain_register_card(cwaintmp); + } else { +- cwaintmp->spans = cwainspan->type - 46418; ++ switch (cwainspan->type) { ++ case 0xb554 : ++ case 0xb564 : ++ case 0xb565 : cwaintmp->spans = 2; ++ break; ++ default : cwaintmp->spans = 0; ++ break; ++ } ++ + if (cwainspan->cardID < cwaintmp->cardID) { + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->span[1] = cwaintmp->span[0]; +@@ -341,10 +366,30 @@ + cwaintmp->span[1] = cwainspan; + cwaintmp->syncs[1] = -1; + } +- printk(KERN_INFO +- "cwain: Junghanns.NET doubleE1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", +- (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, +- cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ ++ switch (cwainspan->type) { ++ case 0xb554 : printk(KERN_INFO ++ "cwain: Junghanns.NET doubleE1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ case 0xb564 : printk(KERN_INFO ++ "cwain: BeroNet BN2E1 (dual HFC-E1) PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ case 0xb565 : printk(KERN_INFO ++ "cwain: BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ default : printk(KERN_INFO ++ "cwain: Unknown HFC-E1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ } ++ + } + } + return 0; +@@ -1232,6 +1277,15 @@ + case 0xb554: + sprintf(cwainspan->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; ++ case 0xb563: ++ sprintf(cwainspan->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; ++ case 0xb564: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; ++ case 0xb565: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; + } + } else { + switch (cwainspan->type) { +@@ -1241,6 +1295,15 @@ + case 0xb554: + sprintf(cwainspan->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; ++ case 0xb563: ++ sprintf(cwainspan->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; ++ case 0xb564: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; ++ case 0xb565: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; + } + } + cwain_doLEDs(cwainspan); +@@ -1862,6 +1925,15 @@ + case 0xb554: + sprintf(cwaintmp->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + break; ++ case 0xb563: ++ sprintf(cwaintmp->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d)",cwaintmp->cardno,cwaintmp->cardID); ++ break; ++ case 0xb564: ++ sprintf(cwaintmp->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); ++ break; ++ case 0xb565: ++ sprintf(cwaintmp->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); ++ break; + default: + return -1; + } +@@ -2038,7 +2110,7 @@ + /* disable interrupts */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + +- if (((tmp->subsystem_device==0xb553) || (tmp->subsystem_device==0xb554))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { ++ if (((tmp->subsystem_device==0xb553) || (tmp->subsystem_device==0xb554) || (tmp->subsystem_device==0xb563) || (tmp->subsystem_device==0xb564) || (tmp->subsystem_device==0xb565))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { + dips = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 5); + cid = 7; + for (i=0;i<3;i++) { +diff -urNad qozap/qozap.c qozap/qozap.c +--- a/kernel/qozap/qozap.c 2007-12-30 10:26:21.000000000 +0200 ++++ b/kernel/qozap/qozap.c 2007-12-30 10:44:21.000000000 +0200 +@@ -9,6 +9,14 @@ + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * ++ * Patched to recognize and support BN2S0, BN4S0, BN8S0 and BN8S0+ ++ * by Daniel Scheller or and ++ * Henning Holtschneider ++ * ++ * The patch is (C) 2006, 2007 LocaNet oHG. It is free software and ++ * may be modified and distributed under the terms of the GNU Public ++ * License. ++ * + */ + #include + #include +@@ -112,7 +120,7 @@ + unsigned long flags; + spin_lock_irqsave(&qoztmp->lock,flags); + +- if ((qoztmp->type == 0xb520) && (qoztmp->stports == 4)){ ++ if (((qoztmp->type == 0xb520) || (qoztmp->type == 0xb560)) && (qoztmp->stports == 4)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xf); + qoz_outb(qoztmp,qoz_R_GPIO_OUT1,(qoztmp->leds[0] | (qoztmp->leds[1] << 1) | (qoztmp->leds[2] << 2) | (qoztmp->leds[3] << 3))); +@@ -147,7 +155,7 @@ + */ + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + +- } else if (((qoztmp->type == 0xb556) || (qoztmp->type == 0xb751)) && (qoztmp->stports == 2)){ ++ } else if (((qoztmp->type == 0xb556) || (qoztmp->type == 0xb566) || (qoztmp->type == 0xb751)) && (qoztmp->stports == 2)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xff); + leds = 0x0; +@@ -159,7 +167,7 @@ + } + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + +- } else if (qoztmp->type == 0xb558) { ++ } else if ((qoztmp->type == 0xb558) || (qoztmp->type == 0xb568)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x80 | 0x40 | 0x20 | 0x4); + if (qoztmp->leds[0] == 1) { +@@ -182,7 +190,7 @@ + gpio_15 2 + gpio_14 3 + */ +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + /* 0x01 g1 st1 + 0x02 g2 st2 +@@ -302,9 +310,9 @@ + + /* set S0 amplitude */ + qoz_outb(qoztmp,qoz_R_PWM_MD,0xa0); +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); + } else { + qoz_outb(qoztmp,qoz_R_PWM0,0x1E); +@@ -318,9 +326,9 @@ + /* all state changes */ + qoz_outb(qoztmp,qoz_R_SCI_MSK, 0xff); + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); + } else { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x26); +@@ -360,11 +368,11 @@ + qoztmp->leds[6] = 0x0; + qoztmp->leds[7] = 0x0; + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoztmp->stports = 8; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoztmp->stports = 8; +- } else if (qoztmp->type == 0xb556) { ++ } else if ((qoztmp->type == 0xb556) || (qoztmp->type == 0xb566)) { + qoztmp->stports = 2; + } else if (qoztmp->type == 0xb557) { + qoztmp->stports = 2; +@@ -373,7 +381,7 @@ + } + qoztmp->ticks = 0; + qoztmp->clicks = 0; +- if ((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556)) { ++ if ((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556) || (qoztmp->type == 0xb566)) { + printk(KERN_INFO "qozap: Starting hardware watchdog.\n"); + qoztmp->wdp = 2; + } else { +@@ -427,9 +435,9 @@ + unsigned long flags; + + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -493,9 +501,9 @@ + char offset = 0; + unsigned long flags; + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -526,7 +534,7 @@ + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; +- if ((qoztmp->type != 0xb552) && (qoztmp->type != 0xb55b)){ ++ if ((qoztmp->type != 0xb552) && (qoztmp->type != 0xb55b) && (qoztmp->type != 0xb562) && (qoztmp->type != 0xb56b)){ + z1 = qoz_inw(qoztmp,qoz_A_Z1) & 0x7ff; + z2 = qoz_inw(qoztmp,qoz_A_Z2) & 0x7ff; + } else { +@@ -535,7 +543,7 @@ + } + } + +- if ((qoztmp->type == 0xb552)|| (qoztmp->type == 0xb55b)) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b) || (qoztmp->type == 0xb562) || (qoztmp->type == 0xb56b)) { + len = z1 - z2; + if (len < 0) { + len += qoz_DFIFO_SIZE8; +@@ -944,9 +952,9 @@ + if (debug > 1) { + printk(KERN_INFO "qozap: card %d R_BERT_STA = %#x\n", qoztmp->cardno, qoz_inb(qoztmp, qoz_R_BERT_STA) & 7); + } +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1131,7 +1139,7 @@ + /* fifo irq */ + spin_lock_irqsave(&(qoztmp->lock), flags); + irq_foview = qoz_inb(qoztmp,qoz_R_IRQ_OVIEW); +- if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b)) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b) || (qoztmp->type == 0xb562) || (qoztmp->type == 0xb56b)) { + if (irq_foview & 0x60) { + offset = 0; + fi = qoz_inb(qoztmp,qoz_R_IRQ_FIFO_BL6); +@@ -1278,9 +1286,9 @@ + // irqs off + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1411,9 +1419,9 @@ + + spin_lock_irqsave(&qoztmp->lock,flags); + // turn off irqs for all fifos +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1642,7 +1650,7 @@ + qoz_resetCard(qoztmp); + + +- if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ if (((tmp->subsystem_device==0xb520) || (tmp->subsystem_device==0xb560)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + // printk(KERN_INFO "MODES = %#x.\n",modes); + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); +@@ -1670,7 +1678,7 @@ + } + } + +- } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb556) || (tmp->subsystem_device==0xb566)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* duoBRI */ + + /* gpi27 1 gpi23 2 */ +@@ -1686,7 +1694,7 @@ + qoz_outw_io(qoztmp,0x0,0x0); */ + + +- } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb558) || (tmp->subsystem_device==0xb568)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* quadBRI minipCI */ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xF0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); +@@ -1706,7 +1714,7 @@ + } + // printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + +- } else if ((tmp->subsystem_device==0xb55b) && (pcidid == PCI_DEVICE_ID_CCD_M)){ ++ } else if (((tmp->subsystem_device==0xb55b) || (tmp->subsystem_device==0xb56b)) && (pcidid == PCI_DEVICE_ID_CCD_M)){ + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + + dips = ~qoz_inb_io(qoztmp,0x4000); +@@ -1730,7 +1738,7 @@ + } + + if (ports == -1) { +- if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ if (((tmp->subsystem_device==0xb520) || (tmp->subsystem_device==0xb560)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb550) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); +@@ -1743,7 +1751,7 @@ + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; +- } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb558) || (tmp->subsystem_device==0xb568)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { +@@ -1754,7 +1762,7 @@ + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; +- } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb556) || (tmp->subsystem_device==0xb566)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { +@@ -1817,6 +1825,20 @@ + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; ++ case 0xb560: ++ printk(KERN_INFO ++ "qozap: BeroNet BN4S0 card configured at io port %#x IRQ %d HZ %d CardID %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ, cid); ++ totalBRIs += 4; ++ break; ++ case 0xb566: ++ printk(KERN_INFO ++ "qozap: BeroNet BN2S0 card configured at io port %#x IRQ %d HZ %d CardID %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ, cid); ++ totalBRIs += 2; ++ break; + } + } else { + switch (tmp->subsystem_device) { +@@ -1832,6 +1854,18 @@ + (u_int) qoztmp->ioport, + qoztmp->irq, HZ); + break; ++ case 0xb562: ++ printk(KERN_INFO ++ "qozap: BeroNet BN8S0 card configured at io port %#x IRQ %d HZ %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ); ++ break; ++ case 0xb56b: ++ printk(KERN_INFO ++ "qozap: BeroNet BN8S0 (+) card configured at io port %#x IRQ %d HZ %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ); ++ break; + default: + printk(KERN_INFO + "qozap: wtf\n"); --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/oslec_kbuild +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/oslec_kbuild @@ -0,0 +1,8 @@ +Add a Kbuild file to oslec. It has a very similar Makefile +but in it, the build is conditioned on a certain Kconfig variable. + +--- /dev/null ++++ b/drivers/staging/echo/Kbuild +@@ -0,0 +1,2 @@ ++# An explicit 'obj-m' , unlike the Makefile ++obj-m += echo.o --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/zaphfc-florz +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/zaphfc-florz @@ -0,0 +1,1264 @@ +## zaphfc-florz by Faidon Liambotis +## +## florz' patch to zaphfc +## Author: Florian Zumbiehl + +diff -urNad trunk~/zaphfc/Makefile trunk/zaphfc/Makefile +--- trunk~/kernel/zaphfc/Makefile 2007-07-22 05:01:06.000000000 +0300 ++++ trunk/kernel/zaphfc/Makefile 2007-08-05 14:46:58.000000000 +0300 +@@ -2,12 +2,11 @@ + BRISTUFFBASE = $(shell dirname `pwd`) + + ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") +-RTAI = $(shell [ -f /usr/realtime/include/rtai.h ] && echo "-DRTAITIMING -I/usr/realtime/include") + +-CFLAGS+=-I. $(ZAP) $(RTAI) -O2 -g -Wall -DBUILDING_TONEZONE ++CFLAGS+=-I. $(ZAP) -O2 -g -Wall -DBUILDING_TONEZONE + CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +-KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) $(RTAI) -Wall ++KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) -Wall + KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") + KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +diff -urN bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.c zaphfc_0.4.0-test1_florz-13/zaphfc.c +--- bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.c 2007-04-03 11:38:45.000000000 +0200 ++++ zaphfc_0.4.0-test1_florz-13/kernel/zaphfc/zaphfc.c 2007-04-17 21:14:39.000000000 +0200 +@@ -7,19 +7,21 @@ + * + * Klaus-Peter Junghanns + * ++ * Copyright (C) 2004, 2005, 2006 Florian Zumbiehl ++ * - support for slave mode of the HFC-S chip which allows it to ++ * sync its sample clock to an external source/another HFC chip ++ * - support for "interrupt bundling" (let only one card generate ++ * 8 kHz timing interrupt no matter how many cards there are ++ * in the system) ++ * - interrupt loss tolerant b channel handling ++ * + * This program is free software and may be modified and +- * distributed under the terms of the GNU Public License. ++ * distributed under the terms of the GNU General Public License. + * + */ + + #include + #include +-#ifdef RTAITIMING +-#include +-#include +-#include +-#include +-#endif + #include + #include + #include +@@ -31,6 +33,8 @@ + #include + #endif + ++#define log2(n) ffz(~(n)) ++ + #if CONFIG_PCI + + #define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +@@ -73,41 +77,30 @@ + static int hfc_dev_count = 0; + static int modes = 0; // all TE + static int debug = 0; ++static int sync_slave = 0; // all master ++static int timer_card = 0; ++static int jitterbuffer = 1; + static struct pci_dev *multi_hfc = NULL; + static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; + +-void hfc_shutdownCard(struct hfc_card *hfctmp) { +- unsigned long flags; +- +- if (hfctmp == NULL) { +- return; +- } +- +- if (hfctmp->pci_io == NULL) { +- return; +- } +- +- spin_lock_irqsave(&hfctmp->lock,flags); +- ++void hfc_shutdownCard1(struct hfc_card *hfctmp) { + printk(KERN_INFO "zaphfc: shutting down card at %p.\n",hfctmp->pci_io); + + /* Clear interrupt mask */ + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + +- /* Reset pending interrupts */ +- hfc_inb(hfctmp, hfc_INT_S1); ++ /* Remove interrupt handler */ ++ free_irq(hfctmp->irq,hfctmp); ++} ++ ++void hfc_shutdownCard2(struct hfc_card *hfctmp) { ++ unsigned long flags; + +- /* Wait for interrupts that might still be pending */ +- spin_unlock_irqrestore(&hfctmp->lock, flags); +- set_current_state(TASK_UNINTERRUPTIBLE); +- schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + +- /* Remove interrupt handler */ +- if (hfctmp->irq) { +- free_irq(hfctmp->irq, hfctmp); +- } ++ /* Reset pending interrupts */ ++ hfc_inb(hfctmp, hfc_INT_S1); + + /* Soft-reset the card */ + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on +@@ -121,8 +114,8 @@ + + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, 0); // disable memio and bustmaster + +- if (hfctmp->fifomem != NULL) { +- kfree(hfctmp->fifomem); ++ if (hfctmp->fifos != NULL) { ++ free_pages((unsigned long)hfctmp->fifos,log2(hfc_FIFO_MEM_SIZE_PAGES)); + } + iounmap((void *) hfctmp->pci_io); + hfctmp->pci_io = NULL; +@@ -132,11 +125,24 @@ + spin_unlock_irqrestore(&hfctmp->lock,flags); + if (hfctmp->ztdev != NULL) { + zt_unregister(&hfctmp->ztdev->span); +- kfree(hfctmp->ztdev); ++ vfree(hfctmp->ztdev); + printk(KERN_INFO "unregistered from zaptel.\n"); + } + } + ++void hfc_shutdownCard(struct hfc_card *hfctmp) { ++ if (hfctmp == NULL) { ++ return; ++ } ++ ++ if (hfctmp->pci_io == NULL) { ++ return; ++ } ++ ++ hfc_shutdownCard1(hfctmp); ++ hfc_shutdownCard2(hfctmp); ++} ++ + void hfc_resetCard(struct hfc_card *hfctmp) { + unsigned long flags; + +@@ -180,14 +186,14 @@ + hfctmp->regs.ctmt = hfc_CTMT_TRANSB1 | hfc_CTMT_TRANSB2; // all bchans are transparent , no freaking hdlc + hfc_outb(hfctmp, hfc_CTMT, hfctmp->regs.ctmt); + +- hfctmp->regs.int_m1 = 0; ++ hfctmp->regs.int_m1=hfc_INTS_L1STATE; ++ if(hfctmp->cardno==timer_card){ ++ hfctmp->regs.int_m2=hfc_M2_PROC_TRANS; ++ }else{ ++ hfctmp->regs.int_m1|=hfc_INTS_DREC; ++ hfctmp->regs.int_m2=0; ++ } + hfc_outb(hfctmp, hfc_INT_M1, hfctmp->regs.int_m1); +- +-#ifdef RTAITIMING +- hfctmp->regs.int_m2 = 0; +-#else +- hfctmp->regs.int_m2 = hfc_M2_PROC_TRANS; +-#endif + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Clear already pending ints */ +@@ -199,8 +205,8 @@ + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_TE; /* set tx_lo mode, error in datasheet ! */ + } + +- hfctmp->regs.mst_mode = hfc_MST_MODE_MASTER; /* HFC Master Mode */ + hfc_outb(hfctmp, hfc_MST_MODE, hfctmp->regs.mst_mode); ++ hfc_outb(hfctmp, hfc_MST_EMOD, hfctmp->regs.mst_emod); + + hfc_outb(hfctmp, hfc_SCTRL, hfctmp->regs.sctrl); + hfctmp->regs.sctrl_r = 3; +@@ -212,10 +218,8 @@ + hfc_outb(hfctmp, hfc_CIRM, 0x80 | 0x40); // bit order + + /* Finally enable IRQ output */ +-#ifndef RTAITIMING + hfctmp->regs.int_m2 |= hfc_M2_IRQ_ENABLE; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); +-#endif + + /* clear pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); +@@ -232,370 +236,215 @@ + spin_unlock(®isterlock); + } + +-static void hfc_btrans(struct hfc_card *hfctmp, char whichB) { +- // we are called with irqs disabled from the irq handler +- int count, maxlen, total; +- unsigned char *f1, *f2; +- unsigned short *z1, *z2, newz1; +- int freebytes; +- +- if (whichB == 1) { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z2 + (*f1 * 4)); +- } else { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z2 + (*f1 * 4)); +- } +- +- freebytes = *z2 - *z1; +- if (freebytes <= 0) { +- freebytes += hfc_B_FIFO_SIZE; +- } +- count = ZT_CHUNKSIZE; +- +- total = count; +- if (freebytes < count) { +- hfctmp->clicks++; +- /* only spit out this warning once per second to not make things worse! */ +- if (hfctmp->clicks > 100) { +- printk(KERN_CRIT "zaphfc: bchan tx fifo full, dropping audio! (z1=%d, z2=%d)\n",*z1,*z2); +- hfctmp->clicks = 0; +- } +- return; +- } +- +- maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z1; +- if (maxlen > count) { +- maxlen = count; +- } +- newz1 = *z1 + total; +- if (newz1 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { newz1 -= hfc_B_FIFO_SIZE; } ++/*===========================================================================*/ + +- if (whichB == 1) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + *z1),hfctmp->ztdev->chans[0].writechunk, maxlen); +- } else { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + *z1),hfctmp->ztdev->chans[1].writechunk, maxlen); +- } +- +- count -= maxlen; +- if (count > 0) { +- // Buffer wrap +- if (whichB == 1) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[0].writechunk+maxlen, count); +- } else { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[1].writechunk+maxlen, count); +- } +- } ++#if hfc_B_FIFO_SIZE%ZT_CHUNKSIZE ++#error hfc_B_FIFO_SIZE is not a multiple of ZT_CHUNKSIZE even though the code assumes this ++#endif ++ ++static void hfc_dch_init(struct hfc_card *hfctmp){ ++ struct dch *chtmp=&hfctmp->dch; + +- *z1 = newz1; /* send it now */ ++ chtmp->rx.f1.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DRX_F1); ++ chtmp->rx.f2.v=0x1f; ++ chtmp->rx.f2.z2.v=0x1ff; + +-// if (count > 0) printk(KERN_CRIT "zaphfc: bchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); +- return; ++ chtmp->tx.f1.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DTX_F1); ++ chtmp->tx.f1.v=0x1f; ++ chtmp->tx.f1.z1.v=0x1ff; ++ chtmp->tx.f2.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DTX_F2); + } + +-static void hfc_brec(struct hfc_card *hfctmp, char whichB) { +- // we are called with irqs disabled from the irq handler +- int count, maxlen, drop; +- volatile unsigned char *f1, *f2; +- volatile unsigned short *z1, *z2, newz2; +- int bytes = 0; +- +- if (whichB == 1) { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); +- } else { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); +- } ++static void hfc_bch_init(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; + +- bytes = *z1 - *z2; +- if (bytes < 0) { +- bytes += hfc_B_FIFO_SIZE; +- } +- count = ZT_CHUNKSIZE; +- +- if (bytes < ZT_CHUNKSIZE) { +-#ifndef RTAITIMING +- printk(KERN_CRIT "zaphfc: bchan rx fifo not enough bytes to receive! (z1=%d, z2=%d, wanted %d got %d), probably a buffer overrun.\n",*z1,*z2,ZT_CHUNKSIZE,bytes); +-#endif +- return; +- } ++ chtmp->checkcnt=0; ++ chtmp->fill_fifo=0; + +- /* allowing the buffering of hfc_BCHAN_BUFFER bytes of audio data works around irq jitter */ +- if (bytes > hfc_BCHAN_BUFFER + ZT_CHUNKSIZE) { +- /* if the system is too slow to handle it, we will have to drop it all (except 1 zaptel chunk) */ +- drop = bytes - ZT_CHUNKSIZE; +- hfctmp->clicks++; +- /* only spit out this warning once per second to not make things worse! */ +- if (hfctmp->clicks > 100) { +- printk(KERN_CRIT "zaphfc: dropped audio (z1=%d, z2=%d, wanted %d got %d, dropped %d).\n",*z1,*z2,count,bytes,drop); +- hfctmp->clicks = 0; +- } +- /* hm, we are processing the b chan data tooooo slowly... let's drop the lost audio */ +- newz2 = *z2 + drop; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; +- } +- *z2 = newz2; +- } ++ chtmp->rx.c[0].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1RX_Z1+0x1f*4); ++ chtmp->rx.c[0].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B1RX_ZOFF); ++ chtmp->rx.c[1].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2RX_Z1+0x1f*4); ++ chtmp->rx.c[1].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B2RX_ZOFF); ++ chtmp->rx.z2=hfc_B_SUB_VAL; ++ chtmp->rx.diff=0; + +- +- maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z2; +- if (maxlen > count) { +- maxlen = count; +- } +- if (whichB == 1) { +- memcpy(hfctmp->ztdev->chans[0].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + *z2), maxlen); +- } else { +- memcpy(hfctmp->ztdev->chans[1].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + *z2), maxlen); +- } +- newz2 = *z2 + count; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; ++ chtmp->tx.c[0].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1TX_Z1+0x1f*4); ++ chtmp->tx.c[0].z2p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1TX_Z2+0x1f*4); ++ chtmp->tx.c[0].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B1TX_ZOFF); ++ chtmp->tx.c[0].filled=0; ++ chtmp->tx.c[1].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2TX_Z1+0x1f*4); ++ chtmp->tx.c[1].z2p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2TX_Z2+0x1f*4); ++ chtmp->tx.c[1].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B2TX_ZOFF); ++ chtmp->tx.c[1].filled=0; ++ chtmp->tx.z1=hfc_B_SUB_VAL; ++ chtmp->tx.diff=0; ++ ++ hfc_dch_init(hfctmp); ++ ++ chtmp->initialized=0; ++} ++ ++static int hfc_bch_check(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; ++ int x,r; ++ ++ for(x=0;x<2;x++){ ++ chtmp->tx.c[x].filled=(chtmp->tx.z1-*chtmp->tx.c[x].z2p+hfc_B_FIFO_SIZE)%hfc_B_FIFO_SIZE; ++ chtmp->rx.c[x].filled=(*chtmp->rx.c[x].z1p-chtmp->rx.z2+hfc_B_FIFO_SIZE)%hfc_B_FIFO_SIZE; + } +- *z2 = newz2; +- +- count -= maxlen; +- if (count > 0) { +- // Buffer wrap +- if (whichB == 1) { +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); +- memcpy(hfctmp->ztdev->chans[0].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + hfc_B_SUB_VAL), count); +- } else { +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); +- memcpy(hfctmp->ztdev->chans[1].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + hfc_B_SUB_VAL), count); +- } +- newz2 = *z2 + count; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; ++ if(chtmp->fill_fifo){ ++ chtmp->checkcnt++; ++ chtmp->checkcnt%=ZT_CHUNKSIZE; ++ r=!chtmp->checkcnt; ++ }else{ ++ x=chtmp->tx.c[0].filled-chtmp->tx.c[1].filled; ++ if(abs(x-chtmp->tx.diff)>1){ ++ printk(KERN_CRIT "zaphfc[%d]: tx sync changed: %d, %d\n",hfctmp->cardno,chtmp->tx.c[0].filled,chtmp->tx.c[1].filled); ++ chtmp->tx.diff=x; + } ++ r=chtmp->tx.c[0].filled<=ZT_CHUNKSIZE*jitterbuffer&&chtmp->tx.c[1].filled<=ZT_CHUNKSIZE*jitterbuffer; + } ++ return(r); ++} + ++#define hfc_bch_inc_z(a,b) (a)=((a)-hfc_B_SUB_VAL+(b))%hfc_B_FIFO_SIZE+hfc_B_SUB_VAL + +- if (whichB == 1) { +- zt_ec_chunk(&hfctmp->ztdev->chans[0], hfctmp->ztdev->chans[0].readchunk, hfctmp->ztdev->chans[0].writechunk); +- } else { +- zt_ec_chunk(&hfctmp->ztdev->chans[1], hfctmp->ztdev->chans[1].readchunk, hfctmp->ztdev->chans[1].writechunk); ++static void hfc_bch_tx(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; ++ int x; ++ ++ for(x=0;x<2;x++) ++ memcpy((void *)(chtmp->tx.c[x].fifo_base+chtmp->tx.z1),hfctmp->ztdev->chans[x].writechunk,ZT_CHUNKSIZE); ++ hfc_bch_inc_z(chtmp->tx.z1,ZT_CHUNKSIZE); ++ if(chtmp->fill_fifo){ ++ chtmp->fill_fifo--; ++ }else if(chtmp->tx.c[0].filled<=1||chtmp->tx.c[1].filled<=1){ ++ chtmp->fill_fifo=jitterbuffer; ++ if(chtmp->initialized) ++ printk(KERN_CRIT "zaphfc[%d]: b channel buffer underrun: %d, %d\n",hfctmp->cardno,chtmp->tx.c[0].filled,chtmp->tx.c[1].filled); + } +- return; ++ if(!chtmp->fill_fifo) ++ for(x=0;x<2;x++)*chtmp->tx.c[x].z1p=chtmp->tx.z1; + } + +- +-static void hfc_dtrans(struct hfc_card *hfctmp) { +- // we are called with irqs disabled from the irq handler ++static void hfc_bch_rx(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; + int x; +- int count, maxlen, total; +- unsigned char *f1, *f2, newf1; +- unsigned short *z1, *z2, newz1; +- int frames, freebytes; + +- if (hfctmp->ztdev->chans[2].bytes2transmit == 0) { +- return; ++ x=chtmp->rx.c[0].filled-chtmp->rx.c[1].filled; ++ if(abs(x-chtmp->rx.diff)>1){ ++ printk(KERN_CRIT "zaphfc[%d]: rx sync changed: %d, %d\n",hfctmp->cardno,chtmp->rx.c[0].filled,chtmp->rx.c[1].filled); ++ chtmp->rx.diff=x; + } +- +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z2 + (*f1 * 4)); +- +- frames = (*f1 - *f2) & hfc_FMASK; +- if (frames < 0) { +- frames += hfc_MAX_DFRAMES + 1; ++ if(chtmp->rx.c[0].filled>=ZT_CHUNKSIZE&&chtmp->rx.c[1].filled>=ZT_CHUNKSIZE){ ++ if((chtmp->rx.c[0].filled>=ZT_CHUNKSIZE*(jitterbuffer+2)&&chtmp->rx.c[1].filled>=ZT_CHUNKSIZE*(jitterbuffer+2))||!chtmp->initialized){ ++ if(chtmp->initialized) ++ printk(KERN_CRIT "zaphfc[%d]: b channel buffer overflow: %d, %d\n",hfctmp->cardno,chtmp->rx.c[0].filled,chtmp->rx.c[1].filled); ++ hfc_bch_inc_z(chtmp->rx.z2,chtmp->rx.c[0].filled-chtmp->rx.c[0].filled%ZT_CHUNKSIZE-ZT_CHUNKSIZE); ++ chtmp->initialized=1; ++ } ++ for(x=0;x<2;x++){ ++ memcpy(hfctmp->ztdev->chans[x].readchunk,(void *)(chtmp->rx.c[x].fifo_base+chtmp->rx.z2),ZT_CHUNKSIZE); ++ zt_ec_chunk(&hfctmp->ztdev->chans[x],hfctmp->ztdev->chans[x].readchunk,hfctmp->ztdev->chans[x].writechunk); ++ } ++ hfc_bch_inc_z(chtmp->rx.z2,ZT_CHUNKSIZE); + } ++} + +- if (frames >= hfc_MAX_DFRAMES) { +- printk(KERN_CRIT "zaphfc: dchan tx fifo total number of frames exceeded!\n"); +- return; +- } ++/*===========================================================================*/ + +- freebytes = *z2 - *z1; +- if (freebytes <= 0) { +- freebytes += hfc_D_FIFO_SIZE; +- } +- count = hfctmp->ztdev->chans[2].bytes2transmit; +- +- total = count; +- if (freebytes < count) { +- printk(KERN_CRIT "zaphfc: dchan tx fifo not enough free bytes! (z1=%d, z2=%d)\n",*z1,*z2); +- return; +- } +- +- newz1 = (*z1 + count) & hfc_ZMASK; +- newf1 = ((*f1 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); // next frame +- +- if (count > 0) { +- if (debug) { +- printk(KERN_CRIT "zaphfc: card %d TX [ ", hfctmp->cardno); +- for (x=0; xdch; ++ u8 tx_f2_v; ++ u16 x; ++ ++ if(hfctmp->ztdev->chans[2].bytes2transmit){ ++ if(debug){ ++ printk(KERN_CRIT "zaphfc[%d]: card TX [ ",hfctmp->cardno); ++ for(x=0;xztdev->chans[2].bytes2transmit;x++){ + printk("%#2x ",hfctmp->dtransbuf[x]); + } +- if (hfctmp->ztdev->chans[2].eoftx == 1) { +- printk("] %d bytes\n", count); +- } else { +- printk("..] %d bytes\n", count); +- } +- } +- maxlen = hfc_D_FIFO_SIZE - *z1; +- if (maxlen > count) { +- maxlen = count; ++ printk("] %d bytes\n",hfctmp->ztdev->chans[2].bytes2transmit); + } +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF + *z1),hfctmp->ztdev->chans[2].writechunk, maxlen); +- count -= maxlen; +- if (count > 0) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF),(char *)(hfctmp->ztdev->chans[2].writechunk + maxlen), count); ++ tx_f2_v=*chtmp->tx.f2.p; ++ if(!(tx_f2_v-chtmp->tx.f1.v+hfc_MAX_DFRAMES+1-1)&(hfc_MAX_DFRAMES+1-1)){ ++ printk(KERN_CRIT "zaphfc[%d]: dchan tx fifo total number of frames exceeded!\n",hfctmp->cardno); ++ }else{ ++ if(((*(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z2+tx_f2_v*4)-chtmp->tx.f1.z1.v+hfc_D_FIFO_SIZE-1)&(hfc_D_FIFO_SIZE-1))ztdev->chans[2].bytes2transmit){ ++ printk(KERN_CRIT "zaphfc[%d]: dchan tx fifo not enough space for frame!\n",hfctmp->cardno); ++ }else{ ++ chtmp->tx.f1.v=((chtmp->tx.f1.v+1)&hfc_MAX_DFRAMES)|(hfc_MAX_DFRAMES+1); ++ x=min(hfctmp->ztdev->chans[2].bytes2transmit,hfc_D_FIFO_SIZE-chtmp->tx.f1.z1.v); ++ memcpy(hfctmp->fifos+hfc_FIFO_DTX_ZOFF+chtmp->tx.f1.z1.v,hfctmp->ztdev->chans[2].writechunk,x); ++ memcpy(hfctmp->fifos+hfc_FIFO_DTX_ZOFF,hfctmp->ztdev->chans[2].writechunk+x,hfctmp->ztdev->chans[2].bytes2transmit-x); ++ *(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z2+chtmp->tx.f1.v*4)=chtmp->tx.f1.z1.v; ++ chtmp->tx.f1.z1.v=(chtmp->tx.f1.z1.v+hfctmp->ztdev->chans[2].bytes2transmit+hfc_D_FIFO_SIZE)&(hfc_D_FIFO_SIZE-1); ++ *(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z1+chtmp->tx.f1.v*4)=chtmp->tx.f1.z1.v; ++ *chtmp->tx.f1.p=chtmp->tx.f1.v; ++ } + } + } +- +- *z1 = newz1; +- +- if (hfctmp->ztdev->chans[2].eoftx == 1) { +- *f1 = newf1; +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); +- *z1 = newz1; +- hfctmp->ztdev->chans[2].eoftx = 0; +- } +-// printk(KERN_CRIT "zaphfc: dchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); +- return; + } + +-/* receive a complete hdlc frame, skip broken or short frames */ +-static void hfc_drec(struct hfc_card *hfctmp) { +- int count=0, maxlen=0, framelen=0; +- unsigned char *f1, *f2, *crcstat; +- unsigned short *z1, *z2, oldz2, newz2; ++static void hfc_dch_rx(struct hfc_card *hfctmp){ ++ struct dch *chtmp=&hfctmp->dch; ++ u16 size; + + hfctmp->ztdev->chans[2].bytes2receive=0; +- hfctmp->ztdev->chans[2].eofrx = 0; +- +- /* put the received data into the zaptel buffer +- we'll call zt_receive() later when the timer fires. */ +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F2); +- +- if (*f1 == *f2) return; /* nothing received, strange eh? */ +- +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z1 + (*f2 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- +- /* calculate length of frame, including 2 bytes CRC and 1 byte STAT */ +- count = *z1 - *z2; +- +- if (count < 0) { +- count += hfc_D_FIFO_SIZE; /* ring buffer wrapped */ +- } +- count++; +- framelen = count; +- +- crcstat = (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z1); +- +- if ((framelen < 4) || (*crcstat != 0x0)) { +- /* the frame is too short for a valid HDLC frame or the CRC is borked */ +- printk(KERN_CRIT "zaphfc: empty HDLC frame or bad CRC received (framelen = %d, stat = %#x, card = %d).\n", framelen, *crcstat, hfctmp->cardno); +- oldz2 = *z2; +- *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ +- // recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- *z2 = (oldz2 + framelen) & hfc_ZMASK; +- hfctmp->drecinframe = 0; +- hfctmp->regs.int_drec--; +- /* skip short or broken frames */ +- hfctmp->ztdev->chans[2].bytes2receive = 0; +- return; +- } +- +- count -= 1; /* strip STAT */ +- hfctmp->ztdev->chans[2].eofrx = 1; +- +- if (count + *z2 <= hfc_D_FIFO_SIZE) { +- maxlen = count; +- } else { +- maxlen = hfc_D_FIFO_SIZE - *z2; ++ hfctmp->ztdev->chans[2].eofrx=0; ++ if(*chtmp->rx.f1.p==chtmp->rx.f2.v){ ++ hfctmp->regs.int_drec=0; ++ }else{ ++ size=((*(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DRX_Z1+chtmp->rx.f2.v*4)-chtmp->rx.f2.z2.v+hfc_D_FIFO_SIZE)&(hfc_D_FIFO_SIZE-1))+1; ++ if(size<4){ ++ printk(KERN_CRIT "zaphfc[%d]: empty HDLC frame received.\n",hfctmp->cardno); ++ }else{ ++ u16 x=min(size,(u16)(hfc_D_FIFO_SIZE-chtmp->rx.f2.z2.v)); ++ memcpy(hfctmp->drecbuf,hfctmp->fifos+hfc_FIFO_DRX_ZOFF+chtmp->rx.f2.z2.v,x); ++ memcpy(hfctmp->drecbuf+x,hfctmp->fifos+hfc_FIFO_DRX_ZOFF,size-x); ++ if(hfctmp->drecbuf[size-1]){ ++ printk(KERN_CRIT "zaphfc[%d]: received d channel frame with bad CRC.\n",hfctmp->cardno); ++ }else{ ++ hfctmp->ztdev->chans[2].bytes2receive=size-1; ++ hfctmp->ztdev->chans[2].eofrx=1; ++ } ++ } ++ chtmp->rx.f2.z2.v=(chtmp->rx.f2.z2.v+size)&(hfc_D_FIFO_SIZE-1); ++ chtmp->rx.f2.v=((chtmp->rx.f2.v+1)&hfc_MAX_DFRAMES)|(hfc_MAX_DFRAMES+1); + } +- +- /* copy first part */ +- memcpy(hfctmp->drecbuf, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z2), maxlen); +- hfctmp->ztdev->chans[2].bytes2receive += maxlen; +- +- count -= maxlen; +- if (count > 0) { +- /* ring buffer wrapped, copy rest from start of d fifo */ +- memcpy(hfctmp->drecbuf + maxlen, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF), count); +- hfctmp->ztdev->chans[2].bytes2receive += count; +- } +- +- /* frame read */ +- oldz2 = *z2; +- newz2 = (oldz2 + framelen) & hfc_ZMASK; +- *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ +- /* recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! */ +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- *z2 = newz2; +- hfctmp->drecinframe = 0; +- hfctmp->regs.int_drec--; + } + +-#ifndef RTAITIMING +-ZAP_IRQ_HANDLER(hfc_interrupt) { +- struct hfc_card *hfctmp = dev_id; +- unsigned long flags = 0; +- unsigned char stat; ++/*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ ++ ++ZAP_IRQ_HANDLER(hfc_interrupt) { +-#else +-static void hfc_service(struct hfc_card *hfctmp) { +-#endif ++ struct hfc_card *hfctmp = dev_id; ++ struct hfc_card *hfctmp2; + struct zt_hfc *zthfc; +- unsigned char s1, s2, l1state; ++ unsigned char stat, s1, s2, l1state; ++ unsigned long flags; ++ unsigned long flags2=0; + int x; + + if (!hfctmp) { +-#ifndef RTAITIMING + #ifdef LINUX26 + return IRQ_NONE; + #else + return; + #endif +-#else +- /* rtai */ +- return; +-#endif + } + + if (!hfctmp->pci_io) { + printk(KERN_WARNING "%s: IO-mem disabled, cannot handle interrupt\n", + __FUNCTION__); +-#ifndef RTAITIMING + #ifdef LINUX26 + return IRQ_NONE; + #else + return; + #endif +-#else +- /* rtai */ +- return; +-#endif + } +- +- /* we assume a few things in this irq handler: +- - the hfc-pci will only generate "timer" irqs (proc/non-proc) +- - we need to use every 8th IRQ (to generate 1khz timing) +- OR +- - if we use rtai for timing the hfc-pci will not generate ANY irq, +- instead rtai will call this "fake" irq with a 1khz realtime timer. :) +- - rtai will directly service the card, not like it used to by triggering +- the linux irq +- */ + +-#ifndef RTAITIMING + spin_lock_irqsave(&hfctmp->lock, flags); + stat = hfc_inb(hfctmp, hfc_STATUS); +- + if ((stat & hfc_STATUS_ANYINT) == 0) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&hfctmp->lock,flags); +@@ -605,8 +458,6 @@ + return; + #endif + } +-#endif +- + s1 = hfc_inb(hfctmp, hfc_INT_S1); + s2 = hfc_inb(hfctmp, hfc_INT_S2); + if (s1 != 0) { +@@ -625,18 +476,10 @@ + } + switch (l1state) { + case 3: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d)", hfctmp->cardno, l1state); +-#endif + break; + default: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d)", hfctmp->cardno, l1state); +-#endif + } + if (l1state == 2) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_ACTIVATE | hfc_STATES_DO_ACTION | hfc_STATES_NT_G2_G3); +@@ -650,18 +493,10 @@ + } + switch (l1state) { + case 7: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d)", hfctmp->cardno, l1state); +-#endif + break; + default: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d)", hfctmp->cardno, l1state); +-#endif + } + if (l1state == 3) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); +@@ -671,7 +506,7 @@ + } + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) +- hfctmp->regs.int_drec++; ++ hfctmp->regs.int_drec = 1; + // mr. zapata there is something for you! + // printk(KERN_CRIT "d chan rx\n"); + } +@@ -692,14 +527,10 @@ + // B1 chan TX (bit 0) + } + } +-#ifdef RTAITIMING +- /* fake an irq */ +- s2 |= hfc_M2_PROC_TRANS; +-#endif + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) +- printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); ++ // printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); + } + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) +@@ -707,32 +538,30 @@ + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } +- if (s2 & hfc_M2_PROC_TRANS) { ++ if((s2&hfc_M2_PROC_TRANS)&&(hfctmp->cardno==timer_card)){ + // processing/non-processing transition (bit 0) +- hfctmp->ticks++; +-#ifndef RTAITIMING +- if (hfctmp->ticks > 7) { +- // welcome to zaptel timing :) +-#endif +- hfctmp->ticks = 0; +- ++ hfctmp2=hfctmp; ++ hfctmp=hfc_dev_list; ++ while(hfctmp){ ++ if(hfctmp->active){ ++ if(hfctmp!=hfctmp2)spin_lock_irqsave(&hfctmp->lock, flags2); ++ if(hfc_bch_check(hfctmp)){ + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + // clear dchan buffer ++ // memset(hfctmp->drecbuf, 0x0, sizeof(hfctmp->drecbuf)); ++ + hfctmp->ztdev->chans[2].bytes2transmit = 0; + hfctmp->ztdev->chans[2].maxbytes2transmit = hfc_D_FIFO_SIZE; + + zt_transmit(&(hfctmp->ztdev->span)); + +- hfc_btrans(hfctmp,1); +- hfc_btrans(hfctmp,2); +- hfc_dtrans(hfctmp); ++ hfc_bch_tx(hfctmp); ++ hfc_dch_tx(hfctmp); + } +- +- hfc_brec(hfctmp,1); +- hfc_brec(hfctmp,2); +- if (hfctmp->regs.int_drec > 0) { ++ hfc_bch_rx(hfctmp); ++ if (hfctmp->regs.int_drec) { + // dchan data to read +- hfc_drec(hfctmp); ++ hfc_dch_rx(hfctmp); + if (hfctmp->ztdev->chans[2].bytes2receive > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d RX [ ", hfctmp->cardno); +@@ -757,19 +586,18 @@ + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + zt_receive(&(hfctmp->ztdev->span)); + } +- +-#ifndef RTAITIMING + } +-#endif ++ if(hfctmp!=hfctmp2)spin_unlock_irqrestore(&hfctmp->lock,flags2); ++ } ++ hfctmp=hfctmp->next; ++ } ++ hfctmp=hfctmp2; + } +- + } +-#ifndef RTAITIMING + spin_unlock_irqrestore(&hfctmp->lock,flags); + #ifdef LINUX26 + return IRQ_RETVAL(1); + #endif +-#endif + } + + +@@ -826,22 +654,21 @@ + } + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + +- if (!alreadyrunning) { +- span->chans[2].flags &= ~ZT_FLAG_HDLC; +- span->chans[2].flags |= ZT_FLAG_BRIDCHAN; +- +- span->flags |= ZT_FLAG_RUNNING; ++ if (alreadyrunning) return 0; + +- hfctmp->ticks = -2; +- hfctmp->clicks = 0; +- hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; +- hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); +- } else { +- return 0; +- } ++ span->chans[2].flags &= ~ZT_FLAG_HDLC; ++ span->chans[2].flags |= ZT_FLAG_BRIDCHAN; ++ ++ span->flags |= ZT_FLAG_RUNNING; ++ ++ hfctmp->ticks = -2; ++ hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; ++ hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + ++ hfc_bch_init(hfctmp); + // drivers, start engines! + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); ++ hfctmp->active=1; + return 0; + } + +@@ -871,17 +698,9 @@ + + sprintf(zthfc->span.name, "ZTHFC%d", hfc_dev_count + 1); + if (hfctmp->regs.nt_mode == 1) { +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] [realtime]", hfc_dev_count + 1); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT]", hfc_dev_count + 1); +-#endif + } else { +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] [realtime]", hfc_dev_count + 1); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE]", hfc_dev_count + 1); +-#endif + } + + zthfc->span.spanconfig = zthfc_spanconfig; +@@ -918,32 +737,6 @@ + return 0; + } + +-#ifdef RTAITIMING +-#define TICK_PERIOD 1000000 +-#define TICK_PERIOD2 1000000000 +-#define TASK_PRIORITY 1 +-#define STACK_SIZE 10000 +- +-static RT_TASK rt_task; +-static struct hfc_card *rtai_hfc_list[hfc_MAX_CARDS]; +-static unsigned char rtai_hfc_counter = 0; +- +-static void rtai_register_hfc(struct hfc_card *hfctmp) { +- rtai_hfc_list[rtai_hfc_counter++] = hfctmp; +-} +- +-static void rtai_loop(int t) { +- int i=0; +- for (;;) { +- for (i=0; i < rtai_hfc_counter; i++) { +- if (rtai_hfc_list[i] != NULL) +- hfc_service(rtai_hfc_list[i]); +- } +- rt_task_wait_period(); +- } +-} +-#endif +- + int hfc_findCards(int pcivendor, int pcidevice, char *vendor_name, char *card_name) { + struct pci_dev *tmp; + struct hfc_card *hfctmp = NULL; +@@ -959,9 +752,9 @@ + } + pci_set_master(tmp); + +- hfctmp = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); ++ hfctmp = vmalloc(sizeof(struct hfc_card)); + if (!hfctmp) { +- printk(KERN_WARNING "zaphfc: unable to kmalloc!\n"); ++ printk(KERN_WARNING "zaphfc: unable to vmalloc!\n"); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; +@@ -969,6 +762,7 @@ + memset(hfctmp, 0x0, sizeof(struct hfc_card)); + spin_lock_init(&hfctmp->lock); + ++ hfctmp->active=0; + hfctmp->pcidev = tmp; + hfctmp->pcibus = tmp->bus->number; + hfctmp->pcidevfn = tmp->devfn; +@@ -982,44 +776,34 @@ + hfctmp->pci_io = (char *) tmp->resource[1].start; + if (!hfctmp->pci_io) { + printk(KERN_WARNING "zaphfc: no iomem!\n"); +- kfree(hfctmp); ++ vfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -1; + } +- +- hfctmp->fifomem = kmalloc(65536, GFP_KERNEL); +- if (!hfctmp->fifomem) { +- printk(KERN_WARNING "zaphfc: unable to kmalloc fifomem!\n"); +- kfree(hfctmp); ++ ++ hfctmp->fifos=(void *)__get_free_pages(GFP_KERNEL,log2(hfc_FIFO_MEM_SIZE_PAGES)); ++ if (!hfctmp->fifos) { ++ printk(KERN_WARNING "zaphfc: unable to __get_free_pages fifomem!\n"); ++ vfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } else { +- memset(hfctmp->fifomem, 0x0, 65536); +- hfctmp->fifos = (void *)(((ulong) hfctmp->fifomem) & ~0x7FFF) + 0x8000; + pci_write_config_dword(hfctmp->pcidev, 0x80, (u_int) virt_to_bus(hfctmp->fifos)); + hfctmp->pci_io = ioremap((ulong) hfctmp->pci_io, 256); + } + +-#ifdef RTAITIMING +- /* we need no stinking irq */ +- hfctmp->irq = 0; +-#else + if (request_irq(hfctmp->irq, &hfc_interrupt, ZAP_IRQ_SHARED, "zaphfc", hfctmp)) { + printk(KERN_WARNING "zaphfc: unable to register irq\n"); +- kfree(hfctmp->fifomem); +- kfree(hfctmp); ++ free_pages((unsigned long)hfctmp->fifos,log2(hfc_FIFO_MEM_SIZE_PAGES)); ++ vfree(hfctmp); + iounmap((void *) hfctmp->pci_io); + pci_disable_device(tmp); + multi_hfc = NULL; + return -EIO; + } +-#endif + +-#ifdef RTAITIMING +- rtai_register_hfc(hfctmp); +-#endif + printk(KERN_INFO + "zaphfc: %s %s configured at mem %lx fifo %lx(%#x) IRQ %d HZ %d\n", + vendor_name, card_name, +@@ -1041,11 +825,21 @@ + hfctmp->regs.nt_mode = 0; + } + +- zthfc = kmalloc(sizeof(struct zt_hfc),GFP_KERNEL); ++ if(sync_slave&(1<regs.mst_mode=hfc_MST_MODE_SLAVE|hfc_MST_MODE_F0_LONG_DURATION; ++ hfctmp->regs.mst_emod=hfc_MST_EMOD_SLOW_CLOCK_ADJ; ++ }else{ ++ printk(KERN_INFO "zaphfc: Card %d configured for master mode\n",hfc_dev_count); ++ hfctmp->regs.mst_mode=hfc_MST_MODE_MASTER|hfc_MST_MODE_F0_LONG_DURATION; ++ hfctmp->regs.mst_emod=0; ++ } ++ ++ zthfc = vmalloc(sizeof(struct zt_hfc)); + if (!zthfc) { +- printk(KERN_CRIT "zaphfc: unable to kmalloc!\n"); ++ printk(KERN_CRIT "zaphfc: unable to vmalloc!\n"); + hfc_shutdownCard(hfctmp); +- kfree(hfctmp); ++ vfree(hfctmp); + multi_hfc = NULL; + return -ENOMEM; + } +@@ -1079,58 +872,42 @@ + return 0; + } + +- +- + int init_module(void) { + int i = 0; +-#ifdef RTAITIMING +- RTIME tick_period; +- for (i=0; i < hfc_MAX_CARDS; i++) { +- rtai_hfc_list[i] = NULL; ++ if(jitterbuffer<1){ ++ printk(KERN_INFO "zaphfc: invalid jitterbuffer size specified: %d - changing to minimum of 1\n",jitterbuffer); ++ jitterbuffer=1; ++ }else if(jitterbuffer>500){ ++ printk(KERN_INFO "zaphfc: invalid jitterbuffer size specified: %d - changing to maximum of 500\n",jitterbuffer); ++ jitterbuffer=500; + } +- rt_set_periodic_mode(); +-#endif +- i = 0; ++ printk(KERN_INFO "zaphfc: jitterbuffer size: %d\n",jitterbuffer); + while (id_list[i].vendor_id) { + multi_hfc = NULL; + hfc_findCards(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_name, id_list[i].card_name); + i++; + } +-#ifdef RTAITIMING +- for (i=0; i < hfc_MAX_CARDS; i++) { +- if (rtai_hfc_list[i]) { +- printk(KERN_INFO +- "zaphfc: configured %d at mem %#x fifo %#x(%#x) for realtime servicing\n", +- rtai_hfc_list[i]->cardno, +- (u_int) rtai_hfc_list[i]->pci_io, +- (u_int) rtai_hfc_list[i]->fifos, +- (u_int) virt_to_bus(rtai_hfc_list[i]->fifos)); +- +- } +- } +- rt_task_init(&rt_task, rtai_loop, 1, STACK_SIZE, TASK_PRIORITY, 0, 0); +- tick_period = start_rt_timer(nano2count(TICK_PERIOD)); +- rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); +-#endif + printk(KERN_INFO "zaphfc: %d hfc-pci card(s) in this box.\n", hfc_dev_count); + return 0; + } + + void cleanup_module(void) { + struct hfc_card *tmpcard; +-#ifdef RTAITIMING +- stop_rt_timer(); +- rt_task_delete(&rt_task); +-#endif ++ + printk(KERN_INFO "zaphfc: stop\n"); + // spin_lock(®isterlock); ++ tmpcard=hfc_dev_list; ++ while(tmpcard){ ++ hfc_shutdownCard1(tmpcard); ++ tmpcard=tmpcard->next; ++ } + while (hfc_dev_list != NULL) { + if (hfc_dev_list == NULL) break; +- hfc_shutdownCard(hfc_dev_list); ++ hfc_shutdownCard2(hfc_dev_list); + tmpcard = hfc_dev_list; + hfc_dev_list = hfc_dev_list->next; + if (tmpcard != NULL) { +- kfree(tmpcard); ++ vfree(tmpcard); + tmpcard = NULL; + printk(KERN_INFO "zaphfc: freed one card.\n"); + } +@@ -1141,11 +918,17 @@ + + + #ifdef LINUX26 +-module_param(modes, int, 0600); ++module_param(modes, int, 0400); + module_param(debug, int, 0600); ++module_param(sync_slave, int, 0400); ++module_param(timer_card, int, 0400); ++module_param(jitterbuffer, int, 0400); + #else + MODULE_PARM(modes,"i"); + MODULE_PARM(debug,"i"); ++MODULE_PARM(sync_slave,"i"); ++MODULE_PARM(timer_card,"i"); ++MODULE_PARM(jitterbuffer,"i"); + #endif + + MODULE_DESCRIPTION("HFC-S PCI A Zaptel Driver"); +@@ -1153,3 +936,6 @@ + #ifdef MODULE_LICENSE + MODULE_LICENSE("GPL"); + #endif ++ ++/* vim:set sw=4: */ ++ +diff -urN bristuff-0.4.0-test1/zaphfc/zaphfc.h zaphfc_0.4.0-test1_florz-13/zaphfc.h +--- bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.h 2005-02-26 23:30:32.000000000 +0100 ++++ zaphfc_0.4.0-test1_florz-13/kernel/zaphfc/zaphfc.h 2005-03-02 20:43:04.000000000 +0100 +@@ -135,8 +135,12 @@ + /* bits in HFCD_MST_MODE */ + #define hfc_MST_MODE_MASTER 0x01 + #define hfc_MST_MODE_SLAVE 0x00 ++#define hfc_MST_MODE_F0_LONG_DURATION 0x08 + /* remaining bits are for codecs control */ + ++/* bits in HFCD_MST_EMOD */ ++#define hfc_MST_EMOD_SLOW_CLOCK_ADJ 0x01 ++ + /* bits in HFCD_SCTRL */ + #define hfc_SCTRL_B1_ENA 0x01 + #define hfc_SCTRL_B2_ENA 0x02 +@@ -236,6 +240,9 @@ + #define hfc_BTRANS_THRESHOLD 128 + #define hfc_BTRANS_THRESMASK 0x00 + ++#define hfc_FIFO_MEM_SIZE_BYTES (32*1024) ++#define hfc_FIFO_MEM_SIZE_PAGES ((hfc_FIFO_MEM_SIZE_BYTES+PAGE_SIZE-1)/PAGE_SIZE) ++ + /* Structures */ + + typedef struct hfc_regs { +@@ -249,20 +256,67 @@ + unsigned char connect; + unsigned char trm; + unsigned char mst_mode; ++ unsigned char mst_emod; + unsigned char bswapped; + unsigned char nt_mode; + unsigned char int_drec; + } hfc_regs; + ++struct bch { ++ int fill_fifo,checkcnt,initialized; ++ struct { ++ u16 z2; ++ struct { ++ volatile u16 *z1p; ++ volatile u8 *fifo_base; ++ int filled; ++ } c[2]; ++ int diff; ++ } rx; ++ struct { ++ u16 z1; ++ struct { ++ volatile u16 *z1p,*z2p; ++ volatile u8 *fifo_base; ++ int filled; ++ } c[2]; ++ int diff; ++ } tx; ++}; ++ ++struct dch { ++ struct { ++ struct { ++ volatile u8 *p; ++ } f1; ++ struct { ++ u8 v; ++ struct { ++ u16 v; ++ } z2; ++ } f2; ++ } rx; ++ struct { ++ struct { ++ u8 v; ++ volatile u8 *p; ++ struct { ++ u16 v; ++ } z1; ++ } f1; ++ struct { ++ volatile u8 *p; ++ } f2; ++ } tx; ++}; ++ + typedef struct hfc_card { + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; +- int clicks; + unsigned char *pci_io; +- void *fifomem; // start of the shared mem +- volatile void *fifos; // 32k aligned mem for the fifos ++ void *fifos; // 32k aligned mem for the fifos + struct hfc_regs regs; + unsigned int pcibus; + unsigned int pcidevfn; +@@ -274,6 +328,9 @@ + unsigned char brecbuf[2][ZT_CHUNKSIZE]; + unsigned char btransbuf[2][ZT_CHUNKSIZE]; + unsigned char cardno; ++ int active; ++ struct bch bch; ++ struct dch dch; + struct hfc_card *next; + } hfc_card; + +@@ -284,6 +341,5 @@ + struct hfc_card *card; + } zt_hfc; + +-/* tune this */ +-#define hfc_BCHAN_BUFFER 8 +-#define hfc_MAX_CARDS 8 ++/* vim:set sw=4: */ ++ --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/zt_alarm_notify_no_master_change +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/zt_alarm_notify_no_master_change @@ -0,0 +1,63 @@ +# This patch adds an extra function to Zaptel to set a span ini alarm +# yet still not look for a new Zaptel sync master. +# +# Currently only used by qozap. + +@DPATCH@ +Index: zaptel-1.2.22/kernel/zaptel.h +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel.h 2007-12-04 10:40:30.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel.h 2007-12-04 14:35:14.545043151 +0100 +@@ -1508,6 +1523,9 @@ + /* Notify a change possible change in alarm status */ + void zt_alarm_notify(struct zt_span *span); + ++/* Notify a change possible change in alarm status, DONT change the zaptel master! */ ++extern void zt_alarm_notify_no_master_change(struct zt_span *span); ++ + /* Initialize a tone state */ + void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt); + +Index: zaptel-1.2.22/kernel/zaptel-base.c +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel-base.c 2007-12-04 10:40:31.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel-base.c 2007-12-04 14:35:14.553042850 +0100 +@@ -138,6 +138,7 @@ + EXPORT_SYMBOL(zt_qevent_lock); + EXPORT_SYMBOL(zt_hooksig); + EXPORT_SYMBOL(zt_alarm_notify); ++EXPORT_SYMBOL(zt_alarm_notify_no_master_change); + EXPORT_SYMBOL(zt_set_dynamic_ioctl); + EXPORT_SYMBOL(zt_ec_chunk); + EXPORT_SYMBOL(zt_ec_span); +@@ -2783,6 +2788,30 @@ + } + } + ++void zt_alarm_notify_no_master_change(struct zt_span *span) ++{ ++ int j; ++ int x; ++ ++ span->alarms &= ~ZT_ALARM_LOOPBACK; ++ /* Determine maint status */ ++ if (span->maintstat || span->mainttimer) ++ span->alarms |= ZT_ALARM_LOOPBACK; ++ /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON. ++ The expression (a != b) does *NOT* do the same thing ++ as ((!a) != (!b)) */ ++ /* if change in general state */ ++ if ((!span->alarms) != (!span->lastalarms)) { ++ if (span->alarms) ++ j = ZT_EVENT_ALARM; ++ else ++ j = ZT_EVENT_NOALARM; ++ span->lastalarms = span->alarms; ++ for (x=0;x < span->channels;x++) ++ zt_qevent_lock(&span->chans[x], j); ++ } ++} ++ + #define VALID_SPAN(j) do { \ + if ((j >= ZT_MAX_SPANS) || (j < 1)) \ + return -EINVAL; \ --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/bri_dchan +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/bri_dchan @@ -0,0 +1,161 @@ +# Translate the D channels to a standard channel data. +# The HFC chipset provides us the D channel as data, but +# Zaptel expects it as a standard channel with 1000 samples +# per second. + +--- a/include/dahdi/kernel.h ++++ b/include/dahdi/kernel.h +@@ -389,6 +389,13 @@ struct dahdi_chan { + int statcount; + int lastnumbufs; + #endif ++#ifdef CONFIG_DAHDI_BRI_DCHANS ++ int bytes2receive; ++ int maxbytes2transmit; /* size of the tx buffer in the card driver */ ++ int bytes2transmit; ++ int eofrx; ++ int eoftx; ++#endif + spinlock_t lock; + char name[40]; + /* Specified by DAHDI */ +@@ -693,6 +700,9 @@ enum { + DAHDI_FLAGBIT_LOOPED = 18, /*!< Loopback the receive data from the channel to the transmit */ + DAHDI_FLAGBIT_MTP2 = 19, /*!< Repeats last message in buffer and also discards repeating messages sent to us */ + DAHDI_FLAGBIT_HDLC56 = 20, /*!< Sets the given channel (if in HDLC mode) to use 56K HDLC instead of 64K */ ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ DAHDI_FLAGBIT_BRIDCHAN = 21, /*!< hardhdlc-like handling of the D channel */ ++#endif + }; + + /* map flagbits to flag masks */ +@@ -731,6 +741,7 @@ enum { + #define DAHDI_FLAG_LOOPED DAHDI_FLAG(LOOPED) + #define DAHDI_FLAG_MTP2 DAHDI_FLAG(MTP2) + #define DAHDI_FLAG_HDLC56 DAHDI_FLAG(HDLC56) ++#define DAHDI_FLAG_BRIDCHAN DAHDI_FLAG(BRIDCHAN) + + struct dahdi_span { + spinlock_t lock; +--- a/include/dahdi/dahdi_config.h ++++ b/include/dahdi/dahdi_config.h +@@ -167,4 +167,10 @@ + */ + /* #define OPTIMIZE_CHANMUTE */ + ++/* ++ * Uncomment the following for BRI D channels ++ * ++ */ ++#define CONFIG_DAHDI_BRI_DCHANS ++ + #endif +--- a/drivers/dahdi/dahdi-base.c ++++ b/drivers/dahdi/dahdi-base.c +@@ -6014,11 +6014,40 @@ static inline void __dahdi_getbuf_chunk( + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes -= left; ++#ifdef CONFIG_DAHDI_BRI_DCHANS ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ /* ++ * Let's get this right, we want to transmit complete frames only. ++ * The card driver will do the dirty HDLC work for us. ++ * txb (transmit buffer) is supposed to be big enough to store one frame ++ * we will make this as big as the D fifo (1KB or 2KB) ++ */ ++ ++ /* there are 'left' bytes in the user buffer left to transmit */ ++ left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf] - 2; ++ if (left > ms->maxbytes2transmit) { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], ms->maxbytes2transmit); ++ ms->writeidx[ms->outwritebuf] += ms->maxbytes2transmit; ++ txb += ms->maxbytes2transmit; ++ ms->bytes2transmit = ms->maxbytes2transmit; ++ ms->eoftx = 0; ++ } else { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); ++ ms->writeidx[ms->outwritebuf] += left + 2; ++ txb += left + 2; ++ ms->bytes2transmit = left; ++ ms->eoftx = 1; ++ } ++ bytes = 0; ++#endif + } else { + memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); + ms->writeidx[ms->outwritebuf]+=left; + txb += left; + bytes -= left; ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ ms->bytes2transmit=DAHDI_CHUNKSIZE; ++#endif + } + /* Check buffer status */ + if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) { +@@ -6077,6 +6106,17 @@ out in the later versions, and is put ba + /* Transmit a flag if this is an HDLC channel */ + if (ms->flags & DAHDI_FLAG_HDLC) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ // if (ms->bytes2transmit > 0) { ++ // txb += 2; ++ // ms->bytes2transmit -= 2; ++ bytes=0; ++ ms->eoftx = 1; ++// printk(KERN_CRIT "zaptel EOF(%d) bytes2transmit %d\n",ms->eoftx,ms->bytes2transmit); ++ // } ++ } ++#endif + #ifdef CONFIG_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) + netif_wake_queue(ztchan_to_dev(ms)); +@@ -6137,6 +6177,12 @@ out in the later versions, and is put ba + memset(txb, 0xFF, bytes); + } + bytes = 0; ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ ms->bytes2transmit = 0; ++ ms->eoftx = 0; ++ bytes = 0; ++#endif + } else { + memset(txb, DAHDI_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */ + bytes = 0; +@@ -6993,6 +7039,14 @@ static inline void __putbuf_chunk(struct + int res; + int left, x; + ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ bytes = ms->bytes2receive; ++ if (bytes < 1) return; ++// printk(KERN_CRIT "bytes2receive %d\n",ms->bytes2receive); ++ } ++#endif ++ + while(bytes) { + #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + skb = NULL; +@@ -7050,6 +7104,19 @@ static inline void __putbuf_chunk(struct + } + } + } ++#ifdef CONFIG_DAHDI_BRI_DCHANS ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); ++ rxb += left; ++ ms->readidx[ms->inreadbuf] += left; ++ bytes -= left; ++ if (ms->eofrx == 1) { ++ eof=1; ++ } ++// printk(KERN_CRIT "receiving %d bytes\n",ms->bytes2receive); ++ ms->bytes2receive = 0; ++ ms->eofrx = 0; ++#endif + } else { + /* Not HDLC */ + memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/patches/proc_read +++ dahdi-linux-2.2.0~dfsg~rc5/debian/patches/proc_read @@ -0,0 +1,40 @@ +# Adds an extra Zaptel span method: proc_read. +# This allows a span to add some arbitrary text at the end +# of the /proc/zaptel/NNN file. +# +# This is currently only used by cwain. +# +# My personal opinion is that such methods are not recommended. +# sysfs support should be used instead (Tzafrir). It is likely to break +# zapconf and genzaptelconf for drivers using it. + +Index: zaptel-1.2.22/kernel/zaptel.h +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel.h 2007-12-04 10:40:30.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel.h 2007-12-04 14:35:14.545043151 +0100 +@@ -1394,6 +1405,10 @@ + int watchcounter; + int watchstate; + #endif ++#ifdef CONFIG_PROC_FS ++ /* Allow subordinate drivers to print out their own stuff */ ++ int (*proc_read)(struct zt_span *span, char *start); ++#endif + }; + + struct zt_transcoder_channel { +Index: zaptel-1.2.22/kernel/zaptel-base.c +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel-base.c 2007-12-04 10:40:31.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel-base.c 2007-12-04 14:35:14.553042850 +0100 +@@ -584,6 +585,10 @@ + len += sprintf(page + len, "\tIRQ misses: %d\n", spans[span]->irqmisses); + len += sprintf(page + len, "\n"); + ++ if (spans[span]->proc_read) { ++ len += spans[span]->proc_read(spans[span], page + len); ++ } ++ + + for (x=1;x + #include ++#include + #include + #include + #include --- dahdi-linux-2.2.0~dfsg~rc5.orig/debian/backports/etch +++ dahdi-linux-2.2.0~dfsg~rc5/debian/backports/etch @@ -0,0 +1,5 @@ +#!/bin/sh + +# Etch has an older debhelper: +sed -i -e '/^Build-Depends:/s/\(debhelper\) ([^)]*),/\1,/' debian/control +echo 5 >debian/compat