--- wctdm.c	2008-03-27 22:17:46.000000000 +0100
+++ wctdm.c-mod	2008-05-03 12:20:59.000000000 +0200
@@ -193,6 +193,14 @@
 	BATTERY_LOST,
 };
 
+enum cid_hook_state {
+	CID_STATE_IDLE = 0,
+        CID_STATE_RING_ON,
+	CID_STATE_RING_OFF,
+	CID_STATE_WAIT_RING_FINISH
+};
+
+
 struct wctdm {
 	struct pci_dev *dev;
 	char *variety;
@@ -254,8 +262,13 @@
 	volatile unsigned int *writechunk;				/* Double-word aligned write memory */
 	volatile unsigned int *readchunk;				/* Double-word aligned read memory */
 	struct zt_chan chans[NUM_CARDS];
-};
 
+        char *cid_history_buf[NUM_CARDS];               /* used to support global cid */
+        int  cid_history_ptr[NUM_CARDS];
+        int  cid_history_clone_cnt[NUM_CARDS];
+        enum cid_hook_state cid_state[NUM_CARDS];
+
+};
 
 struct wctdm_desc {
 	char *name;
@@ -293,6 +306,10 @@
 static int fxstxgain = 0;
 static int fxsrxgain = 0;
 
+static int cidbeforering = 0;
+static int cidbuflen = 3000;    /* in msec, default 3000 */
+static int cidtimeout = 6*1000; /* in msec, default 6000 */
+
 static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane);
 
 static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints)
@@ -415,10 +432,60 @@
 			wc->chans[0].readchunk[x] = (readchunk[x]) & 0xff;
 #endif
 	}
+
+        if(cidbeforering)
+        {
+                for(x=0; x<NUM_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, ZT_CHUNKSIZE);
+                                                wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + ZT_CHUNKSIZE)%(cidbuflen * ZT_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], ZT_MAX_CHUNKSIZE);
+                                                        wc->cid_history_clone_cnt[x]--;
+                                                        wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + ZT_MAX_CHUNKSIZE)%(cidbuflen * ZT_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;x<wc->cards;x++)
 		ring_check(wc, x);
-#endif		
+#endif	
+
 	/* XXX We're wasting 8 taps.  We should get closer :( */
 	for (x = 0; x < NUM_CARDS; x++) {
 		if (wc->cardflag & (1 << x))
@@ -780,7 +847,15 @@
 						fxo->wasringing = 1;
 						if (debug)
 							printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
-						zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+                                                if(cidbeforering)
+                                                {
+                                                        if(wc->cid_state[card] == CID_STATE_IDLE)
+                                                                wc->cid_state[card] = CID_STATE_RING_ON;
+                                                        else
+                                                                zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+                                                }
+                                                else
+							zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
 					}
 					fxo->lastrdtx = res;
 					fxo->ringdebounce = 10;
@@ -789,7 +864,22 @@
 						fxo->wasringing = 0;
 						if (debug)
 							printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
-						zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+                                                if(cidbeforering)
+                                                {
+                                                        if(wc->cid_state[card] == CID_STATE_RING_ON)
+                                                        {
+                                                                zt_qevent_lock(&wc->chans[card], ZT_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;
+                                                                zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+                                                        }
+                                                }
+                                                else
+							zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
 					}
 				}
 			} else if (res && (fxo->battery == BATTERY_PRESENT)) {
@@ -803,7 +893,15 @@
 				if (fxo->ringdebounce >= ZT_CHUNKSIZE * ringdebounce) {
 					if (!fxo->wasringing) {
 						fxo->wasringing = 1;
-						zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+                                                if(cidbeforering)
+                                                {
+                                                        if(wc->cid_state[card] == CID_STATE_IDLE)
+                                                                wc->cid_state[card] = CID_STATE_RING_ON;
+                                                        else
+                                                                zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+                                                }
+                                                else
+							zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
 						if (debug)
 							printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
 					}
@@ -814,7 +912,22 @@
 				if (fxo->ringdebounce <= 0) {
 					if (fxo->wasringing) {
 						fxo->wasringing = 0;
-						zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+                                                if(cidbeforering)
+                                                {
+                                                        if(wc->cid_state[card] == CID_STATE_RING_ON)
+                                                        {
+                                                                zt_qevent_lock(&wc->chans[card], ZT_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;
+                                                                zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+                                                        }
+                                                }
+                                                else
+							zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
 						if (debug)
 							printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
 					}
@@ -1979,6 +2092,13 @@
 		case ZT_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], ZT_LIN2X(0, chan), cidbuflen * ZT_MAX_CHUNKSIZE);
+                        }
 			break;
 		case ZT_TXSIG_ONHOOK:
 			wc->mod[chan->chanpos - 1].fxo.offhook = 0;
@@ -2268,6 +2388,7 @@
 	struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data;
 	int x;
 	int y;
+	int j;
 	
 	for (x=0;x<WC_MAX_IFACES;x++)
 		if (!ifaces[x]) break;
@@ -2359,6 +2480,21 @@
 
 			}
 
+                        if(cidbeforering) 
+                        {               
+                                int len = cidbuflen * ZT_MAX_CHUNKSIZE;
+                                if(debug)
+                                        printk("cidbeforering support enabled, length is %d msec\n", cidbuflen);
+                                for (j = 0; j < NUM_CARDS; j++) 
+                                {
+                                        wc->cid_history_buf[j] = kmalloc(len, GFP_KERNEL);
+                                        wc->cid_history_ptr[j] = 0;
+                                        wc->cid_history_clone_cnt[j] = 0;
+                                        wc->cid_state[j] = CID_STATE_IDLE;
+                                }
+                        }
+
+
 			wctdm_post_initialize(wc);
 
 			/* Enable interrupts */
@@ -2387,6 +2523,13 @@
 	zt_unregister(&wc->span);
 	if (wc->freeregion)
 		release_region(wc->ioaddr, 0xff);
+        if(cidbeforering) 
+        {
+                int x;
+                for (x = 0; x < NUM_CARDS; x++) 
+                        kfree(wc->cid_history_buf[x]);
+        }
+
 	kfree(wc);
 	printk("Freed a Wildcard\n");
 }
@@ -2523,6 +2666,9 @@
 module_param(fxorxgain, int, 0600);
 module_param(fxstxgain, int, 0600);
 module_param(fxsrxgain, int, 0600);
+module_param(cidbeforering, int, 0600);
+module_param(cidbuflen, int, 0600);
+module_param(cidtimeout, int, 0600);
 #else
 MODULE_PARM(debug, "i");
 MODULE_PARM(loopcurrent, "i");
@@ -2545,6 +2691,9 @@
 MODULE_PARM(fxorxgain, "i");
 MODULE_PARM(fxstxgain, "i");
 MODULE_PARM(fxsrxgain, "i");
+MODULE_PARM(cidbeforering, "i");
+MODULE_PARM(cidbuflen, "i");
+MODULE_PARM(cidtimeout, "i");
 #endif
 MODULE_DESCRIPTION("Wildcard TDM400P Zaptel Driver");
 MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
