#ifdef 		MODVERSIONS
#ifndef 	MODULE
#define 	MODULE
#endif
#endif


#include <linux/version.h>
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(ver,rel,seq)	((ver << 16) | (rel << 8) | seq)
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
#ifdef MODULE
#include <linux/config.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#else
#define	MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif


#include <linux/autoconf.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/delay.h>
#include <asm/bitops.h>

#ifndef PCI_ANY_ID
#define PCI_ANY_ID (~0)
#endif
#endif

#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty_driver.h>
#include <linux/pci.h>
#include <linux/circ_buf.h>

#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/serial.h>
#include <linux/interrupt.h>


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/moduleparam.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serialP.h>
#include <linux/delay.h>
#include <linux/device.h>
#endif

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
#include <linux/kref.h>
#endif

#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/poll.h>


#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17)) 
#include <linux/devfs_fs_kernel.h>
#endif

/*-------------------------------------------------------------------------------

            				for wch_main.c        

-------------------------------------------------------------------------------*/
/*******************************************************
             WCH driver information              
*******************************************************/
#define WCH_DRIVER_VERSION 		"1.0.0.0"
#define WCH_DRIVER_DATE			"2010/09/11"
#define WCH_DRIVER_AUTHOR  		"WCH GROUP"
#define WCH_DRIVER_DESC  		"WCH Multi-I/O Board Driver Module"

#define WCH_TTY_MAJOR  			245
#define WCH_PAL_MAJOR			246
#define WCH_PPD_MAJOR			247

#define WCH_DBG					0
#define WCH_DBG_BOARD			0
#define WCH_DBG_SERPORT			0
#define WCH_DBG_PARPORT			0


/*******************************************************
             WCH board information              
*******************************************************/
#define VENDOR_ID_WCH_PCI				0x4348
#define SUB_VENDOR_ID_WCH_PCI			0x4348
#define DEVICE_ID_WCH_CH352_1S1P			0x5053
#define SUB_DEVICE_ID_WCH_CH352_1S1P		0x5053
#define DEVICE_ID_WCH_CH352_2S			0x3253
#define SUB_DEVICE_ID_WCH_CH352_2S		0x3253
#define DEVICE_ID_WCH_CH353_4S			0x3453
#define SUB_DEVICE_ID_WCH_CH353_4S		0x3453
#define DEVICE_ID_WCH_CH353_2S1P			0x7053
#define SUB_DEVICE_ID_WCH_CH353_2S1P		0x7053
#define DEVICE_ID_WCH_CH353_2S1PAR		0x5046
#define SUB_DEVICE_ID_WCH_CH353_2S1PAR	0x5046
#define DEVICE_ID_WCH_CH355_4S			0x7173
#define SUB_DEVICE_ID_WCH_CH355_4S		0x3473
#define DEVICE_ID_WCH_CH356_4S1P			0x7073
#define SUB_DEVICE_ID_WCH_CH356_4S1P		0x3473
#define DEVICE_ID_WCH_CH356_6S			0x3873
#define SUB_DEVICE_ID_WCH_CH356_6S		0x3873
#define DEVICE_ID_WCH_CH356_8S			0x3853
#define SUB_DEVICE_ID_WCH_CH356_8S		0x3853
#define DEVICE_ID_WCH_CH357_4S			0x5334
#define SUB_DEVICE_ID_WCH_CH357_4S		0x5053
#define DEVICE_ID_WCH_CH358_4S1P			0x5334
#define SUB_DEVICE_ID_WCH_CH358_4S1P		0x5334
#define DEVICE_ID_WCH_CH358_8S			0x5338
#define SUB_DEVICE_ID_WCH_CH358_8S		0x5338
#define DEVICE_ID_WCH_CH359_16S			0x5838
#define SUB_DEVICE_ID_WCH_CH359_16S		0x5838

// for chip_flag
#define NONE_HWID	0x0000
#define CH352_HWID	0x0001
#define CH353_HWID	0x0002
#define CH355_HWID	0x0004
#define CH356_HWID	0x0008
#define CH357_HWID	0x0010
#define CH358_HWID	0x0020
#define CH359_HWID	0x0040

// for board_flag
#define BOARDFLAG_NONE			0x0000
#define BOARDFLAG_REMAP			0x0001
#define BOARDFLAG_16PORTS		0x0002

// for port_flag
#define PORTFLAG_NONE			0x0000
#define PORTFLAG_REMAP			0x0001
#define PORTFLAG_16PORTS		0x0002

// for part_number
#define PART_NUMBER_NONE    0x0000

// board info
#define WCH_BOARDS_MAX  			4
#define WCH_PORT_ONBOARD_MAX 	16	

#define WCH_SER_TOTAL_MAX		32
#define WCH_SER_ONBOARD_MAX  	16

/*******************************************************
             uart information              
*******************************************************/

// WCH uart fifo info 
#define CH352_FIFOSIZE_16  				16
#define CH352_TRIGGER_LEVEL_16FIFO_01	1
#define CH352_TRIGGER_LEVEL_16FIFO_04  	4
#define CH352_TRIGGER_LEVEL_16FIFO_08  	8
#define CH352_TRIGGER_LEVEL_16FIFO_14  	14

#define CH353_FIFOSIZE_16  				16
#define CH353_TRIGGER_LEVEL_16FIFO_01	1
#define CH353_TRIGGER_LEVEL_16FIFO_04  	4
#define CH353_TRIGGER_LEVEL_16FIFO_08  	8
#define CH353_TRIGGER_LEVEL_16FIFO_14  	14

#define CH355_FIFOSIZE_16  				16
#define CH355_TRIGGER_LEVEL_16FIFO_01	1
#define CH355_TRIGGER_LEVEL_16FIFO_04  	4
#define CH355_TRIGGER_LEVEL_16FIFO_08  	8
#define CH355_TRIGGER_LEVEL_16FIFO_14  	14

#define CH356_FIFOSIZE_16  				16
#define CH356_TRIGGER_LEVEL_16FIFO_01	1
#define CH356_TRIGGER_LEVEL_16FIFO_04  	4
#define CH356_TRIGGER_LEVEL_16FIFO_08  	8
#define CH356_TRIGGER_LEVEL_16FIFO_14  	14

#define CH357_FIFOSIZE_128  				128
#define CH357_TRIGGER_LEVEL_128FIFO_01		1
#define CH357_TRIGGER_LEVEL_128FIFO_32  	32
#define CH357_TRIGGER_LEVEL_128FIFO_64  	64
#define CH357_TRIGGER_LEVEL_128FIFO_112  	112

#define CH358_FIFOSIZE_128  				128
#define CH358_TRIGGER_LEVEL_128FIFO_01		1
#define CH358_TRIGGER_LEVEL_128FIFO_32  	32
#define CH358_TRIGGER_LEVEL_128FIFO_64  	64
#define CH358_TRIGGER_LEVEL_128FIFO_112  	112

#define CH359_FIFOSIZE_128  				128
#define CH359_TRIGGER_LEVEL_128FIFO_01		1
#define CH359_TRIGGER_LEVEL_128FIFO_32  	32
#define CH359_TRIGGER_LEVEL_128FIFO_64  	64
#define CH359_TRIGGER_LEVEL_128FIFO_112  	112

// uart fifo setup
#define CH352_FIFOSIZE_SET  				CH352_FIFOSIZE_16
#define CH352_TRIGGER_LEVEL_SET  			CH352_TRIGGER_LEVEL_16FIFO_14

#define CH353_FIFOSIZE_SET  				CH353_FIFOSIZE_16
#define CH353_TRIGGER_LEVEL_SET  			CH353_TRIGGER_LEVEL_16FIFO_14

#define CH355_FIFOSIZE_SET  				CH355_FIFOSIZE_16
#define CH355_TRIGGER_LEVEL_SET  			CH355_TRIGGER_LEVEL_16FIFO_14

#define CH356_FIFOSIZE_SET  				CH356_FIFOSIZE_16
#define CH356_TRIGGER_LEVEL_SET  			CH356_TRIGGER_LEVEL_16FIFO_14

#define CH357_FIFOSIZE_SET  				CH357_FIFOSIZE_128
#define CH357_TRIGGER_LEVEL_SET  			CH357_TRIGGER_LEVEL_128FIFO_112

#define CH358_FIFOSIZE_SET  				CH358_FIFOSIZE_128
#define CH358_TRIGGER_LEVEL_SET  			CH358_TRIGGER_LEVEL_128FIFO_112

#define CH359_FIFOSIZE_SET  				CH359_FIFOSIZE_128
#define CH359_TRIGGER_LEVEL_SET  			CH359_TRIGGER_LEVEL_128FIFO_112


#define UART_TRIGGER00_FCR					0x00
#define UART_TRIGGER01_FCR					0x40
#define UART_TRIGGER10_FCR					0x80
#define UART_TRIGGER11_FCR					0xC0

#define UART_DEFAULT_FCR					0x00


#define DEFAULT_FIFOSIZE  					1
#define DEFAULT_TRIGGER_LEVEL  				1


// register status info
#define UART_LSR_ERR_IN_RFIFO  				0x80	
#define UART_MCR_AFE  						0x20
#define UART_IIR_CTO  						0x0C	


// interrupt vedtor offset

// serial address length
#define WCH_SER_ADDRESS_LENGTH 				8

// PCI configuration bar 0 ~ 5
#define WCH_PCICFG_BAR_TOTAL				6				


/*******************************************************
             miscellaneous Information              
*******************************************************/
#define INTERRUPT_COUNT 128
#define WAKEUP_CHARS    256


#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
#define WCHTERMIOS  ktermios
#else
#define WCHTERMIOS  termios
#endif

#define WCH_SER_PORT_MAX_UART 8
#define WCH_SER_PORT_UNKNOWN 0
#define WCH_SER_PORT_CH352 1
#define WCH_SER_PORT_CH353 2
#define WCH_SER_PORT_CH355 3
#define WCH_SER_PORT_CH356 4
#define WCH_SER_PORT_CH357 5
#define WCH_SER_PORT_CH358 6
#define WCH_SER_PORT_CH359 7

// for ser_port->setserial_flag
#define WCH_SER_BAUD_SETSERIAL	1
#define WCH_SER_BAUD_NOTSETSER	0


/*******************************************************
             struct define Information 
*******************************************************/
// name length
#define WCH_BOARDNAME_LENGTH  15
#define WCH_DRIVERVERSION_LENGTH 15


struct ser_port_info 
{
  char  		board_name_info[WCH_BOARDNAME_LENGTH];
  unsigned int  bus_number_info;
  unsigned int  dev_number_info;
  unsigned int  port_info;
  unsigned int  base_info;		
  unsigned int  irq_info;	
};


struct par_port_info 
{
 	char  			board_name_info[WCH_BOARDNAME_LENGTH];
 	unsigned int  	bus_number_info;
 	unsigned int  	dev_number_info;
 	unsigned int  	port_info;
 	unsigned int  	base_info;
 	unsigned int	base_hi_info;		
 	unsigned int  	irq_info;	
};


typedef struct _PORT
{
	char			type;

	int				bar1;
	unsigned char	offset1;
	unsigned char	length1;

	int				bar2;
	unsigned char	offset2;
	unsigned char	length2;

	unsigned int	intmask;
	unsigned int	chip_flag;
	
} PORT;


typedef struct _PCI_BOARD
{
	unsigned int	vendor_id;
	unsigned int 	device_id;
	unsigned int	sub_vendor_id;
	unsigned int	sub_device_id;
	
	unsigned int	num_serport;
	
	unsigned int	intr_vector_bar;
	unsigned char	intr_vector_offset;
	
	char			board_name[WCH_BOARDNAME_LENGTH];
	unsigned int	board_flag; 
	unsigned int	part_number; 
	
	
	PORT			port[WCH_PORT_ONBOARD_MAX];
} PCI_BOARD;


struct wch_board;
struct wch_ser_port;
struct wch_par_port;


struct wch_board 
{
	int						board_enum;
	int						board_number;
	unsigned int			bus_number;
	unsigned int			dev_number;
	
	unsigned int			ports; 
	unsigned int			ser_port;
	
	unsigned int			ser_port_index;
	
	unsigned int			bar_addr[WCH_PCICFG_BAR_TOTAL];
	unsigned int			irq;

	unsigned int			board_flag;

	unsigned int			vector_mask;
    PCI_BOARD        		pb_info;
	struct pci_dev			*pdev;
	int						(*ser_isr)(struct wch_board *, struct wch_ser_port *);
};


/*-------------------------------------------------------------------------------

            				for wch_serial.c       

-------------------------------------------------------------------------------*/
/*******************************************************
             ioctl user define            
*******************************************************/
#define WCH_IOCTL  0x900
#define WCH_SER_DUMP_PORT_INFO  (WCH_IOCTL + 50)
#define WCH_SER_DUMP_PORT_PERF  (WCH_IOCTL + 51)
#define WCH_SER_DUMP_DRIVER_VER (WCH_IOCTL + 52)


/*******************************************************
             serial define            
*******************************************************/
#define PORT_SER_UNKNOWN		0
#define PORT_SER_8250			1
#define PORT_SER_16450			2
#define PORT_SER_16550			3
#define PORT_SER_16550A			4
#define PORT_SER_CIRRUS     	5	
#define PORT_SER_16650			6
#define PORT_SER_16650V2		7
#define PORT_SER_16750			8
#define PORT_SER_MAX_UART		8	/* max serial port ID */


#define WCH_USF_CLOSING_WAIT_INF	(0)
#define WCH_USF_CLOSING_WAIT_NONE	(65535)
#define WCH_UART_CONFIG_TYPE		(1 << 0)
#define WCH_UART_CONFIG_IRQ			(1 << 1)

#define WCH_UART_XMIT_SIZE 			4096

#define ser_circ_empty(circ)		    ((circ)->head == (circ)->tail)
#define ser_circ_clear(circ)		    ((circ)->head = (circ)->tail = 0)
#define ser_circ_chars_pending(circ)    (CIRC_CNT((circ)->head, (circ)->tail, WCH_UART_XMIT_SIZE))
#define ser_circ_chars_free(circ)       (CIRC_SPACE((circ)->head, (circ)->tail, WCH_UART_XMIT_SIZE))
#define ser_tx_stopped(port)            ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
	

#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
#define WCH_SERIAL_INLINE
#endif
  
#ifdef WCH_SERIAL_INLINE
#define _INLINE_ inline
#else
#define _INLINE_
#endif


#define WCH_UPIO_PORT				(0)
#define WCH_UPIO_MEM				(1)

#define WCH_UPF_SAK					(1 << 2)
#define WCH_UPF_SPD_MASK			(0x1030)
#define WCH_UPF_SPD_HI				(0x0010)
#define WCH_UPF_SPD_VHI				(0x0020)
#define WCH_UPF_SPD_CUST			(0x0030)
#define WCH_UPF_SPD_SHI				(0x1000)
#define WCH_UPF_SPD_WARP			(0x1010)
#define WCH_UPF_SKIP_TEST			(1 << 6)
#define WCH_UPF_HARDPPS_CD			(1 << 11)
#define WCH_UPF_LOW_LATENCY			(1 << 13)
#define WCH_UPF_BUGGY_UART			(1 << 14)
#define WCH_UPF_MAGIC_MULTIPLIER	(1 << 16)

#define WCH_UPF_CHANGE_MASK			(0x17fff)
#define WCH_UPF_USR_MASK			(WCH_UPF_SPD_MASK | WCH_UPF_LOW_LATENCY)
					 

#define WCH_UIF_CHECK_CD			(1 << 25)
#define WCH_UIF_CTS_FLOW			(1 << 26)

#define WCH_UIF_NORMAL_ACTIVE		(1 << 29)
#define WCH_UIF_INITIALIZED			(1 << 31)


#define WCH_ENABLE_MS(port, cflag)    ((port)->flags & WCH_UPF_HARDPPS_CD || (cflag) & CRTSCTS || !((cflag) & CLOCAL))        


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
#define WCH_SER_DEVNUM(x)   ((x)->index)
#else
#define WCH_SER_DEVNUM(x)   (MINOR((x)->device) - (x)->driver.minor_start)
#endif


struct ser_info;
struct ser_port;

struct ser_icount 
{
	__u32	cts;
	__u32	dsr;
	__u32	rng;
	__u32	dcd;
	__u32	rx;
	__u32	tx;
	__u32	frame;
	__u32	overrun;
	__u32	parity;
	__u32	brk;
	__u32	buf_overrun;
};


struct ser_info 
{
	struct tty_struct		*tty;
	struct circ_buf			xmit;
	unsigned int			flags;
	unsigned char			*tmpbuf;
	struct semaphore		tmpbuf_sem;
	int						blocked_open;
	struct tasklet_struct	tlet;

	wait_queue_head_t		open_wait;
	wait_queue_head_t		delta_msr_wait;
};


struct ser_driver 
{
	const char				*dev_name;
	int			 			major;
	int			 			minor;
	int			 			nr;
	struct ser_state		*state;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) 
	struct tty_driver		*tty_driver;
#else
    struct tty_driver   	tty_driver;
#endif    
};


struct ser_port 
{
	spinlock_t				lock;			
	unsigned int			iobase;			
	unsigned int			irq;			
	unsigned int			uartclk;		
	unsigned char			fifosize;		
	unsigned char			x_char;			
	unsigned char			iotype;			

	unsigned int			read_status_mask;	
	unsigned int			ignore_status_mask;	
	struct ser_info			*info;			
	struct ser_icount		icount;			


	unsigned int			flags;
	unsigned int			mctrl;			
	unsigned int			timeout;
	unsigned int			wch_type;		
	unsigned int			type;			
	unsigned int			custom_divisor;
	unsigned int			line;			


	int						board_enum;
	unsigned int			bus_number;		
	unsigned int			dev_number;	
	PCI_BOARD				pb_info;
	unsigned int			vector;			
	unsigned int			vector_mask;
	unsigned char			chip_flag;
	unsigned int			port_flag;
	unsigned int			baud_base;		
	int						rx_trigger;	
	unsigned char			ldisc_stop_rx;	
	
	unsigned int			setserial_flag;
};


struct ser_state 
{
	unsigned int			close_delay;
	unsigned int			closing_wait;
	int						count;
	struct ser_info			*info;
	struct ser_port			*port;
	struct semaphore		sem;		
};


static inline int ser_handle_break(struct ser_port *port)
{
	struct ser_info *info = port->info;

	if (info->flags & WCH_UPF_SAK)
	{
		do_SAK(info->tty);
	}
	return 0;
}	
	

static inline void ser_handle_dcd_change(struct ser_port *port, unsigned int status)
{
	struct ser_info *info = port->info;

	port->icount.dcd++;

	if (info->flags & WCH_UIF_CHECK_CD) 
	{
		if (status)
		{
			wake_up_interruptible(&info->open_wait);
		}
		else if (info->tty)
		{
			tty_hangup(info->tty);
		}
	}
}
	

#include <linux/tty_flip.h>

static inline void 
ser_insert_char(
        struct ser_port *port, 
        unsigned int status, 
        unsigned int overrun, 
        unsigned int ch, 
        unsigned int flag
        )
{
	struct tty_struct *tty = port->info->tty;

	if ((status & port->ignore_status_mask & ~overrun) == 0)
    {
		tty_insert_flip_char(tty, ch, flag);
    }


	if (status & ~port->ignore_status_mask & overrun)
    {
		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
    }
}


/*******************************************************
             wch serial port struct
*******************************************************/
struct wch_ser_port 
{
	struct ser_port port;
	struct timer_list	timer;	
	struct list_head	list;	
	
	unsigned int		capabilities;	
	unsigned char		ier;
	unsigned char		lcr;
	unsigned char		mcr;
	unsigned char		mcr_mask;	
	unsigned char		mcr_force;	
	unsigned char		lsr_break_flag;
};

/*-------------------------------------------------------------------------------

            				function and variable extern           

-------------------------------------------------------------------------------*/

// wch_devtable.c
extern PCI_BOARD wch_pci_board_conf[];

// wch_serial.c
extern int      wch_ser_register_ports(struct ser_driver *);
extern void     wch_ser_unregister_ports(struct ser_driver *);
extern int      wch_ser_register_driver(struct ser_driver *);
extern void     wch_ser_unregister_driver(struct ser_driver *);
extern int 		wch_ser_interrupt(struct wch_board *, struct wch_ser_port *);

// wch_main.c
extern struct wch_board			wch_board_table[WCH_BOARDS_MAX];
extern struct wch_ser_port	    wch_ser_table[WCH_SER_TOTAL_MAX + 1];
extern char wch_ser_ic_table[WCH_SER_PORT_MAX_UART][10];
extern char wch_port_remap[2][10];

