Index: dahdi-linux-2.1.0/drivers/dahdi/zaphfc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ dahdi-linux-2.1.0/drivers/dahdi/zaphfc.c	2008-12-10 12:46:14.000000000 +0200
@@ -0,0 +1,1129 @@
+/*
+ * 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 <kpj@junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#ifdef RTAITIMING
+#include <asm/io.h>
+#include <rtai.h>
+#include <rtai_sched.h>
+#include <rtai_fifos.h>
+#endif
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <dahdi/kernel.h>
+#include "zaphfc.h"
+
+#include <linux/moduleparam.h>
+
+#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 DEFINE_SPINLOCK(registerlock);
+
+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(&registerlock);
+    if (hfccard != NULL) {
+	hfccard->cardno = hfc_dev_count++;
+	hfccard->next = hfc_dev_list;
+	hfc_dev_list = hfccard;
+    }
+    spin_unlock(&registerlock);
+}
+
+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; x<count; 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;
+	}
+	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, IRQF_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(&registerlock);
+    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(&registerlock);
+}
+#endif
+
+
+module_param(modes, int, 0600);
+module_param(debug, int, 0600);
+
+MODULE_DESCRIPTION("HFC-S PCI A Zaptel Driver");
+MODULE_AUTHOR("Klaus-Peter Junghanns <kpj@junghanns.net>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif	
Index: dahdi-linux-2.1.0/drivers/dahdi/zaphfc.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ dahdi-linux-2.1.0/drivers/dahdi/zaphfc.h	2008-12-10 12:46:14.000000000 +0200
@@ -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 <kpj@junghanns.net>
+ *
+ * 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
