/*
 *
 *                SUNIX Multi-I/O Board Device Driver
 *
 *
 *
 *	Driver for SUNIX Multi-I/O Board device driver
 *	Based on drivers/char/serial.c, parport_pc.c, ppdev.c and lp.c
 *	by Linus Torvalds, Theodore Ts'o.
 *
 * 	This program is free software; you can redistribute it and/or modify
 * 	it under the terms of the GNU General Public License as published by
 * 	the Free Software Foundation; either version 2 of the License, or
 * 	(at your option) any later version.
 *
 * 	This program is distributed in the hope that it will be useful,
 * 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * 	GNU General Public License for more details.
 *
 * 	You should have received a copy of the GNU General Public License
 * 	along with this program; if not, write to the Free Software
 * 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *                                                    		Version: 2.0.0.0
 *                                                          Date: 2011/12/28
 */
#include "snx_common.h"
#include "driver_extd.h"


MODULE_AUTHOR(SNX_DRIVER_AUTHOR);
MODULE_DESCRIPTION(SNX_DRIVER_DESC);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18))
MODULE_LICENSE("GPL");
#endif


extern struct sunix_board			sunix_board_table[SNX_BOARDS_MAX];
extern struct sunix_ser_port	    sunix_ser_table[SNX_SER_TOTAL_MAX + 1];
extern struct sunix_par_port	    sunix_par_table[SNX_PAR_TOTAL_MAX];


char snx_ser_ic_table[SNX_SER_PORT_MAX_UART][10] =
{
	{"UNKNOWN"},
    {"SUN1889"},
    {"SUN1699"},
	{"SUNMATX"},
	{"SUN1999"}
};


char snx_par_ic_table[SNX_PAR_PORT_MAX_UART][10] =
{
	{"UNKNOWN"},
  	{"SUN1888"},
   	{"SUN1689"},
	{"SUNMATX"},
	{"SUN1999"}
};


char snx_port_remap[2][10] =
{
 	{"NON-REMAP"},
	{"REMAP"}
};


enum
{
// golden-serial
	GOLDEN_BOARD_TEST = 0,
	GOLDEN_BOARD_4027A,
 	GOLDEN_BOARD_4027D,
	GOLDEN_BOARD_4037A,
 	GOLDEN_BOARD_4037D,
	GOLDEN_BOARD_4036A3V,
	GOLDEN_BOARD_4056A,
	GOLDEN_BOARD_4056D,
	GOLDEN_BOARD_4056DW,
	GOLDEN_BOARD_4066A,
	GOLDEN_BOARD_4066R,
	GOLDEN_BOARD_8139,
	GOLDEN_BOARD_8139S,
	GOLDEN_BOARD_8159,
	GOLDEN_BOARD_8159S,
	GOLDEN_BOARD_8169,
	GOLDEN_BOARD_8169S,

// golden-parallel, support by system parport_pc driver
/*
	GOLDEN_BOARD_4008A,
	GOLDEN_BOARD_4018A,
*/

// golden-multi I/O
	GOLDEN_BOARD_4079A,
	GOLDEN_BOARD_4089A,
	GOLDEN_BOARD_4096A,

// matrix-serial
	MATRIX_BOARD_P1002,
	MATRIX_BOARD_P1004,
	MATRIX_BOARD_P1008,
	MATRIX_BOARD_P1016,
	MATRIX_BOARD_P2002,
	MATRIX_BOARD_P2004,
	MATRIX_BOARD_P2008,
	MATRIX_BOARD_P3002,
	MATRIX_BOARD_P3004,
	MATRIX_BOARD_P3008,

// sun1999-serial RS232
	SUN1999_BOARD_5027A,
	SUN1999_BOARD_5037A,
	SUN1999_BOARD_5056A,
	SUN1999_BOARD_5066A,
	SUN1999_BOARD_5016A,

	//sun1999-multi I/O
	SUN1999_BOARD_5069A,
	SUN1999_BOARD_5079A,
	SUN1999_BOARD_5099A,

	//sun1999-parallel

	SUN1999_BOARD_5008A,

	//sun1999-serial RS422/485
	SUN1999_BOARD_P2102,
	SUN1999_BOARD_P2104,
	SUN1999_BOARD_P2108,
	SUN1999_BOARD_P2116,

	//sun1999 3_in_1
	SUN1999_BOARD_P3104,
	SUN1999_BOARD_P3108,

	//cash drawer card
	SUN1999_BOARD_CASH_2S,
	SUN1999_BOARD_CASH_4S,

	//DIO
	SUN1999_BOARD_DIO0802,
	SUN1999_BOARD_DIO1604,
	SUN1999_BOARD_DIO3204,

};


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
static  struct pci_device_id	sunix_pci_board_id[] =
{
// golden-serial
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_TEST, 		0,	0,	GOLDEN_BOARD_TEST},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4027A, 	0,	0,	GOLDEN_BOARD_4027A},
 	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4027D, 	0,	0,	GOLDEN_BOARD_4027D},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4037A, 	0,	0,	GOLDEN_BOARD_4037A},
 	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4037D, 	0,	0,	GOLDEN_BOARD_4037D},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4036A3V,	0, 	0,	GOLDEN_BOARD_4036A3V},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056A,  	0, 	0,	GOLDEN_BOARD_4056A},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056D,  	0, 	0,	GOLDEN_BOARD_4056D},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056DW, 	0, 	0,	GOLDEN_BOARD_4056DW},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4066A,  	0, 	0,	GOLDEN_BOARD_4066A},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4066R,  	0, 	0,	GOLDEN_BOARD_4066R},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8139,   	0, 	0,	GOLDEN_BOARD_8139},
	{VENID_GOLDEN,	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8139S,  	0, 	0,	GOLDEN_BOARD_8139S},
 	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8159,   	0, 	0,	GOLDEN_BOARD_8159},
 	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8159S,  	0, 	0,	GOLDEN_BOARD_8159S},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8169,   	0, 	0,	GOLDEN_BOARD_8169},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8169S,  	0, 	0,	GOLDEN_BOARD_8169S},

// golden-parallel, support by system parport_pc driver
/*
	{VENID_GOLDEN,	DEVID_G_PARALL,	SUBVENID_GOLDEN,	SUBDEVID_4008A, 	0,	0,	GOLDEN_BOARD_4008A},
	{VENID_GOLDEN,	DEVID_G_PARALL,	SUBVENID_GOLDEN,	SUBDEVID_4018A, 	0,	0,	GOLDEN_BOARD_4018A},
*/

// golden-multi I/O
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4079A, 	0,	0,	GOLDEN_BOARD_4079A},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4089A, 	0,	0,	GOLDEN_BOARD_4089A},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4096A, 	0,	0,	GOLDEN_BOARD_4096A},

// matrix-serial
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1002, 	0,	0,	MATRIX_BOARD_P1002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1004, 	0,	0,	MATRIX_BOARD_P1004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1008, 	0,	0,	MATRIX_BOARD_P1008},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1016, 	0,	0,	MATRIX_BOARD_P1016},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2002, 	0,	0,	MATRIX_BOARD_P2002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2004, 	0,	0,	MATRIX_BOARD_P2004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2008, 	0,	0,	MATRIX_BOARD_P2008},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3002, 	0,	0,	MATRIX_BOARD_P3002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3004, 	0,	0,	MATRIX_BOARD_P3004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3008, 	0,	0,	MATRIX_BOARD_P3008},

	// sun1999-serial RS232
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5027A, 	0,	0,	SUN1999_BOARD_5027A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5037A, 	0,	0,	SUN1999_BOARD_5037A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5056A, 	0,	0,	SUN1999_BOARD_5056A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5066A, 	0,	0,	SUN1999_BOARD_5066A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5016A, 	0,	0,	SUN1999_BOARD_5016A},

	// sun1999-multi I/O
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5069A, 	0,	0,	SUN1999_BOARD_5069A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5079A, 	0,	0,	SUN1999_BOARD_5079A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5099A, 	0,	0,	SUN1999_BOARD_5099A},

	// sun1999-parallel
	{VENID_SUN1999,	DEVID_S_PARALL,	SUBVENID_SUN1999,	SUBDEVID_5008A, 	0,	0,	SUN1999_BOARD_5008A},

	// sun1999-serial RS422/485
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2102, 	0,	0,	SUN1999_BOARD_P2102},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2104, 	0,	0,	SUN1999_BOARD_P2104},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2108, 	0,	0,	SUN1999_BOARD_P2108},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2116, 	0,	0,	SUN1999_BOARD_P2116},

	// sun1999 3_in_1
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P3104, 	0,	0,	SUN1999_BOARD_P3104},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P3108, 	0,	0,	SUN1999_BOARD_P3108},

	//cash drawer card  2S
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_CASH_2S, 0,	0,	SUN1999_BOARD_CASH_2S},

	//cash drawer card  4S
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_CASH_4S, 0,	0,	SUN1999_BOARD_CASH_4S},

	//DIO
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO0802, 0,	0,	SUN1999_BOARD_DIO0802},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO1604, 0,	0,	SUN1999_BOARD_DIO1604},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO3204, 0,	0,	SUN1999_BOARD_DIO3204},

	{0}
};
MODULE_DEVICE_TABLE(pci, sunix_pci_board_id);
#else
typedef struct
{
	unsigned short vendor;
	unsigned short device;
	unsigned short subvendor;
	unsigned short subdevice;
	unsigned short driver_data;
	unsigned short part_number;
} sunix_pciInfo;


static sunix_pciInfo sunix_pci_board_id[] =
{
// golden-serial
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_TEST,		GOLDEN_BOARD_TEST},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4027A,		GOLDEN_BOARD_4027A},
 	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4027D,		GOLDEN_BOARD_4027D},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4037A,		GOLDEN_BOARD_4037A},
 	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4037D,		GOLDEN_BOARD_4037D},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4036A3V, GOLDEN_BOARD_4036A3V},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056A,  	GOLDEN_BOARD_4056A},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056D,  	GOLDEN_BOARD_4056D},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4056DW, 	GOLDEN_BOARD_4056DW},
	{VENID_GOLDEN,	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4066A,  	GOLDEN_BOARD_4066A},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_4066R,  	GOLDEN_BOARD_4066R},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8139,   	GOLDEN_BOARD_8139},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8139S,  	GOLDEN_BOARD_8139S},
 	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8159,   	GOLDEN_BOARD_8159},
 	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8159S,  	GOLDEN_BOARD_8159S},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8169,   	GOLDEN_BOARD_8169},
	{VENID_GOLDEN, 	DEVID_G_SERIAL, SUBVENID_GOLDEN, 	SUBDEVID_8169S,  	GOLDEN_BOARD_8169S},

// golden-parallel, support by system parport_pc driver
/*
	{VENID_GOLDEN,	DEVID_G_PARALL,	SUBVENID_GOLDEN,	SUBDEVID_4008A,		GOLDEN_BOARD_4008A},
	{VENID_GOLDEN,	DEVID_G_PARALL,	SUBVENID_GOLDEN,	SUBDEVID_4018A,		GOLDEN_BOARD_4018A},
*/

// golden-multi I/O
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4079A,		GOLDEN_BOARD_4079A},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4089A,		GOLDEN_BOARD_4089A},
	{VENID_GOLDEN,	DEVID_G_SERIAL,	SUBVENID_GOLDEN,	SUBDEVID_4096A,		GOLDEN_BOARD_4096A},

// matrix-serial
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1002, 	MATRIX_BOARD_P1002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1004, 	MATRIX_BOARD_P1004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1008, 	MATRIX_BOARD_P1008},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P1016, 	MATRIX_BOARD_P1016},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2002, 	MATRIX_BOARD_P2002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2004, 	MATRIX_BOARD_P2004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P2008, 	MATRIX_BOARD_P2008},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3002, 	MATRIX_BOARD_P3002},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3004, 	MATRIX_BOARD_P3004},
	{VENID_MATRIX,	DEVID_M_SERIAL,	SUBVENID_MATRIX,	SUBDEVID_P3008, 	MATRIX_BOARD_P3008},

	// sun1999-serial
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5027A, 	SUN1999_BOARD_5027A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5037A, 	SUN1999_BOARD_5037A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5056A, 	SUN1999_BOARD_5056A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5066A, 	SUN1999_BOARD_5066A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5016A, 	SUN1999_BOARD_5016A},

	// sun1999-multi I/O
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5069A, 	SUN1999_BOARD_5069A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5079A, 	SUN1999_BOARD_5079A},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_5099A, 	SUN1999_BOARD_5099A},

	// sun1999-parallel
	{VENID_SUN1999,	DEVID_S_PARALL,	SUBVENID_SUN1999,	SUBDEVID_5008A, 	SUN1999_BOARD_5008A},


	// sun1999-serial RS422/485
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2102, 	SUN1999_BOARD_P2102},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2104, 	SUN1999_BOARD_P2104},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2108, 	SUN1999_BOARD_P2108},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P2116, 	SUN1999_BOARD_P2116},

	// sun1999 3_in_1
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P3104, 	SUN1999_BOARD_P3104},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_P3108, 	SUN1999_BOARD_P3108},

	//cash drawer card
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_CASH_2S, SUN1999_BOARD_CASH_2S},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_CASH_4S, SUN1999_BOARD_CASH_4S},

	//DIO
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO0802, SUN1999_BOARD_DIO0802},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO1604, SUN1999_BOARD_DIO1604},
	{VENID_SUN1999,	DEVID_S_SERIAL,	SUBVENID_SUN1999,	SUBDEVID_DIO3204, SUN1999_BOARD_DIO3204},

	{0}
};
#endif


struct sunix_board			sunix_board_table[SNX_BOARDS_MAX];
struct sunix_ser_port	    sunix_ser_table[SNX_SER_TOTAL_MAX + 1];
struct sunix_par_port	    sunix_par_table[SNX_PAR_TOTAL_MAX];


static int snx_ser_port_total_cnt;
static int snx_par_port_total_cnt;

int snx_board_count = 0 ;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
static irqreturn_t sunix_interrupt(int irq, void *dev_id)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
static irqreturn_t sunix_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#else
static void sunix_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
    struct sunix_ser_port *sp = NULL;
    struct sunix_par_port *pp = NULL;
    struct sunix_board *sb = NULL;
    int i;
    int status = 0;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
    int handled = IRQ_NONE;
#endif

    for (i = 0; i < SNX_BOARDS_MAX; i++)
    {
        if (dev_id == &(sunix_board_table[i]))
        {
            sb = dev_id;
            break;
        }
    }

    if (i == SNX_BOARDS_MAX)
    {
        status = 1;
    }

    if (!sb)
    {
         status = 1;
    }

    if (sb->board_enum <= 0)
    {
         status = 1;
    }

    if (status != 0)
    {


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        return handled;
#else
        return;
#endif
    }


	if ((sb->ser_port > 0) && (sb->ser_isr != NULL))
	{
		sp = &sunix_ser_table[sb->ser_port_index];

    	if (!sp)
    	{
        	status = 1;
    	}

		status = sb->ser_isr(sb, sp);
	}


	if ((sb->par_port > 0) && (sb->par_isr != NULL))
	{
		pp = &sunix_par_table[sb->par_port_index];

    	if (!pp)
    	{
        	status = 1;
    	}

		status = sb->par_isr(sb, pp);
	}


    if (status != 0)
    {


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        return handled;
#else
        return;
#endif
    }



#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
    handled = IRQ_HANDLED;

    return handled;
#endif
}


static int sunix_pci_board_probe(void)
{
    struct sunix_board *sb;
    struct pci_dev *pdev = NULL;
    struct pci_dev *pdev_array[4] = {NULL, NULL, NULL, NULL};

    int sunix_pci_board_id_cnt;
    int tablecnt;
    int boardcnt;
    int i;
    unsigned short int sub_device_id;
    unsigned short int device_part_number;
    unsigned int			 bar3_base_add;

    int status;

    unsigned int			 	bar3_Byte5;
    unsigned int			 	bar3_Byte6;
    unsigned int			 	bar3_Byte7;
    unsigned int				oem_id;
    unsigned char				uart_type;
    unsigned char				gpio_type;
    unsigned char 			gpio_card_type;
    int 								gpio_ch_cnt;


	// clear and init some variable
    memset(sunix_board_table, 0, SNX_BOARDS_MAX * sizeof(struct sunix_board));

    for (i = 0; i < SNX_BOARDS_MAX; i++)
    {
        sunix_board_table[i].board_enum = -1;
        sunix_board_table[i].board_number = -1;
    }

    sunix_pci_board_id_cnt = (sizeof(sunix_pci_board_id) / sizeof(sunix_pci_board_id[0])) - 1;


	// search golden serial and multi-I/O board
	pdev = NULL;
	tablecnt = 0;
	boardcnt = 0;
	sub_device_id = 0;
	status = 0;

    while (tablecnt < sunix_pci_board_id_cnt)
    {

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        pdev = pci_get_device(VENID_GOLDEN, DEVID_G_SERIAL, pdev);
#else
        pdev = pci_find_device(VENID_GOLDEN, DEVID_G_SERIAL, pdev);
#endif

        if (pdev == NULL)
        {
           	tablecnt++;
           	continue;
		    }

        if ((tablecnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) || (pdev == pdev_array[3])))
        {
            continue;
        }

        pci_read_config_word(pdev, 0x2e, &sub_device_id);

        if (sub_device_id == 0)
        {
            printk("SNX Error: SUNIX Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
            printk("           subdevice id isn't vaild.\n\n");
            status = -EIO;
            return status;
        }

        if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
        {
            continue;
        }

        if (pdev == NULL)
        {
            printk("SNX Error: PCI device object is an NULL pointer !\n\n");
            status = -EIO;
            return status;
        }
        else
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            status = pci_enable_device(pdev);

            if (status != 0)
            {
                printk("SNX Error: SUNIX Board Enable Fail !\n\n");
                status = -ENXIO;
                return status;
            }
        }

        if (snx_pci_board_conf[tablecnt].part_number != 0x00)
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            continue;
        }

        boardcnt++;
        if (boardcnt > SNX_BOARDS_MAX)
        {
            printk("\n");
            printk("SNX Error: SUNIX Driver Module Support Four Boards In Maximum !\n\n");
            status = -ENOSPC;
            return status;
        }

        sb = &sunix_board_table[boardcnt-1];


        pdev_array[boardcnt-1] = pdev;
        sb->pdev = pdev;
        sb->bus_number = pdev->bus->number;
        sb->dev_number = PCI_SLOT(pdev->devfn);

        sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
        sb->pb_info = snx_pci_board_conf[sb->board_enum];

				sb->board_flag = sb->pb_info.board_flag;

        sb->board_number  =  boardcnt - 1;
    }

	// search golden parallel board
	pdev = NULL;
	tablecnt = 0;
	sub_device_id = 0;
	status = 0;

    while (tablecnt < sunix_pci_board_id_cnt)
    {

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        pdev = pci_get_device(VENID_GOLDEN, DEVID_G_PARALL, pdev);
#else
        pdev = pci_find_device(VENID_GOLDEN, DEVID_G_PARALL, pdev);
#endif

        if (pdev == NULL)
        {
           	tablecnt++;
           	continue;
		    }

        if ((tablecnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) || (pdev == pdev_array[3])))
        {
            continue;
        }

        pci_read_config_word(pdev, 0x2e, &sub_device_id);

        if (sub_device_id == 0)
        {
            printk("SNX Error: SUNIX Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
            printk("           subdevice id isn't vaild.\n\n");
            status = -EIO;
            return status;
        }

        if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
        {
            continue;
        }

        if (pdev == NULL)
        {
            printk("SNX Error: PCI device object is an NULL pointer !\n\n");
            status = -EIO;
            return status;
        }
        else
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            status = pci_enable_device(pdev);

            if (status != 0)
            {
                printk("SNX Error: SUNIX Board Enable Fail !\n\n");
                status = -ENXIO;
                return status;
            }
        }

        boardcnt++;
        if (boardcnt > SNX_BOARDS_MAX)
        {
            printk("\n");
            printk("SNX Error: SUNIX Driver Module Support Four Boards In Maximum !\n\n");
            status = -ENOSPC;
            return status;
        }

        sb = &sunix_board_table[boardcnt-1];

        pdev_array[boardcnt-1] = pdev;
        sb->pdev = pdev;
        sb->bus_number = pdev->bus->number;
        sb->dev_number = PCI_SLOT(pdev->devfn);

        sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
        sb->pb_info = snx_pci_board_conf[sb->board_enum];
		    sb->board_flag = sb->pb_info.board_flag;

        sb->board_number = boardcnt - 1;
    }

	// search matrix serial board
	pdev = NULL;
	tablecnt = 0;
	sub_device_id = 0;
	status = 0;

    while (tablecnt < sunix_pci_board_id_cnt)
    {

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        pdev = pci_get_device(VENID_MATRIX, DEVID_M_SERIAL, pdev);
#else
        pdev = pci_find_device(VENID_MATRIX, DEVID_M_SERIAL, pdev);
#endif

        if (pdev == NULL)
        {
           	tablecnt++;
           	continue;
        }

        if ((tablecnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) || (pdev == pdev_array[3])))
        {
            continue;
        }

        pci_read_config_word(pdev, 0x2e, &sub_device_id);

        if (sub_device_id == 0)
        {
            printk("SNX Error: SUNIX Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
            printk("           subdevice id isn't vaild.\n\n");
            status = -EIO;
            return status;
        }

        if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
        {
            continue;
        }

        if (pdev == NULL)
        {
            printk("SNX Error: PCI device object is an NULL pointer !\n\n");
            status = -EIO;
            return status;
        }
        else
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            status = pci_enable_device(pdev);

            if (status != 0)
            {
                printk("SNX Error: SUNIX Board Enable Fail !\n\n");
                status = -ENXIO;
                return status;
            }
        }

        boardcnt++;
        if (boardcnt > SNX_BOARDS_MAX)
        {
            printk("\n");
            printk("SNX Error: SUNIX Driver Module Support Four Boards In Maximum !\n\n");
            status = -ENOSPC;
            return status;
        }

        sb = &sunix_board_table[boardcnt-1];

        pdev_array[boardcnt-1] = pdev;
        sb->pdev = pdev;
        sb->bus_number = pdev->bus->number;
        sb->dev_number = PCI_SLOT(pdev->devfn);

        sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
        sb->pb_info = snx_pci_board_conf[sb->board_enum];
				sb->board_flag = sb->pb_info.board_flag;

        sb->board_number = boardcnt - 1;
    }


	// search sun1999 muti I/O board
	pdev = NULL;
	tablecnt = 0;
	sub_device_id = 0;
	status = 0;

	device_part_number = 0;
	bar3_base_add = 0;

	bar3_Byte5 = 0;
  bar3_Byte6 = 0;
  bar3_Byte7 = 0;
  oem_id = 0;
  uart_type = 0;
  gpio_type = 0;
  gpio_card_type = 0;
  gpio_ch_cnt = 0;

    while (tablecnt < sunix_pci_board_id_cnt)
    {

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        pdev = pci_get_device(VENID_SUN1999, DEVID_S_SERIAL, pdev);
#else
        pdev = pci_find_device(VENID_SUN1999, DEVID_S_SERIAL, pdev);
#endif

        if (pdev == NULL)
        {
           	tablecnt++;
           	continue;
			  }

        if ((tablecnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) || (pdev == pdev_array[3])))
        {
            continue;
        }

        pci_read_config_word(pdev, 0x2e, &sub_device_id);


        if (sub_device_id == 0)
        {
            printk("SNX Error: SUNIX Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
            printk("           subdevice id isn't vaild.\n\n");
            status = -EIO;
            return status;
        }

        if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
        {
            continue;
        }

        if (pdev == NULL)
        {
            printk("SNX Error: PCI device object is an NULL pointer !\n\n");
            status = -EIO;
            return status;
        }
        else
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            status = pci_enable_device(pdev);

            if (status != 0)
            {
                printk("SNX Error: SUNIX Board Enable Fail !\n\n");
                status = -ENXIO;
                return status;
            }
        }


        bar3_base_add = pci_resource_start(pdev, 3);

        device_part_number = inb(bar3_base_add + 5);

        bar3_Byte5 = device_part_number;
        bar3_Byte6 = inb(bar3_base_add + 0x06);
        bar3_Byte7 = inb(bar3_base_add + 0x07);

        gpio_card_type = ((bar3_Byte7 & 0x18)>>3);
        oem_id = ( bar3_Byte5 |(bar3_Byte6 << 8 )|(bar3_Byte7 << 16));

        uart_type = ((bar3_Byte5 & 0xc0)>>6);

        gpio_ch_cnt = ((bar3_Byte7 & 0x60)>>5);

        gpio_type = ((bar3_Byte7 & 0x80)>>7);

        if((gpio_ch_cnt == 0x00)&&(gpio_card_type == 0x01))
        {
        	gpio_ch_cnt = 6 ;
        }
        else if((gpio_ch_cnt == 0x00)&&(gpio_card_type == 0x02))
        {
        	gpio_ch_cnt = 8 ;
        }
        else if(gpio_ch_cnt == 0x01)
        {
        	gpio_ch_cnt = 16 ;
        }
        else if(gpio_ch_cnt == 0x02)
        {
        	gpio_ch_cnt = 32 ;
        }

        if (device_part_number != snx_pci_board_conf[tablecnt].part_number)
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            continue;
        }
        else if(gpio_card_type != snx_pci_board_conf[tablecnt].card_type)
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            continue;
        }
        else if(gpio_ch_cnt != snx_pci_board_conf[tablecnt].gpio_ch_cnt)
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            continue;
        }

        boardcnt++;
        if (boardcnt > SNX_BOARDS_MAX)
        {
            printk("\n");
            printk("SNX Error: SUNIX Driver Module Support Four Boards In Maximum !\n\n");
            status = -ENOSPC;
            return status;
        }

        sb = &sunix_board_table[boardcnt-1];


        pdev_array[boardcnt-1] = pdev;
        sb->pdev = pdev;
        sb->bus_number = pdev->bus->number;
        sb->dev_number = PCI_SLOT(pdev->devfn);

        sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
        sb->pb_info = snx_pci_board_conf[sb->board_enum];
				sb->board_flag = sb->pb_info.board_flag;

        sb->board_number = boardcnt - 1;

        sb->oem_id  				= oem_id ;
        sb->uart_cnt  			= sb->pb_info.num_serport ;
        sb->gpio_chl_cnt 	 	= gpio_ch_cnt ;
        sb->board_uart_type  = uart_type ;
        sb->board_gpio_card_type = gpio_card_type ;
        sb->board_gpio_type  = gpio_type ;
    }

		// search SUN1999 parallel board
		pdev = NULL;
		tablecnt = 0;
		sub_device_id = 0;
		status = 0;

    while (tablecnt < sunix_pci_board_id_cnt)
    {


#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
        pdev = pci_get_device(VENID_SUN1999, DEVID_S_PARALL, pdev);
#else
        pdev = pci_find_device(VENID_SUN1999, DEVID_S_PARALL, pdev);
#endif

        if (pdev == NULL)
        {
           	tablecnt++;
           	continue;
		    }

        if ((tablecnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) || (pdev == pdev_array[3])))
        {
            continue;
        }

        pci_read_config_word(pdev, 0x2e, &sub_device_id);

        if (sub_device_id == 0)
        {
            printk("SNX Error: SUNIX Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
            printk("           subdevice id isn't vaild.\n\n");
            status = -EIO;
            return status;
        }

        if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
        {
            continue;
        }

        if (pdev == NULL)
        {
            printk("SNX Error: PCI device object is an NULL pointer !\n\n");
            status = -EIO;
            return status;
        }
        else
        {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3))
            pci_disable_device(pdev);
#endif
            status = pci_enable_device(pdev);

            if (status != 0)
            {
                printk("SNX Error: SUNIX Board Enable Fail !\n\n");
                status = -ENXIO;
                return status;
            }
        }

        boardcnt++;
        if (boardcnt > SNX_BOARDS_MAX)
        {
            printk("\n");
            printk("SNX Error: SUNIX Driver Module Support Four Boards In Maximum !\n\n");
            status = -ENOSPC;
            return status;
        }

        sb = &sunix_board_table[boardcnt-1];

        pdev_array[boardcnt-1] = pdev;
        sb->pdev = pdev;
        sb->bus_number = pdev->bus->number;
        sb->dev_number = PCI_SLOT(pdev->devfn);

        sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
        sb->pb_info = snx_pci_board_conf[sb->board_enum];
				sb->board_flag = sb->pb_info.board_flag;

        sb->board_number = boardcnt - 1;
     }

	// print info
    if (boardcnt == 0)
    {
        printk("SNX Info : No SUNIX Multi-I/O Board Found !\n\n");
        status = -ENXIO;
        return status;
    }
    else
    {
      for (i = 0; i < SNX_BOARDS_MAX; i++)
      {
        sb = &sunix_board_table[i];
        if (sb->board_enum > 0)
        {
          printk("\n");
          if ((sb->pb_info.num_serport > 0) && (sb->pb_info.num_parport > 0))
          {
    				printk("SNX Info : Found SUNIX %s Series Board (%dS%dP),\n", sb->pb_info.board_name, sb->pb_info.num_serport, sb->pb_info.num_parport);
          }
          else if ((sb->pb_info.num_serport) > 0)
          {
            printk("SNX Info : Found SUNIX %s Series Board (%dS),\n", sb->pb_info.board_name, sb->pb_info.num_serport);
          }
          else
          {
            printk("SNX Info : Found SUNIX %s Series Board (%dP),\n", sb->pb_info.board_name, sb->pb_info.num_parport);
          }

    			printk("           bus number:%d, device number:%d\n\n", sb->bus_number, sb->dev_number);
        }
      }

      snx_board_count  = boardcnt ;
    }

    return status;
}


static int sunix_get_pci_board_conf(void)
{
    struct sunix_board *sb = NULL;
    struct pci_dev *pdev = NULL;
    int status = 0;
    int i;
    int j;

    for (i = 0; i < SNX_BOARDS_MAX; i++)
    {
        sb = &sunix_board_table[i];

        if (sb->board_enum > 0)
        {
        	pdev = sb->pdev;
          sb->ports = sb->pb_info.num_serport + sb->pb_info.num_parport;
					sb->ser_port = sb->pb_info.num_serport;
					sb->par_port = sb->pb_info.num_parport;

					snx_ser_port_total_cnt = snx_ser_port_total_cnt + sb->ser_port;
					snx_par_port_total_cnt = snx_par_port_total_cnt + sb->par_port;

					if (snx_ser_port_total_cnt > SNX_SER_TOTAL_MAX)
          {
          	printk("SNX Error: Too much serial port, maximum %d ports can be supported !\n\n", SNX_SER_TOTAL_MAX);
            status = -EIO;
            return status;
          }

					if (snx_par_port_total_cnt > SNX_PAR_SUPPORT_MAX)
          {
            printk("SNX Error: Too much parallel port, maximum %d ports can be supported !\n\n", SNX_PAR_SUPPORT_MAX);
            status = -EIO;
            return status;
          }


          for (j = 0; j < SNX_PCICFG_BAR_TOTAL; j++)
          {
						sb->bar_addr[j] = pci_resource_start(pdev, j);
					}

          sb->irq = sb->pdev->irq;

          if (sb->irq <= 0)
          {
          	printk("SNX Error: SUNIX Board %s Series (bus:%d device:%d), in configuartion space, irq isn't valid !\n\n",
            									  sb->pb_info.board_name,
                                sb->bus_number,
                                sb->dev_number
                                );

            status = -EIO;
            return status;
          }
        }
    }

    return status;
}


static int sunix_assign_resource(void)
{
	struct sunix_board *sb = NULL;
	struct sunix_ser_port *sp = NULL;
	struct sunix_par_port *pp = NULL;

  int status = 0;
	int i;
	int j;
	int k;
	int ser_n;
	int ser_port_index = 0;

	memset(sunix_ser_table, 0, (SNX_SER_TOTAL_MAX + 1) * sizeof(struct sunix_ser_port));
	memset(sunix_par_table, 0, (SNX_PAR_TOTAL_MAX) * sizeof(struct sunix_par_port));

	for (i = 0; i < SNX_BOARDS_MAX; i++)
	{
		sb = &sunix_board_table[i];

		if (sb->board_enum > 0)
		{
			if (sb->ser_port > 0)
			{
				sb->vector_mask = 0;

				// assign serial port resource
				ser_n = sb->ser_port_index = ser_port_index;

				sp = &sunix_ser_table[ser_n];

				if (sp == NULL)
				{
					status = -ENXIO;
					printk("SNX Error: Serial port table address error !\n");
					return status;
				}

				for (j = 0; j < sb->ser_port; j++, ser_n++, sp++)
				{
					sp->port.chip_flag = sb->pb_info.port[j].chip_flag;
					sp->port.iobase = sb->bar_addr[sb->pb_info.port[j].bar1] + sb->pb_info.port[j].offset1;

					if ((sb->board_flag & BOARDFLAG_REMAP) == BOARDFLAG_REMAP)
					{
						sp->port.vector = 0;
						sb->vector_mask = 0x00;
					}
					else
					{

						sp->port.vector = sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset;
						sb->vector_mask |= (1 << j);
					}
				}

				ser_port_index = ser_port_index + sb->ser_port;
			}


			// assign parallel port resource
			if (sb->par_port > 0)
			{
				k = 0;

				for (j = 0; j < SNX_PAR_TOTAL_MAX; j++)
				{
					if ((k + 1) > sb->par_port)
					{
						break;
					}

					if (j >= SNX_PAR_TOTAL_MAX)
					{
						status = -EACCES;
						printk("SNX Error: Too much parallel port, maximum %d ports can be supported !\n\n", SNX_PAR_TOTAL_MAX);
						return status;
					}

					pp = &sunix_par_table[j];

					if (pp == NULL)
					{
						status = -ENXIO;
						printk("SNX Error: Parallel port table address error !\n");
						return status;
					}

					if (pp->chip_flag != SUNNONE_HWID)
					{
						continue;
					}
					else
					{
						pp->chip_flag = sb->pb_info.port[k + sb->ser_port].chip_flag;

						pp->base = sb->bar_addr[sb->pb_info.port[k + sb->ser_port].bar1] + sb->pb_info.port[k + sb->ser_port].offset1;
						pp->base_hi = sb->bar_addr[sb->pb_info.port[k + sb->ser_port].bar2] + sb->pb_info.port[k + sb->ser_port].offset2;

						pp->bus_number = sb->bus_number;
						pp->dev_number = sb->dev_number;
						pp->board_enum = sb->board_enum;
						k++;
					}
				}
			}
		}
	}

	return status;
}


static int sunix_ser_port_table_init(void)
{
	struct sunix_board *sb = NULL;
	struct sunix_ser_port *sp = NULL;
  int status = 0;
	int i;
	int j;
	int n;
	int AHDC_State = 0;
	int RS422_State = 0;

	for (i = 0; i < SNX_BOARDS_MAX; i++)
	{
		sb = &sunix_board_table[i];

		if (sb == NULL)
		{
			status = -ENXIO;
			printk("SNX Error: Board table pointer error !\n");
			return status;
		}

		if ((sb->board_enum > 0) && (sb->ser_port > 0))
		{
			n = sb->ser_port_index;
			sp = &sunix_ser_table[n];

			if (sp == NULL)
			{
				status = -ENXIO;
				printk("SNX Error: Serial port table pointer error !\n");
				return status;
			}

			for (j = 0; j < sb->ser_port; j++, n++, sp++)
			{
				if( j < 4 )
				{
					AHDC_State = inb(sb->bar_addr[3]+2)& 0x0F & ( 0x01 << ( ( (j+1) - 1 ) % 4 ) );
					RS422_State = inb(sb->bar_addr[3]+3)& 0xF0 & ( 0x10 << ( ( (j+1) - 1 ) % 4 ) );
				}
				else if (j <8)
				{
					AHDC_State = inb(sb->bar_addr[1] + 0x32)& 0x0F&( 0x01 << ( ( (j+1)- 1 ) % 4 ) ) ;
					RS422_State = inb(sb->bar_addr[1] + 0x33)& 0xF0&( 0x10 << ( ( (j+1) - 1 ) % 4 ) ) ;
				}

				RS422_State = ( (RS422_State & 0xF0) >> 4 );

				sp->port.AHDC_State 		= AHDC_State >> ( ( (j+1) - 1 ) % 4 );
				sp->port.RS422_State 		= RS422_State >> ( ( (j+1) - 1 ) % 4 );

				sp->port.board_enum			= sb->board_enum;
				sp->port.bus_number			= sb->bus_number;
				sp->port.dev_number			= sb->dev_number;
				sp->port.baud_base			= 921600;
				sp->port.pb_info				= sb->pb_info;

				sp->port.irq 						= sb->irq;
				sp->port.line 					= n;
				sp->port.uartclk  			= sp->port.baud_base * 16;
				sp->port.iotype   			= SNX_UPIO_PORT;
				sp->port.flags 					= ASYNC_SHARE_IRQ;

				sp->port.ldisc_stop_rx		= 0;
				spin_lock_init(&sp->port.lock);

				if (sp->port.chip_flag == SUN1889_HWID)
				{
					sp->port.snx_type		= SNX_SER_PORT_SUN1889;
					sp->port.type			= PORT_SER_16650V2;
					sp->port.fifosize		= SUN1889_FIFOSIZE_SET;
					sp->port.rx_trigger		= SUN1889_TRIGGER_LEVEL_SET;
				}
				else if (sp->port.chip_flag == SUN1699_HWID)
				{
					sp->port.snx_type		= SNX_SER_PORT_SUN1699;
					sp->port.type			= PORT_SER_16650V2;
					sp->port.fifosize		= SUN1699_FIFOSIZE_SET;
					sp->port.rx_trigger		= SUN1699_TRIGGER_LEVEL_SET;
				}
				else if (sp->port.chip_flag == SUNMATX_HWID)
				{
					sp->port.snx_type		= SNX_SER_PORT_SUNMATX;
					sp->port.type			= PORT_SER_16750;
					sp->port.fifosize		= SUNMATX_FIFOSIZE_SET;
					sp->port.rx_trigger		= SUNMATX_TRIGGER_LEVEL_SET;
				}
				else if (sp->port.chip_flag == SUN1999_HWID)
				{
					sp->port.snx_type		= SNX_SER_PORT_SUN1999;
					sp->port.type			= PORT_SER_16750;
					sp->port.fifosize		= SUN1999_FIFOSIZE_SET;
					sp->port.rx_trigger		= SUN1999_TRIGGER_LEVEL_SET;
				}
				else
				{
					sp->port.snx_type		= SNX_SER_PORT_UNKNOWN;
					sp->port.type			= PORT_SER_16450;
					sp->port.fifosize		= DEFAULT_FIFOSIZE;
					sp->port.rx_trigger		= DEFAULT_TRIGGER_LEVEL;
				}


				if ((sb->pb_info.board_flag & BOARDFLAG_REMAP) == BOARDFLAG_REMAP)
				{
					sp->port.vector_mask 	= 0;
					sp->port.port_flag		= PORTFLAG_REMAP;
				}
				else
				{
					sp->port.vector_mask 	= sb->vector_mask;
					sp->port.port_flag		= PORTFLAG_NONE;
				}

				if ((sb->pb_info.board_flag & BOARDFLAG_16PORTS) == BOARDFLAG_16PORTS)
				{
						sp->port.port_flag		|= PORTFLAG_16PORTS;
				}

				sp->port.setserial_flag		= SNX_SER_BAUD_NOTSETSER;
			}

			sb->ser_isr = sunix_ser_interrupt;
		}
		else
		{
			sb->ser_isr = NULL;
		}
	}


	// release io resource
	for (i = 0; i < SNX_SER_TOTAL_MAX; i++)
	{
		sp = &sunix_ser_table[i];

		if (sp->port.iobase > 0)
		{
			release_region(sp->port.iobase, SNX_SER_ADDRESS_LENGTH);
		}
	}
    return status;
}


static int sunix_par_port_table_init(void)
{
	struct sunix_board *sb = NULL;
	struct sunix_par_port *pp = NULL;
    int status = 0;
	int i;
	int j;
	int k;

	for (i = 0; i < SNX_BOARDS_MAX; i++)
	{
		sb = &sunix_board_table[i];

		if (sb == NULL)
		{
			status = -ENXIO;
			printk("SNX Error: Board table pointer error !\n");
			return status;
		}

		if ((sb->board_enum > 0) && (sb->par_port > 0))
		{
			k = 0;
			for (j = 0; j < SNX_PAR_TOTAL_MAX; j++)
			{
				pp = &sunix_par_table[j];

				if (pp == NULL)
				{
					status = -ENXIO;
					printk("SNX Error: Parallel port table pointer error !\n");
					return status;
				}

				if ((k + 1) > sb->par_port)
				{
					break;
				}


				if ((pp->bus_number == sb->bus_number) && (pp->dev_number == sb->dev_number) && (pp->board_enum	= sb->board_enum))
				{
					pp->pb_info				= sb->pb_info;
					//pp->irq 				= sb->irq;
					pp->irq					= PARPORT_IRQ_NONE;
					pp->portnum				= j;

					if (pp->chip_flag == SUN1888_HWID)
					{
						pp->snx_type		= SNX_PAR_PORT_SUN1888;
					}
					else if (pp->chip_flag == SUN1689_HWID)
					{
						pp->snx_type		= SNX_PAR_PORT_SUN1689;
					}
					else if (pp->chip_flag == SUNMATX_HWID)
					{
						pp->snx_type		= SNX_PAR_PORT_SUNMATX;
					}
					else if (pp->chip_flag == SUN1999_HWID)
					{
						pp->snx_type		= SNX_PAR_PORT_SUN1999;
					}
					else
					{
						pp->snx_type		= SNX_PAR_PORT_UNKNOWN;
					}


					if ((sb->pb_info.board_flag & BOARDFLAG_REMAP) == BOARDFLAG_REMAP)
					{
						pp->port_flag		= PORTFLAG_REMAP;
					}
					else
					{
						pp->port_flag		= PORTFLAG_NONE;
					}

					sb->par_isr = NULL;
					k++;
				}
			}
		}
	}


	// release io resource
	for (i = 0; i < SNX_PAR_TOTAL_MAX; i++)
	{
		pp = &sunix_par_table[i];

		if (pp->base > 0)
		{
			release_region(pp->base, SNX_PAR_ADDRESS_LENGTH);


			release_region(pp->base, SNX_PAR_STD_ADDR_LENGTH);
			release_region(pp->base + SNX_PAR_STD_ADDR_LENGTH, SNX_PAR_ETD_ADDR_LENGTH);

			if (pp->base_hi > 0)
			{
				release_region(pp->base_hi, SNX_PAR_ADDRESS_LENGTH);

				release_region(pp->base_hi, SNX_PAR_STD_ADDR_LENGTH);
				release_region(pp->base_hi + SNX_PAR_STD_ADDR_LENGTH, SNX_PAR_ETD_ADDR_LENGTH);
			}
		}
	}

	return status;
}


#if SNX_DBG
void sunix_debug(void)
{
#if SNX_DBG_BOARD
	struct sunix_board *sb = NULL;
	int i;
#endif
#if SNX_DBG_SERPORT
	struct sunix_ser_port *sp = NULL;
	int j;
#endif
#if SNX_DBG_SERPORT
	struct sunix_par_port *pp = NULL;
	int k;
#endif


#if SNX_DBG_BOARD
	printk("\n");
	printk("======== board info ========\n");
	for (i = 0; i < SNX_BOARDS_MAX; i++)
	{
		sb = &sunix_board_table[i];
		if (sb->board_enum != -1)
		{
			printk(" name         : %s\n",   sb->pb_info.board_name);
			printk(" board_enum   : %d\n",   sb->board_enum);
			printk(" board_number : %d\n",   sb->board_number);
			printk(" irq          : %d\n",   sb->irq);
			printk(" vector_mask  : x%2x\n", sb->vector_mask);
			printk(" bar[0]       : x%4x\n", sb->bar_addr[0]);
			printk(" bar[1]       : x%4x\n", sb->bar_addr[1]);
			printk(" bar[2]       : x%4x\n", sb->bar_addr[2]);
			printk(" bar[3]       : x%4x\n", sb->bar_addr[3]);
			printk(" bar[4]       : x%4x\n", sb->bar_addr[4]);
			printk(" bar[5]       : x%4x\n", sb->bar_addr[5]);

			printk(" oem_id       : %4x\n", sb->oem_id);
			printk(" uart_cnt     : %d\n", sb->uart_cnt);
			printk(" gpio_chl_cnt          : %d\n", sb->gpio_chl_cnt);
			printk(" board_uart_type       : %2x\n", sb->board_uart_type);
			printk(" board_gpio_card_type  : %2x\n", sb->board_gpio_card_type);
			printk(" board_gpio_type   		 : %2x\n", sb->board_gpio_type);
		}
	}
	printk("============================\n");
	printk("\n");
#endif


#if SNX_DBG_SERPORT
	printk("\n");
	printk("======== ser  info ========\n");
	for (j = 0; j < SNX_SER_TOTAL_MAX; j++)
	{
		sp = &sunix_ser_table[j];
		if (sp->port.iobase)
		{
			printk(" number       : %d\n",   j);
			printk(" name         : %s\n",   sp->port.pb_info.board_name);
			printk(" iobase       : x%4x\n", sp->port.iobase);
			printk(" irq          : %d\n",   sp->port.irq);
			printk(" vector       : x%4x\n", sp->port.vector);
			printk(" vector_mask  : x%4x\n", sp->port.vector_mask);
			printk(" chip_flag    : x%2x\n", sp->port.chip_flag);
			printk(" port_flag    : x%2x\n", sp->port.port_flag);
			printk(" AHDC_State    : x%d\n", sp->port.AHDC_State);
			printk(" RS422_State    : x%d\n", sp->port.RS422_State);
			printk("----------------------------\n");
		}
	}
	printk("============================\n");
	printk("\n");
#endif


#if SNX_DBG_PARPORT
	printk("\n");
	printk("======== par  info ========\n");
	for (k = 0; k < SNX_PAR_TOTAL_MAX; k++)
	{
		pp = &sunix_par_table[k];
		if (pp->base)
		{
			printk(" number       : %d\n",   k);
			printk(" name         : %s\n",   pp->pb_info.board_name);
			printk(" base         : x%4x\n", (unsigned int)pp->base);
			printk(" base_hi      : x%4x\n", (unsigned int)pp->base_hi);
			printk(" chip_flag    : x%2x\n", pp->chip_flag);
			printk(" port_flag    : x%2x\n", pp->port_flag);
			printk("----------------------------\n");
		}
	}
	printk("============================\n");
	printk("\n");
#endif
}
#endif


int sunix_register_irq(void)
{
	struct sunix_board *sb = NULL;
	int status = 0;
	int i;

	for (i = 0; i < SNX_BOARDS_MAX; i++)
	{
		sb = &sunix_board_table[i];

		if (sb == NULL)
		{
			status = -ENXIO;
			printk("SNX Error: Board table pointer error !\n");
			return status;
		}

		if (sb->board_enum > 0)
		{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
			status = request_irq(sb->irq, sunix_interrupt, IRQF_SHARED, "snx", sb);
#else
      status = request_irq(sb->irq, sunix_interrupt, SA_SHIRQ, "snx", sb);
#endif

			if (status)
			{
	 			printk("SNX Error: SUNIX Multi-I/O %s Board(bus:%d device:%d), request\n", sb->pb_info.board_name, sb->bus_number, sb->dev_number);
	 			printk("           IRQ %d fail, IRQ %d may be conflit with another device.\n", sb->irq, sb->irq);
				return status;
			}

		}
	}

	return status;
}


void sunix_release_irq(void)
{
	struct sunix_board *sb = NULL;
    int i;

    for (i = 0; i < SNX_BOARDS_MAX; i++)
    {
        sb = &sunix_board_table[i];

        if (sb->board_enum > 0)
        {
            free_irq(sb->irq, sb);
        }
    }
}


static struct snx_ser_driver    sunix_ser_reg =
{
    .dev_name       = "ttySNX",
    .major          = SNX_TTY_MAJOR,
    .minor          = 0,
    .nr             = (SNX_SER_TOTAL_MAX + 1),

};


static int __init snx_init(void)
{
	int status = 0;

	printk("\n\n");
	printk("=====================  SUNIX Device Driver Module Install  =====================\n");
	printk("\n");
	printk("SNX Info : Loading SUNIX Multi-I/O Board Driver Module\n");
	printk("                                                       -- Date :    %s\n", SNX_DRIVER_DATE);
	printk("                                                       -- Version : %s\n\n", SNX_DRIVER_VERSION);

	snx_ser_port_total_cnt = snx_par_port_total_cnt = 0;

	status = sunix_pci_board_probe();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_get_pci_board_conf();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_assign_resource();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_ser_port_table_init();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_par_port_table_init();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_register_irq();
	if (status != 0)
	{
		goto step1_fail;
	}


	status = sunix_ser_register_driver(&sunix_ser_reg);
	if (status != 0)
	{
		goto step2_fail;
	}


   	status = sunix_ser_register_ports(&sunix_ser_reg);
    if (status != 0)
    {
        goto step3_fail;
    }


	if (snx_par_port_total_cnt > 0)
	{
		status = sunix_par_parport_init();
   		if (status != 0)
   		{
  			goto step4_fail;
   		}


		status = sunix_par_ppdev_init();
   		if (status != 0)
   		{
   			goto step5_fail;
   		}


		status = sunix_par_lp_init();
   		if (status != 0)
   		{
   			goto step6_fail;
   		}
	}


#if SNX_DBG
	sunix_debug();
#endif


	printk("================================================================================\n");
	return status;


	if (snx_par_port_total_cnt > 0)
	{
step6_fail:

		sunix_par_ppdev_exit();


step5_fail:

		sunix_par_parport_exit();


step4_fail:

		sunix_ser_unregister_ports(&sunix_ser_reg);
	}

step3_fail:

	sunix_ser_unregister_driver(&sunix_ser_reg);


step2_fail:

	sunix_release_irq();


step1_fail:

	printk("SNX Error: Couldn't Loading SUNIX Multi-I/O Board Driver Module correctly,\n");
	printk("           please reboot system and try again. If still can't loading driver,\n");
	printk("           contact support.\n\n");
	printk("================================================================================\n");
	return status;

}


static void __exit snx_exit(void)
{
	printk("\n\n");
	printk("====================  SUNIX Device Driver Module Uninstall  ====================\n");
	printk("\n");

	if (snx_par_port_total_cnt > 0)
	{
		sunix_par_lp_exit();

		sunix_par_ppdev_exit();

		sunix_par_parport_exit();
	}

	sunix_ser_unregister_ports(&sunix_ser_reg);

	sunix_ser_unregister_driver(&sunix_ser_reg);

	sunix_release_irq();

	printk("SNX Info : Unload SUNIX Multi-I/O Board Driver Module Done.\n");
	printk("================================================================================\n");
}

module_init(snx_init);
module_exit(snx_exit);

