/*--------------------------------------------------------------------------*\

	FILE....: vtopenswitch.c
	TYPE....: Linux device driver
	AUTHOR..: Ben Kramer, 08/12/2005
	          Ron Lee 16 July 2006

	Kernel driver for the OpenSwitch12 cards to be used with new driver
	structure. Based on the old vpbhp driver and code from:
	    David Rowe, 2002, 2003
	    Peter Wintulich, 2003.


	 Copyright (C) 2005 - 2018 Voicetronix www.voicetronix.com.au
	 Copyright (C) 2006 - 2018, Ron Lee <ron@voicetronix.com.au>

         This library 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 library 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 library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

/* Driver constants */
#define DRIVER_DESCRIPTION  "Voicetronix OpenSwitch card driver"
#define DRIVER_AUTHOR       "Voicetronix <support@voicetronix.com.au>"

#define	NAME	  "vtopenswitch"
#define MAX_PORTS 12              // Maximum number of ports on each carrier
#define MAX_CARDS  4              // max number of V12PCI cards

#define PCI_VENDOR_SUBID_VOICETRONIX            0x5654
#define PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH12  0x3132
#define PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6   0x2036


#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>

#include "vtcommon.h"
#include "vtcore_ioctl.h"
#include "vtmodule.h"

#include "messages_dsp_pc.h"

// Backward compatibility for kernel < 2.6.20 or so.
#ifndef IRQF_SHARED
#define IRQF_SHARED SA_SHIRQ
#endif

//XXX
#define ASYNC_IRQ 1

#define SIZE_BUF        8096    // max size of V12PCI card block read/write
#define SIZE_FIFO       512     // size of FIFOs on prototype
#define SIZE_SW_FIFO    25600   // 100ms worth of samples
#define SIZE_HW_FIFO    8192    // Maximum amount of data available from HW FIFO

#define EEPROM_SIZE     64      // Eeprom size
#define EEPROM_CS       25      // control bits
#define EEPROM_CLK      24
#define EEPROM_RDBIT    27
#define EEPROM_WRBIT    26

#define IIC_SDA_CTL     6       // USER2 Pin function select
#define IIC_SDA_DIR     7       // USER2 Pin input/output select
#define IIC_SDA_IO      8       // USER2 Pin data in/out
#define IIC_SCL_CTL     9       // USER3 Pin function select
#define IIC_SCL_DIR     10      // USER3 Pin input/output select
#define IIC_SCL_IO      11      // USER3 Pin data in/out

#define PARTY_MAX	10	// Maximum number of parties
#define PARTY_MAXMEM	10	// Maximum number of parties members

#define TX_FIFO_FULL	0x02	// transmit-to-board buffer full
#define TX_FIFO_EMPTY	0x04	// transmit-to-board buffer empty
#define RX_FIFO_FULL	0x08	// transmit-to-board buffer full
#define RX_FIFO_EMPTY	0x10	// transmit-to-board buffer empty


#define PLXREG(addr)    (card->base0 + addr)

#define PLX_LAS0BRD	PLXREG(0x28)    // Bus region descriptor
#define PLX_INTCSR	PLXREG(0x4c)
#define PLX_CNTRL	PLXREG(0x50)


#define PIB(addr)       (card->base2 + addr)

#define NUM_OE          4       // 4 Output Enable registers
#define BITS_OE         8       // bits per OE register

#define TDM_OE_0	PIB(2)  // OE  7...0 (MSB..LSB)
#define TDM_OE_1	PIB(3)  // OE 15...8
#define TDM_OE_2	PIB(4)  // OE 23..16
#define TDM_OE_3	PIB(5)  // OE 31..24
#define CONTROL_REG	PIB(6)
#define HOOKRING_0	PIB(7)
#define HOOKRING_1	PIB(8)
#define HOOKRING_2	PIB(9)
#define TX_FIFO		PIB(10)
#define HW_FIFO		PIB(MAX_PORTS)


#define ON_HOOK_THRESH  40
#define OFF_HOOK_THRESH 50

#define HOOK_ONHOOK       0
#define HOOK_OFFHOOK      1
#define HOOK_MAYBE_ONHOOK 2
#define HOOK_MAYBE_FLASH  3

#define FLASH_THRESH    750 // max length of hook flash in ms (flash detector)
#define FLASH_DEBOUNCE  150

#define MEM             100 // sample memory of hook state

// maps port to addr, bit pairs for hook det for that port
static int hookmap[] = { 7,1, 7,3, 7,5, 7,7, 8,1, 8,3, 8,5, 8,7, 9,1, 9,3, 9,5, 9,7 };

typedef struct {
	unsigned short *base2;                   // V12PCI card memory
	char		hookmem[MAX_PORTS][MEM]; // hook state memories
	int		running[MAX_PORTS];      // running sum
	int		index[MAX_PORTS];        // current index of hook mem
	int		hook_state[MAX_PORTS];
	long		hook_time[MAX_PORTS];
} KHOOK;


/* Ring Detector */
#define	WINDOW_RING     200   /* no of SPERIODS to sample (200ms)       */
#define THRESH_RING     60    /* no. of H samples in window for RING    */
#define THRESH_SIL      0     /* no. of H samples in window for SIL     */

/* ring detector states */
#define RING_L          0     /* idle state                             */
#define COUNT_H         1     /* counting number of H samples           */
#define COUNT_L         2     /* counting number of L samples           */

typedef struct {
	unsigned short *base2;                 // V12PCI card memory
	int            ring_state[MAX_PORTS];  // ring det state   
	int            count[MAX_PORTS];
	int            counth[MAX_PORTS];
	int            ring_evt[MAX_PORTS];    // asserted for ring event
} KRING, *PKRING;

// maps port to addr, bit pairs for ring det for that port
int ringmap[] = { 7,0, 7,2, 7,4, 7,6, 8,0, 8,2, 8,4, 8,6, 9,0, 9,2, 9,4, 9,6 };


/* loop drop detector states */
#define DFRONT_L        0     /* front porch (L) for DWINL samples      */
#define DWAITH_WIN      1     /* wait while H for DWINH                 */
#define DBACK_L         2     /* back porch (L) for DWINL               */

/* loop drop detector constants */
#define DWINL           90    /* 90ms */
#define DWINH           40    /* 50ms */

typedef struct {
	unsigned short *base2;                  // V12PCI card memory
	int            ldrop_state[MAX_PORTS];  // ring det state   
	int            count[MAX_PORTS];
	int            ldrop_evt[MAX_PORTS];    // asserted for ld event
} KLDROP, *PKLDROP;


typedef struct {
	int on;     // ring cadence on period
	int off;    // ring cadence off period
} CADENCE;

typedef struct {
	int     ringing;    // flag ringing cadence in progress.
	int     on;         // flag ringer currently on or off.
	CADENCE cadence[2];
	int     ring_time;
} RINGER;


struct openswitch {
      #if ! ASYNC_IRQ
	struct pci_dev	*dev;
      #endif

	struct vtboard	board;
	struct channel  chans[MAX_PORTS];

	unsigned char   latch_bits[MAX_PORTS];
	int             cardnum;

	int	type;
	int	rev;                 // Revisions of each card
	int	oe[NUM_OE];
	int	irq;
	short	tmpbuf[2*SIZE_FIFO]; // holding b. for data from fifo
	short	tmpbufidx;           // how full is tmpbuf
	int 	HW_R_FRAME;          // Size of data block from card HW_FIFO
	int 	HW_W_FRAME;          // Size of data block to card HW_FIFO

	// translated base address of PLX9052 regions
	unsigned char  *base0; // 9052 config registers (mem mapped)
	unsigned long   base1; // 9052 config registers (io mapped)
	unsigned short *base2; // V12PCI card memory

	void	*khook;  // kernel mode hook detectors
	void	*kring;  // kernel mode ring detectors
	void	*kldrop; // kernel mode ring detectors
	int	hookringbytes[3];
	int	ringcount;        // Number of channels ringing on this card,
				  // max is 4, the dc-dc carks it with more

	RINGER	ringer[MAX_PORTS]; // The ringing status of the channel

      #ifndef NO_OSW_RING_SYNC
	unsigned short ringerreg; // Shadow register for the state of the ring generator
      #endif

	// buffer of samples to/from card
	short 	framerx[SIZE_FIFO];
	short 	frametx[SIZE_FIFO/2];

      #if ASYNC_IRQ
	unsigned long irq_count;
      #endif

} *cards[MAX_CARDS];

// You must hold this lock anytime you access or modify the cards[] array.
DEFINE_MUTEX(cards_mutex);


//XXX
#if ASYNC_IRQ
  #define int_count (card->irq_count)
#else
  static ulong	int_count=0;
#endif

static ulong	incomplete_frame_count=0;
static ulong	junked_samples_count=0;
static int	suppress_initial_frame_sync_warn = 50;

// Alaw <-> Linear stuff
#define ALAW_CODES 256
static unsigned char    bitrev_lut[ALAW_CODES];


static struct pci_device_id openswitch_idtable[] = {
	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
	  PCI_VENDOR_SUBID_VOICETRONIX, PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH12,
	  0, 0, 0 },
//	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
//	  PCI_VENDOR_SUBID_VOICETRONIX, PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6,
//	  0, 0, 0 },
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, openswitch_idtable);

static int openswitch_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent);
static void openswitch_remove_board(struct pci_dev *pdev);

static struct pci_driver vtopenswitch_driver = {
	.name     = NAME,
	.probe    = openswitch_probe_board,
	.remove   = openswitch_remove_board,
	.suspend  = NULL,
	.resume   = NULL,
	.id_table = openswitch_idtable
};


static bool shareirq = 1;
static int debug;

module_param(shareirq, bool, 0444);
module_param(debug, int, 0644);
MODULE_PARM_DESC(shareirq, "Enable (or disable) irq sharing");
MODULE_PARM_DESC(debug,    "Set the logging verbosity");


/* Convenience macros for logging */
#define cardinfo(card,format,...) info("[%d] " format, (card)->cardnum , ## __VA_ARGS__)
#define portinfo(card,port,format,...)                                        \
	info("[%d] %2d: " format, (card)->cardnum, port , ## __VA_ARGS__)
#define cardwarn(card,format,...) warn("[%d] " format, (card)->cardnum , ## __VA_ARGS__)
#define portwarn(card,port,format,...)                                        \
	warn("[%d] %2d: " format, (card)->cardnum, port , ## __VA_ARGS__)
#define cardcrit(card,format,...) crit("[%d] " format, (card)->cardnum , ## __VA_ARGS__)
#define portcrit(card,port,format,...)                                        \
	crit("[%d] %2d: " format, (card)->cardnum, port , ## __VA_ARGS__)
#define dbginfo(n,card,format,...) if(debug>=n) cardinfo((card),format, ## __VA_ARGS__)
#define dbgport(n,card,port,format,...)	if(debug>=n)			    \
	info("[%d] %2d: " format, (card)->cardnum, port , ## __VA_ARGS__)


//XXX Things still to refactor.
static void *khook_open(unsigned short *base2);
static void khook_close(void *khook);
static void khook_sample(struct openswitch *card);

static void *kring_open(unsigned short *base2);
static void kring_close(void *kring);
static void kring_sample(struct openswitch *card);
static int kring_read(void *kring, int ch);

static void *kldrop_open(unsigned short *base2);
static void kldrop_close(void *kldrop);
static void kldrop_sample(struct openswitch *card);
static int kldrop_read(void *kldrop, int ch);


/*--------------------------------------------------------------------------*\
      eeprom access routines
\*--------------------------------------------------------------------------*/

void inline cntrlbit(struct openswitch *card, int bit, int val)
{ //{{{
	iowrite32((ioread32(PLX_CNTRL) & ~(1<<bit)) | ((val&1)<<bit), PLX_CNTRL);
} //}}}

void inline wrbit(struct openswitch *card, int bit)
{ //{{{
	cntrlbit(card,EEPROM_WRBIT, bit);
	cntrlbit(card,EEPROM_CLK, 1);
	cntrlbit(card,EEPROM_CLK, 0);
} //}}}

int inline rdbit(struct openswitch *card)
{ //{{{
	cntrlbit(card,EEPROM_CLK, 1);
	cntrlbit(card,EEPROM_CLK, 0);
	return (ioread32(PLX_CNTRL) >> EEPROM_RDBIT) & 1;
} //}}}

// Read a single word of card data from eeprom.
unsigned int eeread(struct openswitch *card, int addr)
{ //{{{
	int i = 0, d = 0;

	cntrlbit(card,EEPROM_CS,1);
	cntrlbit(card,EEPROM_CLK,0);

	wrbit(card,1); wrbit(card,1); wrbit(card,0);
	for(; i<6; ++i) wrbit(card,addr>>(5-i));
	for(i=0; i<16; ++i){
		d <<= 1;
		d += rdbit(card);
	}
	cntrlbit(card,EEPROM_CS,0);

	return d;
} //}}}


int simple_atoi(char *s)
{ //{{{
	int val = 0;
	for(; *s >= '0' && *s <= '9'; val *= 10, val += *s - '0', ++s);
	return val;
} //}}}

static void read_id(struct openswitch *card)
{ //{{{
	const char      hex[17] = {"0123456789abcdef"};
	unsigned short  eebuf[20], *e = eebuf;
	char            st[16], *s = st;
	int             i = 49;

	while(i<56) { *e = eeread(card,++i); ++e; }

	e = eebuf;
	*s     = hex[(*e>>12)& 0x0f];	//day
	*(++s) = hex[(*e>>8)& 0x0f];
	*(++s) = '/';
	*(++s) = hex[(*e>>4)& 0x0f];	//month
	*(++s) = hex[*e & 0x0f];
	*(++s) = '/';
	++e;
	*(++s) = hex[(*e>>12)& 0x0f];	//year
	*(++s) = hex[(*e>>8)& 0x0f];
	*(++s) = hex[(*e>>4)& 0x0f];
	*(++s) = hex[*e & 0x0f];
	*(++s) = 0;
	++e;
	cardinfo(card, "  Manufactured %s",st);

	s = st;
	*s     = hex[(*e>>12)& 0x0f];	//card
	*(++s) = hex[(*e>>8)& 0x0f];
	*(++s) = 0;
	card->rev = simple_atoi(st) * 100;
	cardinfo(card, "  Card version %s",st);

	s = st;
	*s     = hex[(*e>>4)& 0x0f];	//Rev.
	*(++s) = hex[*e & 0x0f];
	*(++s) = 0;
	++e;
	card->rev += simple_atoi(st);
	cardinfo(card, "  Card revision %s",st);

	++e;				// Reserved word;

	s = card->board.serial;
	*s     = hex[(*e>>12)& 0x0f];	//SN:high
	*(++s) = hex[(*e>>8)& 0x0f];
	*(++s) = hex[(*e>>4)& 0x0f];
	*(++s) = hex[*e & 0x0f];
	++e;
	*(++s) = hex[(*e>>12)& 0x0f];	//SN:low
	*(++s) = hex[(*e>>8)& 0x0f];
	*(++s) = hex[(*e>>4)& 0x0f];
	*(++s) = hex[*e & 0x0f];
	*(++s) = 0;
	cardinfo(card, "  Serial number %s", card->board.serial);
} //}}}


// Write to a register on the TS5070 codec
static void write_reg(struct openswitch *card, int port, unsigned short cword)
{ //{{{
	int data_bit, i;
	/* Val => 7 6 5 4 3 2 1 0
		  | | | | \-----/ 
		  | | | |   \-> Chip select line
		  | | | \->  Enable Chip select??
		  | | \-> Data bit
		  | \-> Clock
		  \-> Codec Reset
	*/

	//XXX New for OS6 support.
	if( card->type == PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6 )
		port += 6;

	// Clock out the 16 bits of the unsigned short!
	for(i=0; i<16; i++) {
		data_bit = (cword >> (15-i)) & 0x1;
		iowrite16(port + (1<<4) + (data_bit<<5) + (1<<6), CONTROL_REG);
		iowrite16(port + (1<<4) + (data_bit<<5), CONTROL_REG);
	}
	iowrite16(0, CONTROL_REG);
} //}}}

// Receive a data byte from codec.
static int read_reg(struct openswitch *card, int port, unsigned short cword)
{ //{{{
	int            data_bit, i = 0, byte = 0;
	unsigned short read_data_bit;

	//XXX New for OS6 support.
	if( card->type == PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6 )
		port += 6;

	// write Codec port address to CI
	for(; i < 8; ++i){
		data_bit = (cword >> (15-i)) & 0x1;
		iowrite16(port + (1<<4) + (data_bit<<5) + (1<<6), CONTROL_REG);
		iowrite16(port + (1<<4) + (data_bit<<5), CONTROL_REG);
	}
	// Read codec data through CO
	for(i=0; i < 8; ++i){
		iowrite16(port + (1<<4) + (1<<6), CONTROL_REG);
		//  may need to add a delay here?
		read_data_bit = ioread16(CONTROL_REG);
		iowrite16(port + (1<<4), CONTROL_REG);
		byte = (byte<<1) + (read_data_bit & 0x01);   // assemble byte
	}
	iowrite16(0, CONTROL_REG);
	return byte;
} //}}}

static void set_codec_reg(struct openswitch *card,
			  int port, unsigned short reg, unsigned char value)
{ //{{{
	write_reg(card, port, (reg<<8) + value);
} //}}}

static int get_codec_reg(struct openswitch *card, int port, unsigned short reg)
{ //{{{
	// reg |= 0x4;        // ensure read bit set?  XXX ?
	return read_reg(card, port, (reg<<8));
} //}}}


// read data from hw fifo and place into frame buffer
void read_hw_fifo(struct openswitch *card)
{ //{{{
	int i,z,j=0,q=0;
	int collect_words = 1;
	int pframe_sync=0;
	int tmpbuf_total=0;
	int junked_samples=0;
	short *pbuf = card->tmpbuf;
	short *nbuf = card->framerx;

	// Sync empty flag. (Clock into FIFO Read logic causes the R_FE flag to update)
	ioread16(HOOKRING_2);

	// on entry i is available, j=0 and collects the byte count, z=0 is a tmp	
	// get all data from hw fifo buffer into tmpbuf (4 words at a time)
	while( collect_words && (card->tmpbufidx + j + 4) < 2*SIZE_FIFO ) {
		z = 0;  // Z only goes up for each word without a Fifo empty flag set
		ioread16_rep(HW_FIFO, pbuf + card->tmpbufidx + j, 4);

		// check each of 4 words for empty flag.
		for(i=0; i<4; i++) {
			// if FifoEmpty flagged then stop collecting words	
			if( *(pbuf + card->tmpbufidx + j + i) & 0x0200 ) {
				collect_words = 0;
				// dont add this word to the input streem (no change to z)
			}
			else {
				// some preceding words were empty 
				// so shift the valid ones down to fill the gaps    
				if( i != z ) {
					*(pbuf + card->tmpbufidx + j + z) =
					*(pbuf + card->tmpbufidx + j + i);
				}
				++z;
			}
		}
		// add count to the total
		j += z;
	}
	tmpbuf_total = j + card->tmpbufidx;

	// now lets find the first frame sync
	for(i=0; i< (card->tmpbufidx + j); ++i) {
		// if we find one
		if(pbuf[i]&0x0100) {
			pframe_sync=i;
			i=card->tmpbufidx + j + 10; // Set to exit loop
		}
	}
	if( i == card->tmpbufidx + j ) {
		if( suppress_initial_frame_sync_warn )
			--suppress_initial_frame_sync_warn;
		else
			cardwarn(card,"Couldnt find first frame sync");
	}

	// j will increment relative to time slot number.
	j = z = q = 0;
	nbuf = card->framerx; // always loaded with ch 0 at offset 0
	pbuf = card->tmpbuf;
	//pbuf+=pframe_sync;	// how do we deal with this??
	// find last frame sync in tmpbuf[bd]
	for(i=tmpbuf_total-1; i > 0 && q == 0; --i) if(pbuf[i] & 0x100) q=i;
	do{
		// Process slot 0
		if(j%32 == 0) {
			while ((!(pbuf[z] & 0x0100))&&(z<tmpbuf_total)){
				z++;
				junked_samples++;
			}
			// first check that there is enough data to fill a frame
			if((q>z) || (((z-1)+ card->HW_R_FRAME)<tmpbuf_total)) {
				// is the frame sync there?
				if(pbuf[z] & 0x0100){
					*(nbuf+j) = pbuf[z];
					++z;
				}
				++j;
			} else {
				//make negative to break out of do{}while
				q= -q;
			}
		}
		// Process all other slots in Rx Frame
		else if(((j%32) >0) && ((j%32) < card->HW_R_FRAME)) {
			if( ! (pbuf[z] & 0x0100) ){ // is the frame sync there?
				*(nbuf+j) = pbuf[z];
				z++;
			}
			j++;
		}
		// Process all slots past HW_R_FRAME[bd] frame size
		else if((j%32) >= card->HW_R_FRAME) {
			// Pad buffer out to 32nd slot.
			for(;(j%32)>0; j++) {
				*(nbuf+j)=0;
			}
			// on exit j will point to start of next frame (slot0)
		}
		// while we have data and have not recived too much
		//incomplete_frame_count=(incomplete_frame_count+junked_samples)/2;
		if (junked_samples > incomplete_frame_count)
			incomplete_frame_count=junked_samples;
		if (junked_samples >0)
			junked_samples_count++;
		junked_samples=0;
	}while((z < tmpbuf_total) && (j<SIZE_FIFO/2) && (q>0));

	// move unread words to start of tmpbuf for next isr
	for(j=0,i=z; i < tmpbuf_total; ++i) pbuf[j++] = pbuf[i];
	card->tmpbufidx = j;  // save word count for next isr
} //}}}


// Read the bytes used by the hook, ring and loop detection routines
static void read_hookringbytes(struct openswitch *card)
{ //{{{
	short *p = HOOKRING_0;
	int i = 0;

	for(; i < 3; ++p, ++i) card->hookringbytes[i] = ioread16(p);
	//XXX New for OS6 support.
	if( card->type == PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6 )
	{
		card->hookringbytes[0] = card->hookringbytes[1] >> 16
				       | card->hookringbytes[2] << 16;
		card->hookringbytes[1] = card->hookringbytes[2] >> 16;
		card->hookringbytes[2] = 0;
	}
      #ifndef NO_OSW_RING_SYNC
	card->ringerreg = ioread16(CONTROL_REG);
      #endif
} //}}}

static void set_ring_on(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "setting ring on");
	card->latch_bits[port] |= 0x80;
	write_reg(card, port, 0x0a00+card->latch_bits[port]);
	card->ringer[port].on = 1;
} //}}}

static void set_ring_off(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "setting ring off");
	card->latch_bits[port] &= ~0x80;
	write_reg(card, port, 0x0a00+card->latch_bits[port]);
	card->ringer[port].on = 0;
} //}}}

// station port - audio sw only
static void set_audio_on(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "setting audio on");
	card->latch_bits[port] |= 0x20;
	write_reg(card, port, 0x0a00+card->latch_bits[port]);
} //}}}

static void set_audio_off(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "setting audio off");
	card->latch_bits[port] &= ~0x20;
	write_reg(card, port, 0x0a00+card->latch_bits[port]);
} //}}}

static void do_ringing(struct openswitch *card)
{ //{{{
	/* 
	We need to check that the ring generator is at zero voltage.
	Then we can determine which ports should be ringing, with a max of
	4 ports ringing at one time.
	*/
	int i;

      #ifndef NO_OSW_RING_SYNC
	if( ! (card->ringerreg & (1<<5)) ) return;
      #endif

	for(i=0; i < MAX_PORTS; ++i){
		RINGER	*ringer = &card->ringer[i];

		if( ringer->ringing ){
			if( ringer->ring_time ){
				// Already ringing, Check ring time
				int timelapse = int_count - ringer->ring_time;
				int ring_on = ringer->cadence[0].on;
				int ring_off = ringer->cadence[0].off;
				int ring_on2 = ringer->cadence[1].on;
				int ring_off2 = ringer->cadence[1].off;
				if(timelapse > (ring_on + ring_off + ring_on2 + ring_off2)){
					// Finished this cycle
					ringer->ring_time = 0;
				} else if(timelapse > (ring_on + ring_off + ring_on2)){
					// End second ring-on cadence
					if( ringer->on ){
						set_ring_off(card,i);
						--card->ringcount;
					}
				} else if (timelapse > (ring_on + ring_off)){
					// Start second ring-on cadence
					if( ! ringer->on ){
						set_ring_on(card,i);
						//++card->ringcount;
					}
				} else if (timelapse > ring_on){
					// End first ring-on cadence
					if( ringer->on ){
						set_ring_off(card,i);
						//--card->ringcount;
					}
				} else {
					// Start first ring-on cadence
					if( ! ringer->on ){
						if(card->ringcount < 4){
							set_ring_on(card,i);
							++card->ringcount;
						}
					}
				}
			} else {
				// New request for ring
				if(card->ringcount < 4){
					ringer->ring_time = int_count;
					set_ring_on(card,i);
					++card->ringcount;
				}
			}
		} else if( ringer->on ){
			// We are still ringing, must stop it!
			set_ring_off(card,i);
			--card->ringcount;
		}
	}
} //}}}

static void detect_loop_drop(struct openswitch *card)
{ //{{{
	int  i;
	char mess[3] = { DSP_LDROP, DSP_DROP };

	for(i=0; i < MAX_PORTS; ++i) {
		if( card->chans[i].porttype == VT_FXO_AN
		 && kldrop_read(card->kldrop, i) )
		{
			dbgport(2,card,i,"msg LOOP_DROP");
			mess[2] = i;
			if( vt_send_event(&card->board, mess, DSP_LDROP) )
				portwarn(card,i,"Couldnt send LDROP event into MSGQ");
		}
	}
} //}}}

static void detect_ring(struct openswitch *card)
{ //{{{
	int	i;
	char	mess[DSP_LCODEC_RING];

	for(i=0; i < MAX_PORTS; ++i) {
	    if(card->chans[i].porttype == VT_FXO_AN){
		mess[2] = i;
		switch( kring_read(card->kring, i) ){
		    case 1:
			dbgport(2,card,i,"msg CODEC_RING");
			// generate event
			mess[0] = DSP_LCODEC_RING;
			mess[1] = DSP_CODEC_RING;
			if( vt_send_event(&card->board, mess, DSP_LCODEC_RING) )
				portwarn(card,i,"Couldnt send CODEC_RING to MSGQ");
			break;

		    case -1:
			dbgport(2,card,i,"msg RING_OFF");
			// generate event
			mess[0] = DSP_LRING_OFF;
			mess[1] = DSP_RING_OFF;
			if( vt_send_event(&card->board, mess, DSP_LRING_OFF) )
				portwarn(card,i,"Couldnt send RING_OFF to MSGQ");
		}
	    }
	}
} //}}}

static void detect_hook(struct openswitch *card)
{ //{{{
	int		i, state, next_state;
	int		running;
	char		mess[DSP_LCODEC_HKOFF];
	unsigned long	duration;
	KHOOK          *khook = card->khook;

	for(i=0; i < MAX_PORTS; ++i) {
		if (card->chans[i].porttype != VT_FXS_AN)
			continue;

		running = khook->running[i];

		state = khook->hook_state[i];
		next_state = khook->hook_state[i];

		// used to "tune" HOOK_THRESH during ring (unittest/ringstat)
		/* 
		if (i == 0 && running > 10)
			cardinfo(card, "running = %d state = %d \n", running, state);
		*/

		switch(state) {
		    case HOOK_ONHOOK:
			if (running > OFF_HOOK_THRESH) {
				dbgport(2,card,i,"msg CODEC_HKOFF");
				next_state = HOOK_OFFHOOK;

				// stop ringing
				card->ringer[i].ringing = 0;
				if( card->ringer[i].on ){
					set_ring_off(card,i);
					--card->ringcount;
				}
				set_audio_on(card, i);
				card->chans[i].state = CH_OFFHOOK;

				// generate event
				mess[0] = DSP_LCODEC_HKOFF;
				mess[1] = DSP_CODEC_HKOFF;
				mess[2] = i;
				if( vt_send_event(&card->board,mess,DSP_LCODEC_HKOFF) )
					portwarn(card,i,"Couldnt send CODEC_HKOFF to MSGQ");
			}
			break;

		    case HOOK_OFFHOOK:
			if (running < ON_HOOK_THRESH) {
				next_state = HOOK_MAYBE_ONHOOK;
				khook->hook_time[i] = int_count;
			}
			break;

		    case HOOK_MAYBE_ONHOOK:
			// check we are still on hook
			if (running < ON_HOOK_THRESH) {
				// if on hook for > max flash duration
				// must be a real on hook
				duration = int_count - khook->hook_time[i];
				if (duration > FLASH_THRESH) {
					dbgport(2,card,i,"msg CODEC_HKON");
					set_audio_off(card, i);
					card->chans[i].state = CH_IDLE;
					next_state = HOOK_ONHOOK;
					// generate event
					mess[0] = DSP_LCODEC_HKON;
					mess[1] = DSP_CODEC_HKON;
					mess[2] = i;
					if( vt_send_event(&card->board,mess,DSP_LCODEC_HKON) )
						portwarn(card,i,"Couldnt send CODEC_HKON to MSGQ");
				}
			} else {
				// maybe a flash
				khook->hook_time[i] = int_count;
				next_state = HOOK_MAYBE_FLASH;
			}
			break;

		    case HOOK_MAYBE_FLASH:
			duration = int_count - khook->hook_time[i];
			if (duration > FLASH_DEBOUNCE) {
				mess[2] = i;
				// check we are still on hook
				if (running < ON_HOOK_THRESH) {
					dbgport(2,card,i,"msg CODEC_HKON");
					set_audio_off(card, i);
					card->chans[i].state = CH_IDLE;
					next_state = HOOK_ONHOOK;
					// generate event
					mess[0] = DSP_LCODEC_HKON;
					mess[1] = DSP_CODEC_HKON;
					if( vt_send_event(&card->board,mess,DSP_LCODEC_HKON) )
						portwarn(card,i,"Couldnt send CODEC_HKON to MSGQ");
				} else {
					// else must have been a flash
					dbgport(2,card,i,"msg CODEC_FLASH");
					next_state = HOOK_OFFHOOK;
					// generate event
					mess[0] = DSP_LCODEC_FLASH;
					mess[1] = DSP_CODEC_FLASH;
					if( vt_send_event(&card->board,mess,DSP_LCODEC_FLASH) )
						portwarn(card,i,"Couldnt send CODEC_FLASH to MSGQ");
				}
			}
		}
		khook->hook_state[i] = next_state;
	}
} //}}}


#if ASYNC_IRQ
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
static irqreturn_t openswitch_isr(int irq, void *dev_id, struct pt_regs *regs)
#else
static irqreturn_t openswitch_isr(int irq, void *dev_id)
#endif
{ //{{{
	struct openswitch *card = dev_id;
	struct vtboard    *board;
	short             *pbuf = NULL;
	int                i = 0, j = 0;

	// check that this is really our interupt
	if( ! card ) return IRQ_NONE;
	for(; i < MAX_CARDS; ++i ) if( cards[i] == card ) goto ok;
	return IRQ_NONE;

    ok:
	++int_count;

	#if 0
	// Monitor the clock skew on the first two cards.
	if( i == 0 && (int_count % 5000) == 0) {
		info("%lu, %lu, %lu", cards[0]->irq_count, cards[1]->irq_count,
				      cards[0]->irq_count - cards[1]->irq_count);
	}
	#endif


	// clear edge triggered LINTi1
	// Yes, this isn't an active register according to the data sheet
	// but we do need to poke it to clear the interrupt status...
	// XXX Should we do this _after_ every thing else?
	//     Not after _everything it would appear, since that triggers
	//     complaints about APIC errors ...
	iowrite32( ioread32(PLX_INTCSR) | (1<<10), PLX_INTCSR );

	board = &card->board;

	// read from RX HW FIFO into card->framerx
	read_hw_fifo(card);
	// de-interlace frame into board buffers
	for(; j < MAX_PORTS; ++j) {
	    for(i=0; i < FRAME; ++i){
		card->chans[j].rxbuf[i] =
		    bitrev_lut[(unsigned char)card->framerx[i*32+j]];
	    }
	}
#if 0
	if( c == 0  && (int_count % 1000) == 0)
	    info("RX %02x %02x %02x %02x %02x %02x %02x %02x",
		    card->chans[0].rxbuf[0],
		    card->chans[0].rxbuf[1],
		    card->chans[0].rxbuf[2],
		    card->chans[0].rxbuf[3],
		    card->chans[0].rxbuf[4],
		    card->chans[0].rxbuf[5],
		    card->chans[0].rxbuf[6],
		    card->chans[0].rxbuf[7]
	    );
#endif
	// Write to User Fifo's
	vt_read(board);

	// Get samples from User Fifo's
	vt_write(board);
#if 0
	if( c == 0  && (int_count % 1000) == 0)
	    info("TX %02x %02x %02x %02x %02x %02x %02x %02x",
		    card->chans[0].txbuf[0],
		    card->chans[0].txbuf[1],
		    card->chans[0].txbuf[2],
		    card->chans[0].txbuf[3],
		    card->chans[0].txbuf[4],
		    card->chans[0].txbuf[5],
		    card->chans[0].txbuf[6],
		    card->chans[0].txbuf[7]
	    );
#endif
	// interlace into card->frametx
	memset(card->frametx,0,sizeof(short)*SIZE_FIFO/2);
	for(i=0; i<FRAME; ++i){
		for(j=0; j< MAX_PORTS; ++j) {
			card->frametx[i*32+j+MAX_PORTS] =
				    (short)bitrev_lut[card->chans[j].txbuf[i]];
		}
		//card->frametx[i*32+12] = 0x00;
	}

	// Write to TX FIFO.  Note FS pulse inserted here rather than in user
	// mode to ensure continuity of FS pulses even if user mode too slow
	// to fill sw FIFO.  We assume data from user mode is FS aligned.
	// We write 16 bits to the card here, but it only uses 9 of them.
	pbuf = card->frametx;
	for(i=0; i < (SIZE_FIFO/2); i += 32){
		pbuf[0] |= 0x100;
		for(j=0; j < (card->HW_W_FRAME); j += 2) {
			iowrite16_rep(TX_FIFO, pbuf+j, 2);
		}
		pbuf += 32;
	}

	// update hook and ring detector samples
	// Read bytes from card
	read_hookringbytes(card);
	khook_sample(card);
	kring_sample(card);
	kldrop_sample(card);
	do_ringing(card);
	// Check for ring, hook and loop drop
	if( ! (int_count % 5) ){
		detect_loop_drop(card);
		detect_ring(card);
		detect_hook(card);
	}

	return IRQ_HANDLED;
} //}}}
#else
//{{{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
static irqreturn_t openswitch_isr(int irq, void *dev_id, struct pt_regs *regs)
#else
static irqreturn_t openswitch_isr(int irq, void *dev_id)
#endif
{ //{{{
	struct openswitch *card = cards[0];
	struct vtboard    *board;
	short             *pbuf = NULL;
	int                i, j, c = 0;

	// check that this is really our interupt
	if(*cards != dev_id) return IRQ_NONE;
	int_count++;

	// clear edge triggered LINTi1
	// Yes, this isn't an active register according to the data sheet
	// but we do need to poke it to clear the interrupt status...
	iowrite32( ioread32(PLX_INTCSR) | (1<<10), PLX_INTCSR );

	for(; c < MAX_CARDS; ++c){
		card = cards[c];

		if( ! card ) continue;

		board = &card->board;

		// read from RX HW FIFO into card->framerx
		read_hw_fifo(card);
		// de-interlace frame into board buffers
		for(j=0; j < MAX_PORTS; ++j) {
		    for(i=0; i < FRAME; ++i){
			card->chans[j].rxbuf[i] =
			    bitrev_lut[(unsigned char)card->framerx[i*32+j]];
		    }
		}
#if 0
		if( c == 0  && (int_count % 1000) == 0)
		    info("RX %02x %02x %02x %02x %02x %02x %02x %02x",
			    card->chans[0].rxbuf[0],
			    card->chans[0].rxbuf[1],
			    card->chans[0].rxbuf[2],
			    card->chans[0].rxbuf[3],
			    card->chans[0].rxbuf[4],
			    card->chans[0].rxbuf[5],
			    card->chans[0].rxbuf[6],
			    card->chans[0].rxbuf[7]
		    );
#endif
		// Write to User Fifo's
		vt_read(board);

		// Get samples from User Fifo's
		vt_write(board);
#if 0
		if( c == 0  && (int_count % 1000) == 0)
		    info("TX %02x %02x %02x %02x %02x %02x %02x %02x",
			    card->chans[0].txbuf[0],
			    card->chans[0].txbuf[1],
			    card->chans[0].txbuf[2],
			    card->chans[0].txbuf[3],
			    card->chans[0].txbuf[4],
			    card->chans[0].txbuf[5],
			    card->chans[0].txbuf[6],
			    card->chans[0].txbuf[7]
		    );
#endif
		// interlace into card->frametx
		memset(card->frametx,0,sizeof(short)*SIZE_FIFO/2);
		for(i=0; i<FRAME; ++i){
			for(j=0; j< MAX_PORTS; ++j) {
				card->frametx[i*32+j+MAX_PORTS] = (short)bitrev_lut[card->chans[j].txbuf[i]];
			}
			//card->frametx[i*32+12] = 0x00;
		}

		// Write to TX FIFO.  Note FS pulse inserted here rather than in user
		// mode to ensure continuity of FS pulses even if user mode too slow
		// to fill sw FIFO.  We assume data from user mode is FS aligned.
		// We write 16 bits to the card here, but it only uses 9 of them.
		pbuf = card->frametx;
		for(i=0; i<(SIZE_FIFO/2); i+=32){
			pbuf[0] |= 0x100;
			for(j=0; j<(card->HW_W_FRAME); j+=2) {
				iowrite16_rep(TX_FIFO, pbuf+j, 2);
			}
			pbuf +=32;
		}

		// update hook and ring detector samples
		// Read bytes from card
		read_hookringbytes(card);
		khook_sample(card);
		kring_sample(card);
		kldrop_sample(card);
		do_ringing(card);
		// Check for ring, hook and loop drop
		if( ! (int_count % 5) ){
			detect_loop_drop(card);
			detect_ring(card);
			detect_hook(card);
		}
	}

	return IRQ_HANDLED;
} //}}}
//}}}
#endif


/*--------------------------------------------------------------------------*\
      module initialisation and exit
\*--------------------------------------------------------------------------*/

// Bit reversal of samples - hardware bug work around.
void bitrev(unsigned char a[], int n)
{ //{{{
	int i,j;
	unsigned char b,c;

	for(i=0; i<n; ++i) {
		b = a[i];
		c = 0;
		for(j=0; j<8; ++j) c |= ((b>>j)&0x1) << (7-j);
		a[i] = c;
	}
} //}}}

int __init vtopenswitch_init(void)
{ //{{{
	int i = 0;

	info(DRIVER_DESCRIPTION " " VT_VERSION " for linux " UTS_RELEASE);

	for(; i < ALAW_CODES; ++i) bitrev_lut[i] = i;
	bitrev(bitrev_lut, ALAW_CODES);

	i = pci_register_driver(&vtopenswitch_driver);
	if( i < 0 ){
		crit("Failed to register pci driver (%d)", i);
		return i;
	}
	for( i=0; cards[i]; ++i );

      #if ! ASYNC_IRQ
	if( i ){
		struct openswitch *card;

		if( request_irq(cards[0]->dev->irq, openswitch_isr,
				shareirq ? IRQF_SHARED : 0, NAME, *cards))
		{
			crit("FAILED to get IRQ");
			pci_unregister_driver(&vtopenswitch_driver);
			return -EIO;
		}
		dbginfo(1,*cards, "using IRQ %d", cards[0]->dev->irq);

		card = cards[0];
		// enable edge triggered LINTi1
		iowrite32( ioread32(PLX_INTCSR) | (1 + (1<<1) + (1<<8)), PLX_INTCSR );
		// enable interrupt
		iowrite32( ioread32(PLX_INTCSR) | (1<<6), PLX_INTCSR );
	}
      #endif

	info("module loaded, driver bound to %d cards", i);
	if(debug) info("using debug level %d", debug);
	return 0;
} //}}}

void __exit vtopenswitch_exit(void)
{ //{{{
      #if ! ASYNC_IRQ
	struct openswitch *card = cards[0];

	if( card ){
		iowrite32( ioread32(PLX_INTCSR) & ~(1<<6), PLX_INTCSR );
		free_irq(card->dev->irq, card);
	}
      #endif

	pci_unregister_driver(&vtopenswitch_driver);
	if( debug ){
		info("Ignored data received %ld",incomplete_frame_count);
		info("Junked count %ld",junked_samples_count);
	      #if ! ASYNC_IRQ
		info("Interupt count %ld",int_count);
	      #endif
	}
	info("module exit");
} //}}}


// loop start port - hook sw and audio sw
static void set_loop_offhook(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "taking loop offhook");
	card->latch_bits[port] |= 0x60;
	write_reg(card, port, 0x0a00 + card->latch_bits[port]);
	card->chans[port].state = CH_OFFHOOK;
} //}}}

static void set_loop_onhook(struct openswitch *card, int port)
{ //{{{
	dbgport(3,card, port, "putting loop onhook");
	card->latch_bits[port] &= ~0x60;
	write_reg(card, port, 0x0a00 + card->latch_bits[port]);
	card->chans[port].state = CH_IDLE;
} //}}}

static inline unsigned char openswitch_gain( int gain )
{ //{{{
	if( gain < 0 )    gain = 0;
	if( gain > 0xff ) gain = 0xff;
	return gain;
} //}}}

static void set_play_gain(struct openswitch *card, int port, unsigned char gain)
{ //{{{
	dbgport(5,card,port,"setting play gain %#x", gain);
	write_reg(card, port, 0x2200 + gain);
} //}}}

static void set_rec_gain(struct openswitch *card, int port, unsigned char gain)
{ //{{{
	dbgport(5,card,port,"setting rec gain %#x (hw: %#x)",
		gain, gain ? 256 - gain : 0);
	if( gain ) gain = 256 - gain;
	write_reg(card, port, 0x2a00 + gain);
} //}}}


static inline const char *porttype(struct openswitch *card, int port)
{ //{{{
	switch( card->chans[port].porttype ) {
	    case VT_FXO_AN:  return "FXO";
	    case VT_FXS_AN:  return "FXS";
	    case VT_UNKNOWN: return "empty port";
	    default:         return "unknown type";
	}
} //}}}

static int openswitch_ioctl(struct vtboard *board, int port, unsigned int cmd, void *data, int length)
{ //{{{
	VT_REG_RW          vt_reg;
	struct openswitch *card = container_of( board, struct openswitch, board );

	if( ! card->chans[port].porttype ){
		portcrit(card,port,"port not available");
		return -ENODEV;
	}
	switch(cmd) {
	    case VT_IOC_HOOK:
		// If we're here to send a hook flash, then its probably
		// because we got a PC_CODEC_BREAK message in libvpb.
		// In that case, we want to send a DSP_CODEC_BKEN reply
		// to signal completion of the flash.
		// It's all a bit convoluted, but keeps us compatible with
		// the OpenLine DSP.
		dbgport(3,card,port,"VT_IOC_HOOK %d", length);
		if(card->chans[port].porttype == VT_FXO_AN) {
			switch( length )
			{
			    case 0:
				set_loop_onhook(card, port);
				break;

			    case 1:
			    case 2:
				set_loop_offhook(card, port);
				break;

			    default:
				if( length >= 50 && length <= 1500 ) {
					unsigned char mess[DSP_LCODEC_BKEN] =
							{ DSP_LCODEC_BKEN,
							  DSP_CODEC_BKEN,
							  port
							};
					int ret = -EINVAL;

					if( card->chans[port].state == CH_OFFHOOK ) {
						set_loop_onhook(card, port);
						msleep( length );
						set_loop_offhook(card, port);
						ret = 0;
					}

					if( vt_send_event(&card->board, mess, mess[0]) )
						portcrit(card,port,
							 "FAILED to forward "
							 "DSP_CODEC_BKEN into MSGQ");
					return ret;
				}
				return -EINVAL;
			}
		} else
			portwarn(card,port,"cant change %s hook state",
					   porttype(card,port));
		break;

	    case VT_IOC_CHAN_BALANCE_SET:
		if( copy_from_user(&vt_reg,data,sizeof(VT_REG_RW)) )
			return -EFAULT;
		switch( vt_reg.reg ){
		    case 1: set_codec_reg(card, port, 0x32, vt_reg.value); break;
		    case 2: set_codec_reg(card, port, 0x3a, vt_reg.value); break;
		    case 3: set_codec_reg(card, port, 0x42, vt_reg.value); break;
		    default:
			 portcrit(card,port,
				  "VT_IOC_CHAN_BALANCE_SET: register (%d) out of range",
				  vt_reg.reg);
			return -EINVAL;
		}
		dbgport(4,card,port,"Set codec %d to %d", vt_reg.reg, vt_reg.value);
		break;

	    case VT_IOC_SET_PLAYGAIN:
		dbgport(3,card,port,"VT_IOC_SET_PLAYGAIN %#x", length);
		set_play_gain(card, port, openswitch_gain(length) );
		break;

	    case VT_IOC_SET_RECGAIN:
		dbgport(3,card,port,"VT_IOC_SET_RECGAIN %#x", length);
		set_rec_gain(card, port, openswitch_gain(length) );
		break;

	    case VT_IOC_SETFLASH:
	    case VT_IOC_SETHOOKTHRESH:
	    case VT_IOC_SETLOOPDROP:
	    case VT_IOC_SETVDAAIMPEDANCE:
		// Don't warn about these, we know we don't support them
		break;

	    case VT_IOC_SET_LOGGING:
		portwarn(card,port,"Logging mode not supported for OpenSwitch");
		break;

	    default:
		portcrit(card,port,"unknown ioctl type %d", cmd);
		return -EINVAL;
	}
	return 0;
} //}}}


static void tdm_output_enable(struct openswitch *card, int port, int slot)
{ //{{{
	int reg  = slot/BITS_OE;
	int bit  = slot - reg*BITS_OE;
	int mask = 1 << bit;

	card->oe[reg] |= mask;
	dbgport(4,card,port,"oe[%d] = 0x%02x (slot %d)", reg, card->oe[reg], slot);
	iowrite16(card->oe[reg], TDM_OE_0 + reg);
} //}}}

static void init_port(struct openswitch *card, int port)
{ //{{{
	// CONTROL REG
	// codec in normal mode
	// delayed data timing
	// even bit inversion A-law -- changed to m255 law
	// power amp disabled
	// MCLK = 2.048 MHz
	write_reg(card, port, 0x02a0);
	//write_reg(card, port, 0x0280);
	mdelay(2);

	// LATCH DIRECTION REG
	// L0,1,2 outputs
	// - L0 ring
	// - L1 hook_sw
	// - L2 audio_
	write_reg(card, port, 0x12e0);
	mdelay(2);

	// init output latch
	card->latch_bits[port] = 0;
	write_reg(card, port, 0x0a00 + card->latch_bits[port]);
	mdelay(2);

	// Set TDM bus time slots
	write_reg(card, port, 0x5280 + port);
	mdelay(2);
	write_reg(card, port, 0x4a80 + port + MAX_PORTS);
	mdelay(2);

	// Turn on Output Enable
	tdm_output_enable(card, port, port + MAX_PORTS);
	mdelay(2);

	// Turn on basic codec registers
	set_codec_reg(card, port, 0x32, 199);
	mdelay(2);
	dbgport(2,card,port,"initialised port");

	// Turn the audio on
	set_audio_on(card, port);
} //}}}

static inline int get_hookstate(struct openswitch *card, int port)
{ //{{{
	//XXX New for OS6 support.
	if( card->type == PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6 ) {
		if( port >= 6 ) return HOOK_ONHOOK;
		port += 6;
	}
	return ioread16(PIB(hookmap[port * 2])) & (1 << hookmap[port * 2 + 1])
		? HOOK_OFFHOOK : HOOK_ONHOOK;
} //}}}

static void check_port_type(struct openswitch *card, int port)
{ //{{{
	int val = get_codec_reg(card, port, 0x6);

	if( val == 0xa0 ){
		set_loop_offhook(card, port);
		mdelay(50);
		if( get_hookstate(card, port) == HOOK_OFFHOOK )
			card->chans[port].porttype = VT_FXS_AN;
		else
			card->chans[port].porttype = VT_FXO_AN;
		set_loop_onhook(card, port);
		card->chans[port].codec = VT_ALAW;
		dbgport(1,card,port,"port is %s",porttype(card,port));
	} else {
		//card->chans[port].porttype = VT_UNKNOWN;
		//card->chans[port].codec = VT_UNKNOWN;
		dbgport(1,card,port,"port has No Codec (%#x)", val);
	}
} //}}}

static const char *device_type( int type )
{ //{{{
	switch( type ) {
	    case PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH12:
		     return "OpenSwitch12";
	    case PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6:
		     return "OpenSwitch6";
	    default: return "OpenSwitch?";
	}
} //}}}

static int vtopenswitch_set_playgain(struct vtboard *board, int port, int gain)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );

	set_play_gain( card, port, openswitch_gain(gain) );
	return 0;
} //}}}

static int vtopenswitch_get_playgain(struct vtboard *board, int port, int *err)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );

	*err = 0;
	return read_reg(card, port, 0x2600);
} //}}}

static int vtopenswitch_set_recgain(struct vtboard *board, int port, int gain)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );

	set_rec_gain( card, port, openswitch_gain(gain) );
	return 0;
} //}}}

static int vtopenswitch_get_recgain(struct vtboard *board, int port, int *err)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );
	int                gain = read_reg(card, port, 0x2e00);

	*err = 0;
	return gain ? 256 - gain : 0;
} //}}}

static int vtopenswitch_set_hook(struct vtboard *board, int port, int hookstate)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );
	struct channel    *chan = &card->chans[port];

	if(chan->porttype != VT_FXO_AN)
		return -EINVAL;

	switch( hookstate )
	{
	    case 0:
		set_loop_onhook(card, port);
		break;

	    case 1:
	    case 2:
		set_loop_offhook(card, port);
		break;

	    default:
		if( hookstate < 50 || hookstate > 1500 ) {
			portwarn(card,port,"vtopenswitch_set_hook: bad value %d",
				 hookstate);
			return -EINVAL;
		}
		if( chan->state != CH_OFFHOOK ) {
			portwarn(card,port,"vtopenswitch_set_hook: flash while on-hook (%d)",
				 chan->state);
			return -EINVAL;
		}
		set_loop_onhook(card, port);
		msleep( hookstate );
		set_loop_offhook(card, port);
	}
	return 0;
} //}}}

static int vtopenswitch_get_hook(struct vtboard *board, int port, int *err)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );
	struct channel    *chan = &card->chans[port];

	*err = chan->porttype ? 0 : -EINVAL;
	return chan->state;
} //}}}

static int vtopenswitch_set_ring(struct vtboard *board, int port, int ringing)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );
	struct channel    *chan = &card->chans[port];

	if( chan->porttype != VT_FXS_AN || ringing < 0 || ringing > 255 )
		return -EINVAL;
	card->ringer[port].ringing = (ringing == 255) ? 0 : ringing;
	return 0;
} //}}}

static int vtopenswitch_get_ring(struct vtboard *board, int port, int *err)
{ //{{{
	struct openswitch *card = container_of( board, struct openswitch, board );
	struct channel    *chan = &card->chans[port];

	*err = chan->porttype ? 0 : -EINVAL;
	return card->ringer[port].ringing;
} //}}}

static int vtopenswitch_board_register(struct openswitch *card)
{ //{{{
	struct vtboard *board = &card->board;

	board->owner        = THIS_MODULE;
	board->name         = device_type( card->type );
	board->maxports     = MAX_PORTS;
	board->chans        = card->chans;
	board->ioctl        = openswitch_ioctl;
	board->set_playgain = vtopenswitch_set_playgain;
	board->get_playgain = vtopenswitch_get_playgain;
	board->set_recgain  = vtopenswitch_set_recgain;
	board->get_recgain  = vtopenswitch_get_recgain;
	board->set_hook     = vtopenswitch_set_hook;
	board->get_hook     = vtopenswitch_get_hook;
	board->set_ring     = vtopenswitch_set_ring;
	board->get_ring     = vtopenswitch_get_ring;

	if( ! vt_board_register(board) ) return RET_FAIL;

	dbginfo(2,card, "registered for %d ports with vtcore", board->maxports);
	return RET_OK;
} //}}}

static int openswitch_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent)
{ //{{{
	struct openswitch *card;
	int cardnum = 0;
	int failret = -ENOMEM;
	int i;

	card = kzalloc(sizeof(struct openswitch), GFP_KERNEL);
	if(!card) {
		crit("FAILED to allocate memory for new card");
		return -ENOMEM;
	}

	mutex_lock(&cards_mutex);
	for(; cardnum < MAX_CARDS && cards[cardnum]; ++cardnum);
	if(cardnum >= MAX_CARDS){
		crit("Too many OpenSwitch cards(%d), max is %d", cardnum, MAX_CARDS);
		mutex_unlock(&cards_mutex);
		goto hell;
	}
	cards[cardnum]=card;
	mutex_unlock(&cards_mutex);

	pci_set_drvdata(pdev,card);

      #if ! ASYNC_IRQ
	card->dev     = pdev;
      #endif
	card->cardnum = cardnum;
	card->type    = ent->subdevice;

	dbginfo(1,card,"initialising %s", device_type(card->type) );

	if(pci_enable_device(pdev)){
		failret = -EIO;
		goto hell_2;
	}

	if( ! request_mem_region(pci_resource_start(pdev,0),
				 pci_resource_len(pdev,0), NAME ) ){
		cardcrit(card, "FAILED to lock iomem region 0");
		failret = -EBUSY;
		goto hell_2;
	}
	// We don't use the ioport mapping, but lock it anyway
	// so noone else can accidentally request it later.
	if( ! request_region(pci_resource_start(pdev,1),
			     pci_resource_len(pdev,1), NAME ) ){
		cardcrit(card, "FAILED to lock ioport region 1");
		failret = -EBUSY;
		goto hell_3;
	}
	if( ! request_mem_region(pci_resource_start(pdev,2),
				 pci_resource_len(pdev,2), NAME ) ){
		cardcrit(card, "FAILED to lock iomem region 2");
		failret = -EBUSY;
		goto hell_4;
	}
	//XXX The new way.
	//void __iomem * map = pci_iomap(dev, bar, maxbytes);

	card->base0 = ioremap_nocache( pci_resource_start(pdev,0),
				       pci_resource_len(pdev,0) );
	card->base2 = ioremap( pci_resource_start(pdev,2),
			       pci_resource_len(pdev,2) );

	// Set up bus wait states
	iowrite32(0x40410020, PLX_LAS0BRD);


	// set up OE generator
	// XXX
#if 0
	// test value for development
	// User mode overrides
	// 0xffffffff => all on
	// 0x00000000 => all off
	// Each bit (from the 32 bit word) represents a time slot
	// from the TDM bus, when the bit is on only the host can
	// write to the TDM bus.
	//oe = 0x00000005; // ports 1&3
	oe = 0xffffffff;
	//oe = 0x00001000;
	iowrite16(oe & 0xff,       TDM_OE_0);
	iowrite16((oe>>8) & 0xff,  TDM_OE_1);
	iowrite16((oe>>16) & 0xff, TDM_OE_2);
	iowrite16((oe>>24) & 0xff, TDM_OE_3);
#endif
	iowrite16(0xff, TDM_OE_0);
	iowrite16(0xff, TDM_OE_1);
	iowrite16(0xff, TDM_OE_2);
	iowrite16(0xff, TDM_OE_3);

	// reset HW (FIFOs and codecs)
	iowrite16(0x00, CONTROL_REG);
	iowrite16(0x80, CONTROL_REG);
	iowrite16(0x00, CONTROL_REG);

	//XXX Redo this this lot.
	// create hook detector
	card->khook = khook_open(card->base2);
	// create ring detector
	card->kring = kring_open(card->base2);
	// create Loop Drop detector
	card->kldrop = kldrop_open(card->base2);


	read_id(card);
	if( card->rev == 111 || card->rev > 210
	 || (card->type == PCI_DEVICE_ID_VOICETRONIX_OPENSWITCH6 && card->rev == 102) ) {
		//XXX Do these need to be different for OS6 XXX ?
		card->HW_R_FRAME=13;
		card->HW_W_FRAME=24;
	}
	else {
		crit("FAILED: wrong driver for revision %d cards", card->rev);
		card->HW_R_FRAME=32;	//XXX Are they unsupported or not?
		card->HW_W_FRAME=32;	//    Test this later
		goto hell_5;
	}


	//XXX Revisit this
	card->tmpbufidx=0;

	// Set IIC control for user i/o 2 & 3.
	// Note: This should be the default after reset.
	// XXX ... so why do it twice more here?
	cntrlbit(card,IIC_SDA_IO, 0);  //Ensure =low
	cntrlbit(card,IIC_SDA_DIR,0);  // i/p Hi-Z
	cntrlbit(card,IIC_SDA_CTL,0);  // User I/O
	cntrlbit(card,IIC_SCL_IO, 0);
	cntrlbit(card,IIC_SCL_DIR,0);
	cntrlbit(card,IIC_SCL_CTL,0);

	//XXX see above
	// OE init
	iowrite16(0, TDM_OE_0);
	iowrite16(0, TDM_OE_1);
	iowrite16(0, TDM_OE_2);
	iowrite16(0, TDM_OE_3);

	// Reset card and codecs
	iowrite16(0x80, CONTROL_REG);
	iowrite16(0x00, CONTROL_REG);


	// disable watch dog!
	// set high PLX9052 pin user1, connected to WD reset, asserting WD
	// Note: WD reset is rising-edge triggered
	msleep(250);  //XXX
	iowrite32( ioread32(PLX_CNTRL) | (1<<5), PLX_CNTRL);
	msleep(500);

	// set low PLX9052 pin user1, connected to WD reset, de-asserting WD reset
	iowrite32( ioread32(PLX_CNTRL) & ~(1<<5), PLX_CNTRL);

	// set low PLX9052 pin user0, connected to WD enable, disabling WD timer
	iowrite32( ioread32(PLX_CNTRL) & ~(1<<2), PLX_CNTRL);

	for(i=0; i < MAX_PORTS; ++i) {
		RINGER *ringer = &card->ringer[i];

		ringer->ringing        = 0;
		ringer->on             = 0;
		ringer->cadence[0].on  = 1000;
		ringer->cadence[0].off = 0;
		ringer->cadence[1].on  = 0;
		ringer->cadence[1].off = 2000;
		ringer->ring_time      = 0;

		init_port(card,i);
		check_port_type(card,i);
		set_play_gain(card, i, 0x80);
		set_rec_gain(card, i, 0x80);
		set_codec_reg(card, i, 0x32, 0xf0);
		set_codec_reg(card, i, 0x3a, 0x5c);
		set_codec_reg(card, i, 0x42, 0x51);
	}

	if( ! vtopenswitch_board_register(card) ){
		cardcrit(card, "FAILED to register with vtcore");
		failret = -EIO;
		goto hell_5;
	}

      #if ASYNC_IRQ
	// Flush out whatever has accumulated in the rx fifo before we
	// enable interrupts and take the data in it seriously.  If we
	// start with a backlog in it there will be unneccessary delay.
	{
		int d = 0;

		// Sync empty flag. (Clock into FIFO Read logic causes
		//                   the R_FE flag to update)
		ioread16(HOOKRING_2);

		// get all data from hw fifo buffer into tmpbuf (4 words at a time)
		for(;;) {
			u16 tmpbuf[4];
			int n;

			ioread16_rep(HW_FIFO, tmpbuf, 4);

			// check each of 4 words for empty flag.
			for(n=0; n < 4; ++n) {
				if( tmpbuf[n] & 0x0200 ) {
					dbginfo(1,card,
						"rx fifo flushed %d words", d);
					goto flushed;
				}
				++d;
			}
		}
		// XXX This might spinlock with a broken card...
		//     can a card that broken get this far?
	}

    flushed:

	card->irq_count = 0;
	if( request_irq(pdev->irq, openswitch_isr,
			shareirq ? IRQF_SHARED : 0, NAME, card))
	{
		crit("FAILED to get IRQ");
		failret = -EIO;
		goto hell_6;
	}
	dbginfo(1,card, "using IRQ %d", pdev->irq);

	// enable edge triggered LINTi1
	iowrite32( ioread32(PLX_INTCSR) | (1 + (1<<1) + (1<<8)), PLX_INTCSR );
	// enable interrupt
	iowrite32( ioread32(PLX_INTCSR) | (1<<6), PLX_INTCSR );
      #endif

	dbginfo(1,card,"card initialised");
	return 0;

  #if ASYNC_IRQ
    hell_6:
	vt_board_unregister(&(card->board));
  #endif
    hell_5:
	kldrop_close(card->kldrop);
	kring_close(card->kring);
	khook_close(card->khook);
	iounmap(card->base2);
	iounmap(card->base0);
	release_mem_region(pci_resource_start(pdev,2), pci_resource_len(pdev,2));
    hell_4:
	release_region(pci_resource_start(pdev,1), pci_resource_len(pdev,1));
    hell_3:
	release_mem_region(pci_resource_start(pdev,0), pci_resource_len(pdev,0));
    hell_2:
	cards[cardnum] = NULL;
    hell:
	kfree(card);
	return failret;
} //}}}

// Clearly this can't really put a station on-hook, but since we're about
// to unplug it from our side anyway, that soon will be moot.  This signals
// all our dependents to consider the station inactive.
static void force_station_onhook(struct openswitch *card, int port)
{ //{{{
	unsigned char mess[] = {DSP_LCODEC_HKON, DSP_CODEC_HKON, port};

	dbgport(1,card,port,"forcing station from state %d to idle",
			    card->chans[port].state );
	card->chans[port].state = CH_IDLE;
	vt_send_event(&card->board, mess, mess[0]);
	set_audio_off( card, port );
} //}}}

static void openswitch_remove_board(struct pci_dev *pdev)
{ //{{{
	struct openswitch *card = pci_get_drvdata(pdev);
	int    i = 0;

	dbginfo(1,card,"releasing card");

      #if ASYNC_IRQ
	iowrite32( ioread32(PLX_INTCSR) & ~(1<<6), PLX_INTCSR );
	free_irq(pdev->irq, card);
	dbginfo(1,card,"serviced %lu interrupts", card->irq_count);
      #endif

	// Hangup on anyone still offhook, vtcore will refuse
	// to unregister a channel that is not already idle.
	for(; i < MAX_PORTS; ++i) {
		if( card->chans[i].state != CH_IDLE ) {
			switch( card->chans[i].porttype ){
			    case VT_FXO_AN:
				set_loop_onhook(card, i);
				break;
			    case VT_FXS_AN:
				force_station_onhook(card, i);
				break;
			    default:
				portcrit(card,i,"%s in state %d not idle?",
						porttype(card,i),
						card->chans[i].state );
			}
		}
		if( card->ringer[i].on ) set_ring_off(card,i);
	}
	vt_board_unregister(&(card->board));

	kldrop_close(card->kldrop);
	kring_close(card->kring);
	khook_close(card->khook);
	iounmap(card->base2);
	iounmap(card->base0);
	release_mem_region(pci_resource_start(pdev,2), pci_resource_len(pdev,2));
	release_region(pci_resource_start(pdev,1), pci_resource_len(pdev,1));
	release_mem_region(pci_resource_start(pdev,0), pci_resource_len(pdev,0));

	mutex_lock(&cards_mutex);
	cards[card->cardnum] = NULL;
	mutex_unlock(&cards_mutex);

	kfree(card);
	cardinfo(card,"card released");
} //}}}


/*--------------------------------------------------------------------------*\

	FUNCTION.: khook_open()
	AUTHOR...: David Rowe
	DATE.....: 1/8/02

	Constructor to init hook detection module for a board.

\*--------------------------------------------------------------------------*/

void *khook_open(unsigned short *base2) {
	KHOOK *khook;
	int    i,j;

	khook = vmalloc(sizeof(KHOOK));

	for(j=0; j<MAX_PORTS; j++) {
		for(i=0; i<MEM; i++) {
			khook->hookmem[j][i] = 0;
		}
		khook->running[j] = 0;
		khook->index[j] = 0;
		khook->hook_state[j] = 0;
	}
	khook->base2 = base2;

	return((void*)khook);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: khook_close()
	AUTHOR...: David Rowe
	DATE.....: 1/8/02

	Destructor to close down hook detection module.

\*--------------------------------------------------------------------------*/

void khook_close(void *khook) {
	vfree(khook);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: khook_sample()
	AUTHOR...: David Rowe
	DATE.....: 2/8/02

	Called every 1ms by V12PCI interrupt service routine to sample
	hook status.  Maintains running sum used to determine hook status.

\*--------------------------------------------------------------------------*/
//static int hookmap[] = { 7,1, 7,3, 7,5, 7,7, 8,1, 8,3, 8,5, 8,7, 9,1, 9,3, 9,5, 9,7 };

void khook_sample(struct openswitch *card)
{
	KHOOK *khook = card->khook;
	int    ch,  bit, hookbit;

	for(ch=0; ch<MAX_PORTS; ch++) {
		// extract hook bit
		bit = 1 << hookmap[ch*2+1];
		hookbit = card->hookringbytes[ch/4];
		hookbit &= bit;

		// update running sum

		if (hookbit) { hookbit=1; } else { hookbit=0; }
		khook->running[ch] -= khook->hookmem[ch][khook->index[ch]];
		khook->running[ch] += hookbit;
		khook->hookmem[ch][khook->index[ch]] = hookbit;
		khook->index[ch]++;
		if (khook->index[ch] == MEM)
			khook->index[ch] = 0;
	}
}




/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_open()
	AUTHOR...: David Rowe
	DATE.....: 1/8/02

	Constructor to init hook detection module for a board.

\*--------------------------------------------------------------------------*/

void *kring_open(unsigned short *base2) {
	PKRING kring;
	int    ch;

	kring = vmalloc(sizeof(KRING));

	for(ch=0; ch<MAX_PORTS; ch++) {
		kring->ring_state[ch] = RING_L;
		kring->ring_evt[ch] = 0;
	}
	kring->base2 = base2;

	return((void*)kring);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_close()
	AUTHOR...: David Rowe
	DATE.....: 17/8/02

	Destructor to close down ring detection module.

\*--------------------------------------------------------------------------*/

void kring_close(void *kring) {
	vfree(kring);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_sample
	AUTHOR...: David Rowe
	DATE.....: 16/8/02

	Called from ISR every 1ms to sample ring bits and iterate ring det
	state machine.

\*--------------------------------------------------------------------------*/

//int ringmap[] = { 7,0, 7,2, 7,4, 7,6, 8,0, 8,2, 8,4, 8,6, 9,0, 9,2, 9,4, 9,6 };
//static int hookmap[] = { 7,1, 7,3, 7,5, 7,7, 8,1, 8,3, 8,5, 8,7, 9,1, 9,3, 9,5, 9,7 };
void kring_sample(struct openswitch *card)
{
	PKRING  kring = (PKRING)card->kring;
	int     ring, state, next_state, bit;
	int     ch;
	int     *ring_state = kring->ring_state;
	int     *count = kring->count;
	int     *counth = kring->counth;
	int     *ring_evt = kring->ring_evt;

	for(ch=0; ch<MAX_PORTS; ch++) {

		/* sense ring bit in i-th channel */

		//int addr = ringmap[ch*2];
		bit = 1 << ringmap[ch*2+1];
		ring = card->hookringbytes[ch/4];
		ring &= bit;


		state = ring_state[ch];

		next_state = state;
		switch(state) {

		case RING_L:
			/* Idle State */
			if (ring) {
				next_state = COUNT_H;
				count[ch] = 0;
				counth[ch] = 0;
			}
			else
				next_state = RING_L;
			break;

		case COUNT_H:
			/* Count number of H samples in sample window */
			if (count[ch]++ < WINDOW_RING) {
				if (ring) counth[ch]++;
			}
			else {
				/* sample window finished, make decn */
				if (counth[ch] >= THRESH_RING) {
					/* generate event */
					ring_evt[ch] = 1;
					next_state = COUNT_L;
					count[ch] = 0;
					counth[ch] = 0;
				}
				else {
					next_state = RING_L;
				}
			}
			break;

		case COUNT_L:
			/* Count number of H samples in sample window */
			if (count[ch]++ < WINDOW_RING) {
				if (ring) counth[ch]++;
			}
			else {
				/* sample window finished, make decn */
				if (counth[ch] <= THRESH_SIL) {
					ring_evt[ch] = -1;
					next_state = RING_L;
				}
				else {
					next_state = COUNT_L;
					count[ch] = 0;
					counth[ch] = 0;
				}
			}
			break;

		}

		ring_state[ch] = next_state;

	} /* for(ch=0 ... */
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_read()
	AUTHOR...: David Rowe
	DATE.....: 18/8/02

	Called by driver IOCTL to determine if a ring event has occurred.
	After reading, the ring_evt flag is reset.

\*--------------------------------------------------------------------------*/

int kring_read(void *pv_kring, int ch) {
	PKRING  kring = (PKRING)pv_kring;
	int     *ring_evt = kring->ring_evt;
	int     ret;

	ret =  ring_evt[ch];
	ring_evt[ch] = 0;
	return ret;
}


/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_open()
	AUTHOR...: David Rowe
	DATE.....: 1/8/02
	AUTHOR..: Ben Kramer
	DATE....: 29/1/03

	Constructor to init hook detection module for a board.

\*--------------------------------------------------------------------------*/

void *kldrop_open(unsigned short *base2) {
	PKLDROP kldrop;
	int    ch;

	kldrop = vmalloc(sizeof(KLDROP));

	for(ch=0; ch<MAX_PORTS; ch++) {
		kldrop->ldrop_state[ch] = DFRONT_L;
		kldrop->ldrop_evt[ch] = 0;
	}
	kldrop->base2 = base2;

	return((void*)kldrop);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_close()
	AUTHOR...: David Rowe
	DATE.....: 17/8/02
	AUTHOR..: Ben Kramer
	DATE....: 29/1/03

	Destructor to close down ring detection module.

\*--------------------------------------------------------------------------*/

void kldrop_close(void *kldrop) {
	vfree(kldrop);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kldrop_sample
	AUTHOR...: David Rowe
	DATE.....: 16/8/02
	AUTHOR..: Ben Kramer
	DATE....: 29/1/03

	Called from ISR every 1ms to sample ring bits and iterate ld det
	state machine.

\*--------------------------------------------------------------------------*/

void kldrop_sample(struct openswitch *card)
{
	PKLDROP  kldrop = (PKLDROP)card->kldrop;
	int     ring, state, next_state, bit;
	int     ch;
	int     *ldrop_state = kldrop->ldrop_state;
	int     *count = kldrop->count;
	int     *ldrop_evt = kldrop->ldrop_evt;

	for(ch=0; ch<MAX_PORTS; ch++) {

		// sense ring bit in i-th channel 

		//int addr = ringmap[ch*2];
		//vpbmemcpy_fromio(&ring, kldrop->base2 + addr, 1);
		bit = 1 << ringmap[ch*2+1];
		ring = card->hookringbytes[ch/4];
		ring &= bit;

		state = ldrop_state[ch];

		next_state = state;
		switch(state) {

		case DFRONT_L:
			// Wait for DWINL L samples  
			if (count[ch]++ < DWINL) {
				if (ring) {
					// if H reset 
					count[ch] = 0;
					next_state = DFRONT_L;
				}
			}
			else {
			// now wait for spike (single H sample) 
				if (ring) {
					count[ch] = 0;
					next_state = DWAITH_WIN;
//					printk("<7> Change to DWAITH_WIN\n");
				}
				else {
					next_state = DFRONT_L;
				}
			}
		break;
		case DWAITH_WIN:
			// Wait for DWINH samples  
			if (count[ch]++ > DWINH) {
				count[ch] = 0;
				next_state = DBACK_L;
				//printk("<7> Change to DBACK_L\n");
			}
			else
				next_state = DWAITH_WIN;
		break;

		case DBACK_L:
			// Wait for DWINL L samples  
			if (count[ch]++ < DWINL) {
				if (ring) {
					// if H reset 
					count[ch] = 0;
					next_state = DFRONT_L;
				//printk("<7> Change to DFRONT_L\n");
				}
			}
			else {
				// generate event
				//printk("<7> Hey Dude, got a Loop Drop on channel %d !\n", ch);
				ldrop_evt[ch] = 1;
				count[ch] = 0;
				next_state = DFRONT_L;
			}
		break;

		default:
			next_state = state;

		}

		ldrop_state[ch] = next_state;

	} // for(ch=0 ... 
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: kring_read()
	AUTHOR...: David Rowe
	DATE.....: 18/8/02
	AUTHOR..: Ben Kramer
	DATE....: 29/1/03

	Called by driver IOCTL to determine if a ring event has occurred.
	After reading, the ring_evt flag is reset.

\*--------------------------------------------------------------------------*/

int kldrop_read(void *pv_kldrop, int ch) {
	PKLDROP  kldrop = (PKLDROP)pv_kldrop;
	int     *ldrop_evt = kldrop->ldrop_evt;
	int     ret;

	ret =  ldrop_evt[ch];
	ldrop_evt[ch] = 0;
	return ret;
}


module_init(vtopenswitch_init);
module_exit(vtopenswitch_exit);

MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_VERSION(VT_VERSION);
MODULE_LICENSE("GPL");

