lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:   Tue, 30 Mar 2021 14:37:07 +0800
From:   Moriis Ku <saumah@...il.com>
To:     lee.jones@...aro.org
Cc:     linux-kernel@...r.kernel.org, Morris <saumah@...il.com>
Subject: [PATCH] Add Driver for SUNIX PCI(e) I/O expansion board

From: Morris <saumah@...il.com>

The board provide UART, DIO and CAN functions.

Signed-off-by: Morris <saumah@...il.com>
---
 drivers/mfd/bus.c          | 1524 +++++++++++++++
 drivers/mfd/dio.c          |  529 +++++
 drivers/mfd/dio_pack.c     | 3126 +++++++++++++++++++++++++++++
 drivers/mfd/list.c         |   99 +
 drivers/mfd/mem.c          |   37 +
 drivers/mfd/sdc_define.h   |  668 +++++++
 drivers/mfd/sdc_function.h |   96 +
 drivers/mfd/sdc_include.h  |  108 +
 drivers/mfd/spi.c          |  420 ++++
 drivers/mfd/spi_pack.c     | 1506 ++++++++++++++
 drivers/mfd/uart.c         | 3796 ++++++++++++++++++++++++++++++++++++
 11 files changed, 11909 insertions(+)
 create mode 100644 drivers/mfd/bus.c
 create mode 100644 drivers/mfd/dio.c
 create mode 100644 drivers/mfd/dio_pack.c
 create mode 100644 drivers/mfd/list.c
 create mode 100644 drivers/mfd/mem.c
 create mode 100644 drivers/mfd/sdc_define.h
 create mode 100644 drivers/mfd/sdc_function.h
 create mode 100644 drivers/mfd/sdc_include.h
 create mode 100644 drivers/mfd/spi.c
 create mode 100644 drivers/mfd/spi_pack.c
 create mode 100644 drivers/mfd/uart.c

diff --git a/drivers/mfd/bus.c b/drivers/mfd/bus.c
new file mode 100644
index 000000000000..7820072e918f
--- /dev/null
+++ b/drivers/mfd/bus.c
@@ -0,0 +1,1524 @@
+/*
+ *	Driver for SUNIX PCI(e) expansion board
+ *	Based on drivers/tty/serial/8250/8250_pci.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.
+ */
+
+
+#include "sdc_include.h"
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+static  struct pci_device_id	sunix_pci_board_id[] = 
+{
+	{0x1fd4, 0x2000, 0x1fd4, 0x0001, 0, 0, 123},
+	{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[] = 
+{
+	{0x1fd4, 0x2000, 0x1fd4, 0x0001, 123},
+	{0}
+};
+#endif
+
+
+struct sunix_sdc_board			sunix_sdc_board_table[SUNIX_SDC_BOARD_MAX];
+struct sunix_sdc_uart_channel	sunix_sdc_uart_table[SUNIX_SDC_UART_MAX + 1];
+struct sunix_sdc_dio_channel	sunix_sdc_dio_table[SUNIX_SDC_DIO_MAX];
+struct sunix_sdc_spi_channel	sunix_sdc_spi_table[SUNIX_SDC_SPI_MAX];
+
+struct kmem_cache *				sunix_sdc_dio_pack_cache;
+struct kmem_cache *				sunix_sdc_spi_pack_cache;
+
+unsigned int					sunix_sdc_board_amount;
+unsigned int					sunix_sdc_uart_amount;
+unsigned int					sunix_sdc_dio_amount;
+unsigned int					sunix_sdc_spi_amount;
+
+
+#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_sdc_board *sb = NULL;
+	struct sunix_sdc_uart_channel *uart_chl = NULL;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int i, j, k;
+	int status = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	int handled = IRQ_NONE;
+#endif
+	unsigned int vector[8];
+	unsigned int event_header;
+	unsigned char chl_num;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		if (dev_id == &(sunix_sdc_board_table[i]))
+		{
+			sb = dev_id;
+			break;
+		}
+	}
+
+	if (i == SUNIX_SDC_BOARD_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
+	}
+
+	vector[0] = mem_rx32(sb->bar0_membase, 0, 0);
+	vector[1] = mem_rx32(sb->bar0_membase, 0, 1);
+	vector[2] = mem_rx32(sb->bar0_membase, 0, 2);
+	vector[3] = mem_rx32(sb->bar0_membase, 0, 3);
+	vector[4] = mem_rx32(sb->bar0_membase, 0, 4);
+	vector[5] = mem_rx32(sb->bar0_membase, 0, 5);
+	vector[6] = mem_rx32(sb->bar0_membase, 0, 6);
+	vector[7] = mem_rx32(sb->bar0_membase, 0, 7);
+
+	for (i = 0; i < 8; i++)
+	{
+		if (vector[i] & 0xffffffff)
+		{
+			for (j = 0; j < 32; j++)
+			{
+				if (vector[i] & (0x00000001 << j))
+				{
+					chl_num = (i * 8) + j;
+
+					event_header = mem_rx32(sb->bar0_membase, 0, 8 + chl_num);
+
+					for (k = 0; k < SUNIX_SDC_UART_MAX + 1; k++)
+					{
+						uart_chl = &sunix_sdc_uart_table[k];
+
+						// uart
+						if ((uart_chl->port.iobase != 0) && 
+							(uart_chl->port.bus_number == sb->bus_number) &&
+							(uart_chl->port.dev_number == sb->dev_number) &&
+							(uart_chl->port.cib_info.num == chl_num) &&
+							(uart_chl->port.cib_info.type == 0x01))
+						{
+							//printk("SUNIX: bus_num:%d, dev_num:%d, chl_num:%d, event:x%08x, ttySDC%d has interrupt\n", uart_chl->port.bus_number, uart_chl->port.dev_number, chl_num, event_header, uart_chl->port.line);
+							status = sb->uart_isr(uart_chl);
+						}
+					}
+
+					for (k = 0; k < SUNIX_SDC_DIO_MAX; k++)
+					{
+						dio_chl = &sunix_sdc_dio_table[k];
+
+						// dio
+						if ((dio_chl->info.memsize != 0) && 
+							(dio_chl->info.bus_number == sb->bus_number) &&
+							(dio_chl->info.dev_number == sb->dev_number) &&
+							(dio_chl->info.cib_info.num == chl_num) &&
+							(dio_chl->info.cib_info.type == 0x02))
+						{
+							//printk("SUNIX: bus_num:%d, dev_num:%d, chl_num:%d, event:x%08x, SDCDIO%d has interrupt\n", dio_chl->info.bus_number, dio_chl->info.dev_number, chl_num, event_header, dio_chl->info.line);
+							status = sb->dio_isr(dio_chl, event_header);
+						}
+					}
+
+					for (k = 0; k < SUNIX_SDC_SPI_MAX; k++)
+					{
+						spi_chl = &sunix_sdc_spi_table[k];
+
+						// spi
+						if ((spi_chl->info.memsize != 0) && 
+							(spi_chl->info.bus_number == sb->bus_number) &&
+							(spi_chl->info.dev_number == sb->dev_number) &&
+							(spi_chl->info.cib_info.num == chl_num) &&
+							(spi_chl->info.cib_info.type == 0x03))
+						{
+							//printk("SUNIX: bus_num:%d, dev_num:%d, chl_num:%d, event:x%08x, SDCSPI%d has interrupt\n", spi_chl->info.bus_number, spi_chl->info.dev_number, chl_num, event_header, spi_chl->info.line);
+							status = sb->spi_isr(spi_chl, event_header);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	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 snx_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	return 0;
+}
+
+
+static int snx_suspend_one(struct pci_dev *pdev, pm_message_t state)
+{
+	return  0;
+}
+
+
+static int snx_set_port_termios(struct snx_ser_state *state)
+{
+	struct tty_struct *tty = state->info->tty;
+	struct SNXTERMIOS *termios;
+	int retval = 0;
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	termios = &tty->termios;
+#else
+	termios = tty->termios;
+#endif
+
+	retval = snx_ser_startup(state, 0);
+
+	if (retval == 0)
+	{
+		snx_ser_update_termios(state);
+	}
+
+	return 0;
+}
+
+
+static int snx_resume_port_termios(struct snx_ser_info *info)
+{
+	struct snx_ser_state *state = NULL;
+	struct tty_struct *tty = info->tty ;
+
+
+	state = tty->driver_data;
+	snx_set_port_termios(state);
+
+	return 0;
+}
+
+
+static int snx_resume_port(struct sunix_sdc_uart_channel *uart_chl)
+{
+	struct snx_ser_port *port = &uart_chl->port;
+	struct snx_ser_info *info = port->info;
+
+
+	if (info)
+	{
+		snx_resume_port_termios(info);
+	}
+
+	return 0;
+}
+
+
+static int snx_resume_one(struct pci_dev *pdev)
+{
+	struct sunix_sdc_board *sb = pci_get_drvdata(pdev);
+	struct sunix_sdc_uart_channel *uart_chl = NULL;
+	int j;
+
+
+	if (sb == NULL)
+	{
+		return 0;
+	}
+
+	for (j = 0; j < sb->uart_amount; j++)
+	{
+		uart_chl = &sunix_sdc_uart_table[sb->uart_start_index + j];
+
+		if (uart_chl == NULL)
+		{
+			return 0;
+		}
+
+		if (uart_chl->port.suspended == 1)
+		{
+			snx_resume_port(uart_chl);
+		}
+	}
+
+	return 0;
+}
+
+
+static int sunix_pci_board_probe(void)
+{
+	struct sunix_sdc_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 board_amount = 0;
+	int i, j;
+	unsigned short int sub_device_id;
+	int status;
+	unsigned char data1B;
+	unsigned int data8B;
+
+
+	// clear and init some variable
+	memset(sunix_sdc_board_table, 0, SUNIX_SDC_BOARD_MAX * sizeof(struct sunix_sdc_board));
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sunix_sdc_board_table[i].board_enum = -1;
+		sunix_sdc_board_table[i].board_number = -1;
+		sunix_sdc_board_table[i].bar0_membase = NULL;
+		sunix_sdc_board_table[i].bar2_membase = NULL;
+	}
+	sunix_pci_board_id_cnt = (sizeof(sunix_pci_board_id) / sizeof(sunix_pci_board_id[0])) - 1;
+
+
+	// search 
+	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(0x1fd4, 0x2000, pdev);
+#else
+		pdev = pci_find_device(0x1fd4, 0x2000, 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("SUNIX: Board (bus:%d device:%d), in configuration space, subdevice id isn't vaild.\n", pdev->bus->number, PCI_SLOT(pdev->devfn));
+			status = -EIO;
+			return status;
+		}
+
+		if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
+		{
+			continue;
+		}
+
+		if (pdev == NULL)
+		{
+			printk("SUNIX: PCI device object is an NULL pointer !\n");
+			status = -EIO;
+			return status;
+		}
+		else
+		{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 3))
+			pci_disable_device(pdev);
+#endif
+			status = pci_enable_device(pdev);
+			if (status != 0)
+			{
+				printk("SUNIX: Board Enable Fail !\n");
+				status = -ENXIO;
+				return status;
+			}
+		}
+
+		if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM))
+		{
+			printk("SUNIX: Incorrect BAR0 configuration.\n");
+			status = -ENODEV;
+			return status;
+		}
+		if (!(pci_resource_flags(pdev, 1) & IORESOURCE_IO))
+		{
+			printk("SUNIX: Incorrect BAR1 configuration.\n");
+			status = -ENODEV;
+			return status;
+		}
+		if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM))
+		{
+			printk("SUNIX: Incorrect BAR2 configuration.\n");
+			status = -ENODEV;
+			return status;
+		}
+
+
+		board_amount++;
+		if (board_amount > SUNIX_SDC_BOARD_MAX)
+		{
+			printk("SUNIX: Driver support 4 boards in maximum !\n");
+			status = -ENOSPC;
+			return status;
+		}
+
+		sb = &sunix_sdc_board_table[board_amount-1];
+		pdev_array[board_amount-1] = pdev;
+
+
+		sb->pdev = pdev;
+		sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
+		sb->board_number = board_amount - 1;
+		sb->bus_number = pdev->bus->number;
+		sb->dev_number = PCI_SLOT(pdev->devfn);
+		sb->irq = pdev->irq;
+
+		sb->bar0_membase = mem_get_bar_ioremap(pdev, 0);
+		sb->bar1_iobase = pci_resource_start(pdev, 1);
+		sb->bar2_membase = mem_get_bar_ioremap(pdev, 2);
+		data8B = mem_rx32(sb->bar2_membase, 0, 0);
+		sb->major_version = data8B & 0x000000ff;
+		sb->minor_version = (data8B & 0x0000ff00) >> 8;
+		sb->aval_channels = (data8B & 0x00ff0000) >> 16;
+
+		j = 0;
+		memset(sb->model_name, 0, sizeof(sb->model_name));
+		for (i = 0; i < 4; i++)
+		{
+			data8B = mem_rx32(sb->bar2_membase, 0, 2 + i);
+
+			data1B = data8B & 0x000000ff;
+			if (data1B == 0x00)
+				break;
+			sb->model_name[j++] = data1B;
+
+			data1B = (data8B & 0x0000ff00) >> 8;
+			if (data1B == 0x00)
+				break;
+			sb->model_name[j++] = data1B;
+
+			data1B = (data8B & 0x00ff0000) >> 16;
+			if (data1B == 0x00)
+				break;
+			sb->model_name[j++] = data1B;
+
+			data1B = (data8B & 0xff000000) >> 24;
+			if (data1B == 0x00)
+				break;
+			sb->model_name[j++] = data1B;
+		}
+	}
+
+
+	if (board_amount == 0)
+	{
+		printk("SUNIX: No board found !\n");
+		status = -ENXIO;
+		return status;
+	}
+	else
+	{
+		for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+		{
+			sb = &sunix_sdc_board_table[i];
+
+			if (sb->board_enum > 0)
+			{
+				printk("SUNIX: Found %s board (bus_num:%d, dev_num:%d, maj_v:%d, min_vr:%d)\n", sb->model_name, sb->bus_number, sb->dev_number, sb->major_version, sb->minor_version);
+			}
+		}
+		sunix_sdc_board_amount  = board_amount;
+    }
+
+	return status;
+}
+
+
+static int sunix_get_pci_board_conf(void)
+{
+    struct sunix_sdc_board *sb = NULL;
+    int status = 0;
+    int i, j, k, l;
+	unsigned int data8B;
+	unsigned int cib_addr;
+	unsigned int cib_addr_offset;
+	unsigned short next_ptr;
+	int uart_amount;
+	int dio_amount;
+	int spi_amount;
+	unsigned int dio_io_mask = 0;
+	unsigned int dio_io_shift_bits = 0;
+	unsigned int dio_io_direction = 0;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb->board_enum > 0)
+		{
+			dio_io_shift_bits = 0;
+
+			data8B = mem_rx32(sb->bar2_membase, 0, 1);
+			next_ptr = data8B & 0x0000ffff;
+
+			uart_amount = 0;
+			dio_amount = 0;
+			spi_amount = 0;
+
+			cib_addr = 0;
+			for (j = 0; j < sb->aval_channels; j++)
+			{
+				cib_addr = next_ptr;
+				cib_addr_offset = 0;
+				data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+				sb->channel[j].num = data8B & 0x000000ff;
+				sb->channel[j].type = (data8B & 0x0000ff00) >> 8;
+				sb->channel[j].version = (data8B & 0x00ff0000) >> 16;
+				sb->channel[j].lengthInDW = (data8B & 0xff000000) >> 24;
+
+				data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+				next_ptr = data8B & 0x0000ffff;
+				sb->channel[j].capability = (data8B & 0x00ff0000) >> 16;
+				sb->channel[j].event_header_type = (data8B & 0xff000000) >> 24;
+
+				if ((sb->channel[j].capability & 0x03) == 0x03)
+				{
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].io_addr_offset = data8B & 0x00ffffff;
+					sb->channel[j].io_space_size = (data8B & 0xff000000) >> 24;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].mem_addr_offset = data8B;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].mem_space_size = data8B;
+				}
+				else if ((sb->channel[j].capability & 0x01) == 0x01)
+				{
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].io_addr_offset = data8B & 0x00ffffff;
+					sb->channel[j].io_space_size = (data8B & 0xff000000) >> 24;
+				}
+				else if ((sb->channel[j].capability & 0x02) == 0x02)
+				{
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].mem_addr_offset = data8B;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].mem_space_size = data8B;
+				}
+
+				if (sb->channel[j].type == 0x00)
+				{
+					// configuration controller
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].cfg_ic_model = data8B & 0x000000ff;
+				}
+
+				if (sb->channel[j].type == 0x01)
+				{
+					uart_amount++;
+					// uart controller
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].uart_tx_fifo_size = data8B & 0x0000ffff;
+					sb->channel[j].uart_rx_fifo_size = (data8B & 0xffff0000) >> 16;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].uart_significand_of_clock = data8B & 0x00ffffff;
+					sb->channel[j].uart_exponent_of_clock = (data8B & 0xff000000) >> 24;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].uart_RS232_cap = data8B & 0x00000001;
+					sb->channel[j].uart_RS422_cap = (data8B & 0x00000002) >> 1;
+					sb->channel[j].uart_RS485_cap = (data8B & 0x00000004) >> 2;
+					sb->channel[j].uart_AHDC_cap = (data8B & 0x00000008) >> 3;
+					sb->channel[j].uart_CS_cap = (data8B & 0x00000010) >> 4;
+					sb->channel[j].uart_auto_RS422485_cap = (data8B & 0x00000020) >> 5;
+					sb->channel[j].uart_RS422_termination_cap = (data8B & 0x00000040) >> 6;
+					sb->channel[j].uart_RS485_termination_cap = (data8B & 0x00000080) >> 7;
+					sb->channel[j].uart_RI_5V_cap = (data8B & 0x00000100) >> 8;
+					sb->channel[j].uart_RI_12V_cap = (data8B & 0x00000200) >> 9;
+					sb->channel[j].uart_DCD_5V_cap = (data8B & 0x00000400) >> 10;
+					sb->channel[j].uart_DCD_12V_cap = (data8B & 0x00000800) >> 11;
+					// switch to rs422 if support rs422
+					if ((sb->channel[j].uart_RS232_cap == 0x00) && (sb->channel[j].uart_RS422_cap == 0x01))
+					{
+						outb(0x02, sb->bar1_iobase + sb->channel[j].io_addr_offset + 0x0e);
+					}
+				}
+
+				if (sb->channel[j].type == 0x02)
+				{
+					dio_amount++;
+					// dio controller
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].dio_number_of_banks = data8B & 0x000000ff;
+					sb->channel[j].dio_shares_same_direction_ctrl_cap = (data8B & 0x00000100) >> 8;
+					sb->channel[j].dio_writing_setting_to_flash_cap = (data8B & 0x00000200) >> 9;
+
+					for (k = 0; k < sb->channel[j].dio_number_of_banks; k++)
+					{
+						data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+						sb->channel[j].dio_bank_cap[k].number_of_io = data8B & 0x000000ff;
+						sb->channel[j].dio_bank_cap[k].support_inputs = (data8B & 0x00000100) >> 8;
+						sb->channel[j].dio_bank_cap[k].support_outputs = (data8B & 0x00000200) >> 9;
+						sb->channel[j].dio_bank_cap[k].rising_edge_trigger_cap = (data8B & 0x00000400) >> 10;
+						sb->channel[j].dio_bank_cap[k].falling_edge_trigger_cap = (data8B & 0x00000800) >> 11;
+					}
+
+					dio_io_direction = mem_rx32(sb->bar2_membase, sb->channel[j].mem_addr_offset, 12);
+					for (k = 0; k < sb->channel[j].dio_number_of_banks; k++)
+					{
+						dio_io_mask = 0;
+						for (l = 0; l < sb->channel[j].dio_bank_cap[k].number_of_io; l++)
+						{
+							dio_io_mask |= 0x00000001;
+							if (l < (sb->channel[j].dio_bank_cap[k].number_of_io - 1))
+								dio_io_mask = dio_io_mask <<1;
+						}
+
+						if (k > 0)
+						{
+							dio_io_shift_bits += sb->channel[j].dio_bank_cap[k-1].number_of_io;
+							dio_io_mask = dio_io_mask << dio_io_shift_bits;
+						}
+
+						sb->channel[j].dio_bank_cap[k].io_mask = dio_io_mask;
+						sb->channel[j].dio_bank_cap[k].io_shift_bits = dio_io_shift_bits;
+					}
+				}
+
+				if (sb->channel[j].type == 0x03)
+				{
+					spi_amount++;
+					// spi controller
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].spi_significand_of_clock = data8B & 0x00ffffff;
+					sb->channel[j].spi_exponent_of_clock = (data8B & 0xff000000) >> 24;
+					data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+					sb->channel[j].spi_number_of_device = data8B & 0x000000ff;
+
+					for (k = 0; k < sb->channel[j].spi_number_of_device; k++)
+					{
+						data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+						sb->channel[j].spi_device_cap[k].type = data8B & 0x00000001;
+						sb->channel[j].spi_device_cap[k].number_of_gpio_input = (data8B & 0x00000f00) >> 8;
+						sb->channel[j].spi_device_cap[k].number_of_gpio_output = (data8B & 0x0000f000) >> 12;
+
+						memset(sb->channel[j].spi_device_cap[k].name, 0, 32);
+						data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+						sb->channel[j].spi_device_cap[k].name[0] = data8B & 0x000000ff;
+						sb->channel[j].spi_device_cap[k].name[1] = (data8B & 0x0000ff00) >> 8;
+						sb->channel[j].spi_device_cap[k].name[2] = (data8B & 0x00ff0000) >> 16;
+						sb->channel[j].spi_device_cap[k].name[3] = (data8B & 0xff000000) >> 24;
+						data8B = mem_rx32(sb->bar2_membase, cib_addr, cib_addr_offset++);
+						sb->channel[j].spi_device_cap[k].name[4] = data8B & 0x000000ff;
+						sb->channel[j].spi_device_cap[k].name[5] = (data8B & 0x0000ff00) >> 8;
+						sb->channel[j].spi_device_cap[k].name[6] = (data8B & 0x00ff0000) >> 16;
+						sb->channel[j].spi_device_cap[k].name[7] = (data8B & 0xff000000) >> 24;
+					}
+				}
+			}
+
+			sb->uart_amount = uart_amount;
+			sunix_sdc_uart_amount = sunix_sdc_uart_amount + sb->uart_amount;
+
+			if (sunix_sdc_uart_amount > SUNIX_SDC_UART_MAX)
+			{
+				printk("SUNIX: Too much UART channel, maximum %d channels can be supported !\n", SUNIX_SDC_UART_MAX);
+				status = -EIO;
+				return status;
+			}
+
+			sb->dio_amount = dio_amount;
+			sunix_sdc_dio_amount = sunix_sdc_dio_amount + sb->dio_amount;
+
+			if (sunix_sdc_dio_amount > SUNIX_SDC_DIO_MAX)
+			{
+				printk("SUNIX: Too much DIO channel, maximum %d channels can be supported !\n", SUNIX_SDC_DIO_MAX);
+				status = -EIO;
+				return status;
+			}
+
+			sb->spi_amount = spi_amount;
+			sunix_sdc_spi_amount = sunix_sdc_spi_amount + sb->spi_amount;
+
+			if (sunix_sdc_spi_amount > SUNIX_SDC_SPI_MAX)
+			{
+				printk("SUNIX: Too much SPI channel, maximum %d channels can be supported !\n", SUNIX_SDC_SPI_MAX);
+				status = -EIO;
+				return status;
+			}
+		}
+	}
+
+
+
+
+#if (ENABLE_DEBUG_SDC_BUS == 1)
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb->board_enum > 0)
+ 		{
+			printk("=================================\n");
+			printk("board_enum         : %d\n", sb->board_enum);
+			printk("board_number       : %d\n", sb->board_number);
+			printk("bus_number         : %d\n", sb->bus_number);
+			printk("dev_number         : %d\n", sb->dev_number);
+			printk("irq                : %d\n", sb->irq);
+			printk("bar0_membase       : %p\n", sb->bar0_membase);
+			printk("bar1_iobase        : x%08x\n", sb->bar1_iobase);
+			printk("bar2_membase       : %p\n", sb->bar2_membase);
+			printk("major_version      : %d\n", sb->major_version);
+			printk("minor_version      : %d\n", sb->minor_version);
+			printk("aval_channels      : %d\n", sb->aval_channels);
+			printk("model_name         : %s\n", sb->model_name);
+			printk("uart_amount        : %d\n", sb->uart_amount);
+			printk("dio_amount         : %d\n", sb->dio_amount);
+			printk("spi_amount         : %d\n", sb->spi_amount);
+			for (j = 0; j < sb->aval_channels; j++)
+			{
+				//
+				if (sb->channel[j].type == 0x00)
+				{
+					printk("------------------------------, CONFIG\n");
+					printk("num                : %d\n", sb->channel[j].num);
+					printk("type               : %d\n", sb->channel[j].type);
+					printk("version            : %d\n", sb->channel[j].version);
+					printk("lengthInDW         : %d\n", sb->channel[j].lengthInDW);
+					printk("capability         : x%02x\n", sb->channel[j].capability);
+					printk("event_header_type  : x%02d\n", sb->channel[j].event_header_type);
+					printk("mem_addr_offset    : x%08x\n", sb->channel[j].mem_addr_offset);
+					printk("mem_space_size     : %d\n", sb->channel[j].mem_space_size);
+					printk("cfg_ic_model       : x%02x\n", sb->channel[j].cfg_ic_model);
+					printk("------------------------------\n");
+				}
+				//
+				//
+				if (sb->channel[j].type == 0x01)
+				{
+					printk("------------------------------, UART\n");
+					printk("num                : %d\n", sb->channel[j].num);
+					printk("type               : %d\n", sb->channel[j].type);
+					printk("version            : %d\n", sb->channel[j].version);
+					printk("lengthInDW         : %d\n", sb->channel[j].lengthInDW);
+					printk("capability         : x%02x\n", sb->channel[j].capability);
+					printk("event_header_type  : x%02d\n", sb->channel[j].event_header_type);
+					printk("io_addr_offset     : x%08x\n", sb->channel[j].io_addr_offset);
+					printk("io_space_size      : %d\n", sb->channel[j].io_space_size);
+					printk("mem_addr_offset    : x%08x\n", sb->channel[j].mem_addr_offset);
+					printk("mem_space_size     : %d\n", sb->channel[j].mem_space_size);
+					printk("uart_tx_fifo_size  : %d\n", sb->channel[j].uart_tx_fifo_size);
+					printk("uart_rx_fifo_size  : %d\n", sb->channel[j].uart_rx_fifo_size);
+					printk("significand_clock  : %d\n", sb->channel[j].uart_significand_of_clock);
+					printk("exponent_clock     : %d\n", sb->channel[j].uart_exponent_of_clock);
+					printk("uart_RS232_cap     : %d\n", sb->channel[j].uart_RS232_cap);
+					printk("uart_RS422_cap     : %d\n", sb->channel[j].uart_RS422_cap);
+					printk("uart_RS485_cap     : %d\n", sb->channel[j].uart_RS485_cap);
+					printk("uart_AHDC_cap      : %d\n", sb->channel[j].uart_AHDC_cap);
+					printk("uart_CS_cap        : %d\n", sb->channel[j].uart_CS_cap);
+					printk("uart_auto_RS422485 : %d\n", sb->channel[j].uart_auto_RS422485_cap);
+					printk("uart_RS422_termin  : %d\n", sb->channel[j].uart_RS422_termination_cap);
+					printk("uart_RS485_termin  : %d\n", sb->channel[j].uart_RS485_termination_cap);
+					printk("uart_RI_5V_cap     : %d\n", sb->channel[j].uart_RI_5V_cap);
+					printk("uart_RI_12V_cap    : %d\n", sb->channel[j].uart_RI_12V_cap);
+					printk("uart_DCD_5V_cap    : %d\n", sb->channel[j].uart_DCD_5V_cap);
+					printk("uart_DCD_12V_cap   : %d\n", sb->channel[j].uart_DCD_12V_cap);
+					printk("------------------------------\n");
+				}
+				//
+				//
+				if (sb->channel[j].type == 0x02)
+				{
+					printk("------------------------------, DIO\n");
+					printk("num                : %d\n", sb->channel[j].num);
+					printk("type               : %d\n", sb->channel[j].type);
+					printk("version            : %d\n", sb->channel[j].version);
+					printk("lengthInDW         : %d\n", sb->channel[j].lengthInDW);
+					printk("capability         : x%02x\n", sb->channel[j].capability);
+					printk("event_header_type  : x%02d\n", sb->channel[j].event_header_type);
+					printk("mem_addr_offset    : x%08x\n", sb->channel[j].mem_addr_offset);
+					printk("mem_space_size     : %d\n", sb->channel[j].mem_space_size);
+					printk("dio_number_of_banks: %d\n", sb->channel[j].dio_number_of_banks);
+					printk("shares_same_direct : %d\n", sb->channel[j].dio_shares_same_direction_ctrl_cap);
+					printk("writing_setting    : %d\n", sb->channel[j].dio_writing_setting_to_flash_cap);
+					for (k = 0; k < sb->channel[j].dio_number_of_banks; k++)
+					{
+						printk("++++++++++++++++++++++++++++\n");
+						printk("number_of_io       : %d\n", sb->channel[j].dio_bank_cap[k].number_of_io);
+						printk("support_inputs     : %d\n", sb->channel[j].dio_bank_cap[k].support_inputs);
+						printk("support_outputs    : %d\n", sb->channel[j].dio_bank_cap[k].support_outputs);
+						printk("rising_trigger     : %d\n", sb->channel[j].dio_bank_cap[k].rising_edge_trigger_cap);
+						printk("falling_trigger    : %d\n", sb->channel[j].dio_bank_cap[k].falling_edge_trigger_cap);
+						printk("++++++++++++++++++++++++++++\n");
+					}
+					printk("------------------------------\n");
+				}
+				//
+				//
+				if (sb->channel[j].type == 0x03)
+				{
+					printk("------------------------------, SPI\n");
+					printk("num                : %d\n", sb->channel[j].num);
+					printk("type               : %d\n", sb->channel[j].type);
+					printk("version            : %d\n", sb->channel[j].version);
+					printk("lengthInDW         : %d\n", sb->channel[j].lengthInDW);
+					printk("capability         : x%02x\n", sb->channel[j].capability);
+					printk("event_header_type  : x%02d\n", sb->channel[j].event_header_type);
+					printk("io_addr_offset     : x%08x\n", sb->channel[j].io_addr_offset);
+					printk("io_space_size      : %d\n", sb->channel[j].io_space_size);
+					printk("mem_addr_offset    : x%08x\n", sb->channel[j].mem_addr_offset);
+					printk("mem_space_size     : %d\n", sb->channel[j].mem_space_size);
+					printk("significand_clock  : %d\n", sb->channel[j].spi_significand_of_clock);
+					printk("exponent_clock     : %d\n", sb->channel[j].spi_exponent_of_clock);
+					printk("spi_number_device  : %d\n", sb->channel[j].spi_number_of_device);
+					for (k = 0; k < sb->channel[j].spi_number_of_device; k++)
+					{
+						printk("++++++++++++++++++++++++++++\n");
+						printk("type               : %d\n", sb->channel[j].spi_device_cap[k].type);
+						printk("number_gpio_input  : %d\n", sb->channel[j].spi_device_cap[k].number_of_gpio_input);
+						printk("number_gpio_output : %d\n", sb->channel[j].spi_device_cap[k].number_of_gpio_output);
+						printk("name               : %s\n", sb->channel[j].spi_device_cap[k].name);
+						printk("++++++++++++++++++++++++++++\n");
+					}
+					printk("------------------------------\n");
+				}
+				//
+			}
+			printk("=================================\n");
+		}
+	}
+#endif
+	return status;
+}
+
+
+static int sunix_assign_resource(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_uart_channel *uart_chl = NULL;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+	int i;
+	int j;
+	int uart_index = 0;
+	int dio_index = 0;
+	int spi_index = 0;
+
+
+	memset(sunix_sdc_uart_table, 0, (SUNIX_SDC_UART_MAX + 1) * sizeof(struct sunix_sdc_uart_channel));
+	memset(sunix_sdc_dio_table, 0, SUNIX_SDC_DIO_MAX * sizeof(struct sunix_sdc_dio_channel));
+	memset(sunix_sdc_spi_table, 0, SUNIX_SDC_SPI_MAX * sizeof(struct sunix_sdc_spi_channel));
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb->board_enum > 0)
+		{
+			if (sb->uart_amount > 0)
+			{
+				sb->uart_start_index = uart_index;
+
+				for (j = 0; j < sb->aval_channels; j++)
+				{
+					if (sb->channel[j].type == 0x01)
+					{
+						uart_chl = &sunix_sdc_uart_table[uart_index];
+
+						uart_chl->port.iobase = sb->bar1_iobase + sb->channel[j].io_addr_offset;
+						uart_chl->port.iosize = sb->channel[j].io_space_size;
+						memcpy(&uart_chl->port.cib_info, &sb->channel[j], sizeof(struct sdc_cib));
+						memcpy(uart_chl->port.model_name, sb->model_name, sizeof(sb->model_name));
+
+						uart_index++;
+					}
+				}
+			}
+
+			if (sb->dio_amount > 0)
+			{
+				sb->dio_start_index = dio_index;
+
+				for (j = 0; j < sb->aval_channels; j++)
+				{
+					if (sb->channel[j].type == 0x02)
+					{
+						dio_chl = &sunix_sdc_dio_table[dio_index];
+
+						dio_chl->info.phy2_base_start = pci_resource_start(sb->pdev, 2);
+						dio_chl->info.membase = sb->bar2_membase;
+						dio_chl->info.memoffset = sb->channel[j].mem_addr_offset;
+						dio_chl->info.memsize = sb->channel[j].mem_space_size;
+						memcpy(&dio_chl->info.cib_info, &sb->channel[j], sizeof(struct sdc_cib));
+						memcpy(dio_chl->info.model_name, sb->model_name, sizeof(sb->model_name));
+
+						dio_index++;
+					}
+				}
+			}
+
+			if (sb->spi_amount > 0)
+			{
+				sb->spi_start_index = spi_index;
+
+				for (j = 0; j < sb->aval_channels; j++)
+				{
+					if (sb->channel[j].type == 0x03)
+					{
+						spi_chl = &sunix_sdc_spi_table[spi_index];
+
+						spi_chl->info.phy2_base_start = pci_resource_start(sb->pdev, 2);
+						spi_chl->info.membase = sb->bar2_membase;
+						spi_chl->info.memoffset = sb->channel[j].mem_addr_offset;
+						spi_chl->info.memsize = sb->channel[j].mem_space_size;
+						memcpy(&spi_chl->info.cib_info, &sb->channel[j], sizeof(struct sdc_cib));
+						memcpy(spi_chl->info.model_name, sb->model_name, sizeof(sb->model_name));
+
+						spi_index++;
+					}
+				}
+			}
+		}
+	}
+
+	return status;
+}
+
+
+static int sunix_uart_channel_table_init(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_uart_channel *uart_chl = NULL;
+	int status = 0;
+	int i;
+	int j;
+	int n;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb == NULL)
+		{
+			status = -ENXIO;
+			printk("SUNIX: Board table pointer error !\n");
+			return status;
+		}
+
+		if ((sb->board_enum > 0) && (sb->uart_amount > 0))
+		{
+			n = sb->uart_start_index;
+
+			uart_chl = &sunix_sdc_uart_table[n];
+
+			if (uart_chl == NULL)
+			{
+				status = -ENXIO;
+				printk("SUNIX: UART port table pointer error !\n");
+				return status;
+			}
+
+			for (j = 0; j < sb->uart_amount; j++, n++, uart_chl++)
+			{
+				uart_chl->port.board_enum	= sb->board_enum;
+				uart_chl->port.bus_number	= sb->bus_number;
+				uart_chl->port.dev_number	= sb->dev_number;
+				uart_chl->port.baud_base = 921600;
+
+				uart_chl->port.irq = sb->irq;
+				uart_chl->port.line = n;
+				uart_chl->port.uartclk = uart_chl->port.baud_base * 16;
+				uart_chl->port.iotype = SNX_UPIO_PORT;
+				uart_chl->port.flags = ASYNC_SHARE_IRQ;
+				uart_chl->port.ldisc_stop_rx = 0;
+
+				spin_lock_init(&uart_chl->port.lock);
+
+				uart_chl->port.type = PORT_SER_16750;
+				uart_chl->port.fifosize = uart_chl->port.cib_info.uart_rx_fifo_size - 8;
+				uart_chl->port.rx_trigger = uart_chl->port.cib_info.uart_rx_fifo_size / 2;
+
+				uart_chl->port.setserial_flag = SNX_SER_BAUD_NOTSETSER;
+			}
+
+			sb->uart_isr = sunix_ser_interrupt;
+		}
+		else
+		{
+			sb->uart_isr = NULL;
+		}
+	}
+
+    return status;
+}
+
+
+static int sunix_dio_channel_table_init(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+	int i;
+	int j;
+	int n;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+		if ((sb->board_enum > 0) && (sb->dio_amount > 0))
+		{
+			n = sb->dio_start_index;
+
+			dio_chl = &sunix_sdc_dio_table[n];
+			for (j = 0; j < sb->dio_amount; j++, n++, dio_chl++)
+			{
+				dio_chl->info.bus_number	= sb->bus_number;
+				dio_chl->info.dev_number	= sb->dev_number;
+				dio_chl->info.irq = sb->irq;
+				dio_chl->info.line = n;
+
+				spin_lock_init(&dio_chl->packLock);
+				SxxListInit(&dio_chl->packList);
+				init_waitqueue_head(&dio_chl->readWQ);
+				sema_init(&dio_chl->sem, 1);
+
+				dio_chl->isOpened = false;
+
+				dio_chl->incomeBuff = NULL;
+				dio_chl->outcomeBuff = NULL;
+				dio_chl->translateBuff = NULL;
+
+				do
+				{
+					dio_chl->incomeBuff = (unsigned char *)kmalloc(SUNIX_SDC_DIO_BUFF, GFP_KERNEL);
+					if (dio_chl->incomeBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(dio_chl->incomeBuff, 0, SUNIX_SDC_DIO_BUFF);
+
+					dio_chl->outcomeBuff = (unsigned char *)kmalloc(SUNIX_SDC_DIO_BUFF, GFP_KERNEL);
+					if (dio_chl->outcomeBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(dio_chl->outcomeBuff, 0, SUNIX_SDC_DIO_BUFF);
+
+					dio_chl->translateBuff = (unsigned char *)kmalloc(SUNIX_SDC_DIO_BUFF, GFP_KERNEL);
+					if (dio_chl->translateBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(dio_chl->translateBuff, 0, SUNIX_SDC_DIO_BUFF);
+
+				} while (false);
+
+				if (status != 0)
+				{
+					if (dio_chl->incomeBuff != NULL)
+					{
+						kfree(dio_chl->incomeBuff);
+						dio_chl->incomeBuff = NULL;
+					}
+
+					if (dio_chl->outcomeBuff != NULL)
+					{
+						kfree(dio_chl->outcomeBuff);
+						dio_chl->outcomeBuff = NULL;
+					}
+
+					if (dio_chl->translateBuff != NULL)
+					{
+						kfree(dio_chl->translateBuff);
+						dio_chl->translateBuff = NULL;
+					}
+					break;
+				}
+			}
+
+			sb->dio_isr = sunix_dio_interrupt;
+		}
+		else
+		{
+			sb->dio_isr = NULL;
+		}
+
+		if (status != 0)
+		{
+			break;
+		}
+	}
+
+    return status;
+}
+
+
+static void sunix_dio_channel_table_deinit(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int i;
+	int j;
+	int n;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+		if ((sb->board_enum > 0) && (sb->dio_amount > 0))
+		{
+			n = sb->dio_start_index;
+
+			dio_chl = &sunix_sdc_dio_table[n];
+			for (j = 0; j < sb->dio_amount; j++, n++, dio_chl++)
+			{
+				if (dio_chl->incomeBuff != NULL)
+				{
+					kfree(dio_chl->incomeBuff);
+					dio_chl->incomeBuff = NULL;
+				}
+
+				if (dio_chl->outcomeBuff != NULL)
+				{
+					kfree(dio_chl->outcomeBuff);
+					dio_chl->outcomeBuff = NULL;
+				}
+
+				if (dio_chl->translateBuff != NULL)
+				{
+					kfree(dio_chl->translateBuff);
+					dio_chl->translateBuff = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+static int sunix_spi_channel_table_init(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+	int i;
+	int j;
+	int n;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+		if ((sb->board_enum > 0) && (sb->spi_amount > 0))
+		{
+			n = sb->spi_start_index;
+
+			spi_chl = &sunix_sdc_spi_table[n];
+			for (j = 0; j < sb->spi_amount; j++, n++, spi_chl++)
+			{
+				spi_chl->info.bus_number	= sb->bus_number;
+				spi_chl->info.dev_number	= sb->dev_number;
+				spi_chl->info.irq = sb->irq;
+				spi_chl->info.line = n;
+
+				spin_lock_init(&spi_chl->packLock);
+				SxxListInit(&spi_chl->packList);
+				init_waitqueue_head(&spi_chl->readWQ);
+				sema_init(&spi_chl->sem, 1);
+
+				spi_chl->isOpened = false;
+
+				spi_chl->incomeBuff = NULL;
+				spi_chl->outcomeBuff = NULL;
+				spi_chl->translateBuff = NULL;
+
+				do
+				{
+					spi_chl->incomeBuff = (unsigned char *)kmalloc(SUNIX_SDC_SPI_BUFF, GFP_KERNEL);
+					if (spi_chl->incomeBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(spi_chl->incomeBuff, 0, SUNIX_SDC_SPI_BUFF);
+
+					spi_chl->outcomeBuff = (unsigned char *)kmalloc(SUNIX_SDC_SPI_BUFF, GFP_KERNEL);
+					if (spi_chl->outcomeBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(spi_chl->outcomeBuff, 0, SUNIX_SDC_SPI_BUFF);
+
+					spi_chl->translateBuff = (unsigned char *)kmalloc(SUNIX_SDC_SPI_BUFF, GFP_KERNEL);
+					if (spi_chl->translateBuff == NULL)
+					{
+						status = -ENOMEM;
+						break;
+					}
+					memset(spi_chl->translateBuff, 0, SUNIX_SDC_SPI_BUFF);
+
+				} while (false);
+
+				if (status != 0)
+				{
+					if (spi_chl->incomeBuff != NULL)
+					{
+						kfree(spi_chl->incomeBuff);
+						spi_chl->incomeBuff = NULL;
+					}
+
+					if (spi_chl->outcomeBuff != NULL)
+					{
+						kfree(spi_chl->outcomeBuff);
+						spi_chl->outcomeBuff = NULL;
+					}
+
+					if (spi_chl->translateBuff != NULL)
+					{
+						kfree(spi_chl->translateBuff);
+						spi_chl->translateBuff = NULL;
+					}
+					break;
+				}
+			}
+
+			sb->spi_isr = sunix_spi_interrupt;
+		}
+		else
+		{
+			sb->spi_isr = NULL;
+		}
+
+		if (status != 0)
+		{
+			break;
+		}
+	}
+
+    return status;
+}
+
+
+static void sunix_spi_channel_table_deinit(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int i;
+	int j;
+	int n;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+		if ((sb->board_enum > 0) && (sb->spi_amount > 0))
+		{
+			n = sb->spi_start_index;
+
+			spi_chl = &sunix_sdc_spi_table[n];
+			for (j = 0; j < sb->spi_amount; j++, n++, spi_chl++)
+			{
+				if (spi_chl->incomeBuff != NULL)
+				{
+					kfree(spi_chl->incomeBuff);
+					spi_chl->incomeBuff = NULL;
+				}
+
+				if (spi_chl->outcomeBuff != NULL)
+				{
+					kfree(spi_chl->outcomeBuff);
+					spi_chl->outcomeBuff = NULL;
+				}
+
+				if (spi_chl->translateBuff != NULL)
+				{
+					kfree(spi_chl->translateBuff);
+					spi_chl->translateBuff = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+int sunix_register_irq(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	int status = 0;
+	int i;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb == NULL)
+		{
+			status = -ENXIO;
+			printk("SUNIX: 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, "sunix_sdc", sb);
+#else
+			status = request_irq(sb->irq, sunix_interrupt, SA_SHIRQ, "sunix_sdc", sb);
+#endif
+			if (status)
+			{
+				printk("SUNIX: %s board (bus_num:%d dev_num:%d), request\n", sb->model_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_sdc_board *sb = NULL;
+	int i;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb->board_enum > 0)
+		{
+			free_irq(sb->irq, sb);
+		}
+	}
+}
+
+
+void sunix_release_iomap(void)
+{
+	struct sunix_sdc_board *sb = NULL;
+	int i;
+
+
+	for (i = 0; i < SUNIX_SDC_BOARD_MAX; i++)
+	{
+		sb = &sunix_sdc_board_table[i];
+
+		if (sb->board_enum > 0)
+		{
+			if (sb->bar0_membase != NULL)
+			{
+				iounmap(sb->bar0_membase);
+				sb->bar0_membase = NULL;
+			}
+
+			if (sb->bar2_membase != NULL)
+			{
+				iounmap(sb->bar2_membase);
+				sb->bar2_membase = NULL;
+			}
+		}
+	}
+}
+
+
+static struct pci_driver snx_pci_driver =
+{
+	.name			= "sunix_sdc",
+	.probe			= snx_pci_probe_one,
+	.suspend		= snx_suspend_one,
+	.resume			= snx_resume_one,
+	.id_table		= sunix_pci_board_id,
+};
+
+
+static int __init sunix_sdc_init(void)
+{
+	int status = 0;
+
+
+	sunix_sdc_board_amount = 0;
+	sunix_sdc_uart_amount = 0;
+	sunix_sdc_dio_amount = 0;
+	sunix_sdc_spi_amount = 0;
+
+
+	sunix_sdc_dio_pack_cache = kmem_cache_create("sunix_sdc_dio", sizeof(DIO_PACKAGE), 0, SLAB_HWCACHE_ALIGN, NULL);
+	if (sunix_sdc_dio_pack_cache == NULL)
+		goto step1_fail;
+
+	sunix_sdc_spi_pack_cache = kmem_cache_create("sunix_sdc_spi", sizeof(DIO_PACKAGE), 0, SLAB_HWCACHE_ALIGN, NULL);
+	if (sunix_sdc_spi_pack_cache == NULL)
+		goto step2_fail;
+
+	status = pci_register_driver(&snx_pci_driver);
+	if (status != 0)
+		goto step3_fail;
+
+	status = sunix_pci_board_probe();
+	if (status != 0)	
+		goto step4_fail;
+
+	status = sunix_get_pci_board_conf();
+	if (status != 0)
+		goto step5_fail;
+
+	status = sunix_assign_resource();
+	if (status != 0)
+		goto step5_fail;
+
+	status = sunix_uart_channel_table_init();
+	if (status != 0)
+		goto step5_fail;
+
+	status = sunix_dio_channel_table_init();
+	if (status != 0)
+		goto step5_fail;
+
+	status = sunix_spi_channel_table_init();
+	if (status != 0)
+		goto step6_fail;
+
+	status = sunix_register_irq();
+	if (status != 0)
+		goto step7_fail;
+
+	status = sunix_ser_register_driver();
+	if (status != 0)
+		goto step8_fail;
+
+	status = sunix_ser_register_ports();
+	if (status != 0)
+		goto step9_fail;
+
+	status = sunix_dio_register_channel();
+	if (status != 0)
+		goto step10_fail;
+
+	status = sunix_spi_register_channel();
+	if (status != 0)
+		goto step11_fail;
+
+
+	return status;
+
+
+step11_fail:
+	sunix_dio_unregister_channel();
+
+step10_fail:
+	sunix_ser_unregister_ports();
+
+step9_fail:
+	sunix_ser_unregister_driver();
+
+step8_fail:
+	sunix_release_irq();
+
+step7_fail:
+	sunix_spi_channel_table_deinit();
+
+step6_fail:
+	sunix_dio_channel_table_deinit();
+
+step5_fail:
+	sunix_release_iomap();
+
+step4_fail:
+	pci_unregister_driver(&snx_pci_driver);
+
+step3_fail:
+	kmem_cache_destroy(sunix_sdc_spi_pack_cache);
+
+step2_fail:
+	kmem_cache_destroy(sunix_sdc_dio_pack_cache);
+
+step1_fail:
+
+	return status;
+}
+
+
+static void __exit sunix_sdc_exit(void)
+{
+	sunix_spi_unregister_channel();
+
+	sunix_dio_unregister_channel();
+
+	sunix_ser_unregister_ports();
+
+	sunix_ser_unregister_driver();
+
+	sunix_release_irq();
+
+	sunix_spi_channel_table_deinit();
+
+	sunix_dio_channel_table_deinit();
+
+	sunix_release_iomap();
+
+	pci_unregister_driver(&snx_pci_driver);
+
+	kmem_cache_destroy(sunix_sdc_spi_pack_cache);
+
+	kmem_cache_destroy(sunix_sdc_dio_pack_cache);
+}
+
+
+module_init(sunix_sdc_init);
+module_exit(sunix_sdc_exit);
+
+
+MODULE_AUTHOR("SUNIX Co., Ltd.<info@...ix.com.tw>");
+MODULE_DESCRIPTION("SUNIX PCI(e) expansion board bus driver");
+MODULE_LICENSE("GPL");
+
+
diff --git a/drivers/mfd/dio.c b/drivers/mfd/dio.c
new file mode 100644
index 000000000000..216280194d1b
--- /dev/null
+++ b/drivers/mfd/dio.c
@@ -0,0 +1,529 @@
+
+
+#include "sdc_include.h"
+
+
+static int sunix_dio_open(struct inode *inode, struct file *file)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_DIO_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		dio_chl = &sunix_sdc_dio_table[line];
+		if (dio_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (dio_chl->isOpened == true)
+		{
+			status = -EBUSY;
+			break;
+		}
+
+
+		dio_chl->InputInvertEnableReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 2);
+		dio_chl->InputLatchRegPositiveEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 3);
+		dio_chl->InputLatchRegNegativeEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 4);
+		dio_chl->InputCounterIncrementCtrlRegPositiveEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 6);
+		dio_chl->InputCounterIncrementCtrlRegNegativeEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 7);
+		dio_chl->InputRisingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 8);
+		dio_chl->InputFallingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 9);
+		dio_chl->DirectionCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 12);
+		dio_chl->OutputInitialValueReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 13);
+
+		dio_chl->readDataReady = 0;
+		dio_chl->isOpened = true;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		try_module_get(THIS_MODULE);
+#else
+		MOD_INC_USE_COUNT;
+#endif
+
+	} while (false);
+
+	return status;
+}
+
+
+static int sunix_dio_release(struct inode *inode, struct file *file)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+	DLIST * pListHead = NULL;
+	DLIST * e = NULL;
+	unsigned long Flags;
+	PDIO_PACKAGE pPack = NULL;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_DIO_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		dio_chl = &sunix_sdc_dio_table[line];
+		if (dio_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (dio_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+		pListHead = &dio_chl->packList;
+		spin_lock_irqsave(&dio_chl->packLock, Flags);
+		while (!SxxListEmpty(pListHead))
+		{
+			e = SxxListGetNext(pListHead);
+			pPack = SUNIX_SDC_DIO_PACK_PTR(e);
+			if (pPack != NULL)
+			{
+				printk("SUNIX: DIO FREE pack, line:%d, pack:x%p, DataPtrx%p\n", dio_chl->info.line, pPack, pPack->DataPtr);
+				SxxListRemoveEntry(e);
+				if (pPack->DataPtr != NULL)
+				{
+					kfree(pPack->DataPtr);
+					pPack->DataPtr = NULL;
+				}
+				kmem_cache_free(sunix_sdc_dio_pack_cache, pPack);
+				pPack = NULL;
+			}
+		}
+		spin_unlock_irqrestore(&dio_chl->packLock, Flags);
+
+		dio_chl->isOpened = false;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		module_put(THIS_MODULE);
+#else
+		MOD_DEC_USE_COUNT;
+#endif
+
+	} while (false);
+
+	return status;
+}
+
+
+static long sunix_dio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_DIO_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		dio_chl = &sunix_sdc_dio_table[line];
+		if (dio_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (dio_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+	} while (false);
+
+	return status;
+}
+
+
+static ssize_t sunix_dio_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+	unsigned int outcomeLength = 0;
+
+
+	if (line > SUNIX_SDC_DIO_MAX)
+	{
+		return -ENODEV;
+	}
+	dio_chl = &sunix_sdc_dio_table[line];
+	if (dio_chl->info.memsize == 0)
+	{
+		return -ENODEV;
+	}
+	if (dio_chl->isOpened == false)
+	{
+		return -ENODEV;
+	}
+
+	if (count < sizeof(DIO_HEADER))
+	{
+		return -ENOMEM;
+	}
+
+
+	if (down_interruptible(&dio_chl->sem))
+	{
+		return -ERESTARTSYS;
+	}
+
+	if (dio_chl->readDataReady == 0)
+	{
+		up(&dio_chl->sem);
+
+		if ((file->f_flags & O_NONBLOCK))
+		{
+			return -EAGAIN;
+		}
+
+		wait_event_interruptible(dio_chl->readWQ, dio_chl->readDataReady == 1);
+
+		if (down_interruptible(&dio_chl->sem))
+		{
+			return -ERESTARTSYS;
+		}
+	}
+
+
+	do
+	{
+		status = sunix_dio_handle_outcome(dio_chl, count, &outcomeLength);
+		if (status != 0)
+		{
+			break;
+		}
+		if (count < outcomeLength)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		if (outcomeLength <= 0)
+		{
+			status = 0;
+			break;
+		}
+
+		if (copy_to_user((void *)buf, dio_chl->outcomeBuff, outcomeLength))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		status = outcomeLength;
+
+	} while (false);
+
+
+	dio_chl->readDataReady = 0;
+	up(&dio_chl->sem);
+
+	return status;
+}
+
+
+static ssize_t sunix_dio_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_DIO_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		dio_chl = &sunix_sdc_dio_table[line];
+		if (dio_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (dio_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+		if (count < sizeof(DIO_HEADER))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		if (count > (sizeof(DIO_HEADER) + DIO_MAX_DATA_LENGTH))
+		{
+			status = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(dio_chl->incomeBuff, (void *)buf, count))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		status = sunix_dio_handle_income(dio_chl, count);
+		if (status == 0)
+		{
+			status = count;
+		}
+
+	} while (false);
+
+	return status;
+}
+
+
+unsigned int sunix_dio_poll(struct file *file, poll_table *wait)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+	unsigned int mask = POLLOUT | POLLWRNORM;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_DIO_MAX)
+		{
+			mask = -ENODEV;
+			break;
+		}
+		dio_chl = &sunix_sdc_dio_table[line];
+		if (dio_chl->info.memsize == 0)
+		{
+			mask = -ENODEV;
+			break;
+		}
+		if (dio_chl->isOpened == false)
+		{
+			mask = -ENODEV;
+			break;
+		}
+
+
+		down(&dio_chl->sem);
+
+		poll_wait(file, &dio_chl->readWQ, wait);
+
+		if (dio_chl->readDataReady == 1)
+		{
+			mask |= (POLLIN | POLLRDNORM);
+		}
+
+		up(&dio_chl->sem);
+
+	} while (false);
+
+	return mask;
+}
+
+
+static int sunix_dio_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	add_uevent_var(env, "DEVMODE=%#o", 0666);
+	return 0;
+}
+
+
+static const struct file_operations sunix_sdc_dio_fops =
+{
+	.owner				= THIS_MODULE,
+	.open				= sunix_dio_open,
+	.release			= sunix_dio_release,
+	.unlocked_ioctl		= sunix_dio_ioctl,
+	.read				= sunix_dio_read,
+	.write 				= sunix_dio_write,
+	.poll				= sunix_dio_poll
+};
+
+
+static int sunix_sdc_dio_dev_major = 0;
+static struct class *sunix_sdc_dio_dev_class = NULL;
+
+
+int sunix_dio_register_channel(void)
+{
+	int err, i;
+	dev_t dev;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+
+
+	err = alloc_chrdev_region(&dev, 0, sunix_sdc_dio_amount, "sunix_sdc_dio");
+	if (err != 0)
+	{
+		return err;
+	}
+
+	sunix_sdc_dio_dev_major = MAJOR(dev);
+
+	sunix_sdc_dio_dev_class = class_create(THIS_MODULE, "sunix_sdc_dio");
+	sunix_sdc_dio_dev_class->dev_uevent = sunix_dio_uevent;
+
+	for (i = 0; i < sunix_sdc_dio_amount; i++)
+	{
+		dio_chl = &sunix_sdc_dio_table[i];
+		if ((dio_chl != NULL) && (dio_chl->info.membase != NULL))
+		{
+			request_mem_region(dio_chl->info.phy2_base_start + dio_chl->info.memoffset, dio_chl->info.memsize, "sunix_sdc_dio");
+
+			cdev_init(&dio_chl->cdev, &sunix_sdc_dio_fops);
+			dio_chl->cdev.owner = THIS_MODULE;
+
+			cdev_add(&dio_chl->cdev, MKDEV(sunix_sdc_dio_dev_major, i), 1);
+
+			device_create(sunix_sdc_dio_dev_class, NULL, MKDEV(sunix_sdc_dio_dev_major, i), NULL, "SDCDIO%d", i);
+		}
+	}
+
+	return 0;
+}
+
+
+void sunix_dio_unregister_channel(void)
+{
+	int i;
+	struct sunix_sdc_dio_channel *dio_chl = NULL;
+
+
+	for (i = 0; i < sunix_sdc_dio_amount; i++)
+	{
+		dio_chl = &sunix_sdc_dio_table[i];
+		if ((dio_chl != NULL) && (dio_chl->info.membase != NULL))
+		{
+			device_destroy(sunix_sdc_dio_dev_class, MKDEV(sunix_sdc_dio_dev_major, i));
+
+			release_mem_region(dio_chl->info.phy2_base_start + dio_chl->info.memoffset, dio_chl->info.memsize);
+		}
+	}
+
+	class_unregister(sunix_sdc_dio_dev_class);
+	class_destroy(sunix_sdc_dio_dev_class);
+
+	unregister_chrdev_region(MKDEV(sunix_sdc_dio_dev_major, 0), MINORMASK);
+}
+
+
+int sunix_dio_interrupt(struct sunix_sdc_dio_channel *dio_chl, unsigned int event_header)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	unsigned int InputValue = 0;
+	unsigned int BankInputValue = 0;
+	unsigned int BankInputDelta = 0;
+	int i = 0;
+	unsigned char TxBuff[256];
+	unsigned int TxLength = 0;
+	PDIO_HEADER pTxHeader = (PDIO_HEADER)TxBuff;
+	PDIO_PACKAGE pPack = NULL;
+	bool bCreatePackSuccess = false;
+	unsigned long Flags;
+
+
+	InputValue = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 0);
+
+	for (i = 0; i < cib_info->dio_number_of_banks; i++)
+	{
+		if ((cib_info->dio_bank_cap[i].io_mask & event_header) && (cib_info->dio_bank_cap[i].support_inputs == 0x01))
+		{
+			BankInputValue = ((InputValue & cib_info->dio_bank_cap[i].io_mask) >> cib_info->dio_bank_cap[i].io_shift_bits);
+			BankInputDelta = ((event_header & cib_info->dio_bank_cap[i].io_mask) >> cib_info->dio_bank_cap[i].io_shift_bits);
+
+
+			memset(TxBuff, 0, sizeof(TxBuff));
+			TxLength = 0;
+
+			pTxHeader->Version = 0x01;
+			pTxHeader->CmdResponseEventData = SDCDIO_EVENT;
+			pTxHeader->ResponseStatus = SDCDIO_STATUS_SUCCESS;
+			pTxHeader->Length = 9;
+			TxLength = sizeof(DIO_HEADER);
+
+			TxBuff[TxLength++] = i;
+
+			TxBuff[TxLength++] = (unsigned char)((BankInputDelta & 0xff000000) >> 24);
+			TxBuff[TxLength++] = (unsigned char)((BankInputDelta & 0x00ff0000) >> 16);
+			TxBuff[TxLength++] = (unsigned char)((BankInputDelta & 0x0000ff00) >> 8);
+			TxBuff[TxLength++] = (unsigned char)((BankInputDelta & 0x000000ff));
+
+			TxBuff[TxLength++] = (unsigned char)((BankInputValue & 0xff000000) >> 24);
+			TxBuff[TxLength++] = (unsigned char)((BankInputValue & 0x00ff0000) >> 16);
+			TxBuff[TxLength++] = (unsigned char)((BankInputValue & 0x0000ff00) >> 8);
+			TxBuff[TxLength++] = (unsigned char)((BankInputValue & 0x000000ff));
+
+
+			do
+			{
+				pPack = NULL;
+				bCreatePackSuccess = false;
+
+				pPack = kmem_cache_alloc(sunix_sdc_dio_pack_cache, GFP_ATOMIC);
+				if (pPack == NULL)
+				{
+					break;
+				}
+				memset(pPack, 0, sizeof(DIO_PACKAGE));
+
+				pPack->DataPtr = (unsigned char *)kmalloc(DIO_MAX_DATA_LENGTH, GFP_KERNEL);
+				if (pPack->DataPtr == NULL)
+				{
+					break;
+				}
+				memset(pPack->DataPtr, 0, DIO_MAX_DATA_LENGTH);
+
+				SxxListInit(&pPack->Entry);
+				memcpy(&pPack->Header, pTxHeader, sizeof(DIO_HEADER));
+				memcpy(pPack->DataPtr, TxBuff + sizeof(DIO_HEADER), pTxHeader->Length);
+
+				spin_lock_irqsave(&dio_chl->packLock, Flags);
+				//printk("SUNIX: DIO ALOC pack, line:%d, pack:x%p, DataPtrx%p\n", dio_chl->info.line, pPack, pPack->DataPtr);
+				SxxListInsertTail(&dio_chl->packList, &pPack->Entry);
+
+				dio_chl->readDataReady = 1;
+				wake_up_interruptible(&dio_chl->readWQ);
+				spin_unlock_irqrestore(&dio_chl->packLock, Flags);
+
+				bCreatePackSuccess = true;
+
+			} while (false);
+
+			if (bCreatePackSuccess == false)
+			{
+				if (pPack != NULL)
+				{
+					if (pPack->DataPtr != NULL)
+					{
+						kfree(pPack->DataPtr);
+						pPack->DataPtr = NULL;
+					}
+
+					kmem_cache_free(sunix_sdc_dio_pack_cache, pPack);
+					pPack = NULL;
+				}
+			}
+		}
+	}
+
+    return 0;
+}
+
+
diff --git a/drivers/mfd/dio_pack.c b/drivers/mfd/dio_pack.c
new file mode 100644
index 000000000000..29e623918a45
--- /dev/null
+++ b/drivers/mfd/dio_pack.c
@@ -0,0 +1,3126 @@
+
+
+#include "sdc_include.h"
+
+
+static void get_info(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int Address = 0;
+	int i = 0;
+	unsigned int BankDirection = 0;
+
+
+	do
+	{
+		Address = dio_chl->info.phy2_base_start + dio_chl->info.memoffset;
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?(28 + (cib_info->dio_number_of_banks * 18)):0;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		memcpy(&TrBuff[TrLength], dio_chl->info.model_name, 16);
+		TrLength += 16;
+		TrBuff[TrLength++] = dio_chl->info.bus_number;
+		TrBuff[TrLength++] = dio_chl->info.dev_number;
+		TrBuff[TrLength++] = dio_chl->info.line;
+		TrBuff[TrLength++] = (unsigned char)((Address & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x000000ff));
+		TrBuff[TrLength++] = (unsigned char)(dio_chl->info.irq);
+
+		TrBuff[TrLength++] = cib_info->version;
+		TrBuff[TrLength++] = cib_info->dio_number_of_banks;
+		TrBuff[TrLength++] = cib_info->dio_shares_same_direction_ctrl_cap;
+		TrBuff[TrLength++] = cib_info->dio_writing_setting_to_flash_cap;
+
+		for (i = 0; i < cib_info->dio_number_of_banks; i++)
+		{
+			BankDirection = (dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[i].io_mask) >>cib_info->dio_bank_cap[i].io_shift_bits;
+
+			TrBuff[TrLength++] = i;
+			TrBuff[TrLength++] = cib_info->dio_bank_cap[i].number_of_io;
+			TrBuff[TrLength++] = cib_info->dio_bank_cap[i].support_inputs;
+			TrBuff[TrLength++] = cib_info->dio_bank_cap[i].support_outputs;
+			TrBuff[TrLength++] = cib_info->dio_bank_cap[i].rising_edge_trigger_cap;
+			TrBuff[TrLength++] = cib_info->dio_bank_cap[i].falling_edge_trigger_cap;
+
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_mask & 0xff000000) >> 24);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_mask & 0x00ff0000) >> 16);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_mask & 0x0000ff00) >> 8);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_mask & 0x000000ff));
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_shift_bits & 0xff000000) >> 24);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_shift_bits & 0x00ff0000) >> 16);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_shift_bits & 0x0000ff00) >> 8);
+			TrBuff[TrLength++] = (unsigned char)((cib_info->dio_bank_cap[i].io_shift_bits & 0x000000ff));
+			TrBuff[TrLength++] = (unsigned char)((BankDirection & 0xff000000) >> 24);
+			TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x00ff0000) >> 16);
+			TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x0000ff00) >> 8);
+			TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x000000ff));
+		}
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_state(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankState = 0;
+	unsigned int InputReg = 0;
+	unsigned int OutputReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			InputReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 0);
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (input), 1, InputReg:x%08x\n", dio_chl->info.line, InputReg);
+			InputReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (input), 2, InputReg:x%08x\n", dio_chl->info.line, InputReg);
+			InputReg &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (input), 3, InputReg:x%08x\n", dio_chl->info.line, InputReg);
+
+			OutputReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 11);
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (output), 4, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+			OutputReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (output), 5, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+			OutputReg &= dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL (output), 6, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+
+			BankState = (InputReg | OutputReg) & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL, 7, BankState:x%08x\n", dio_chl->info.line, BankState);
+			BankState = BankState >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank state, INDIVIDUAL, 8, BankState:x%08x\n", dio_chl->info.line, BankState);
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 0);
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (input), 1, InputReg:x%08x\n", dio_chl->info.line, InputReg);
+			BankState = InputReg & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (input), 2, BankState:x%08x\n", dio_chl->info.line, BankState);
+			BankState = BankState >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (input), 3, BankState:x%08x\n", dio_chl->info.line, BankState);
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			OutputReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 11);
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (output), 1, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+			BankState = OutputReg & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (output), 2, BankState:x%08x\n", dio_chl->info.line, BankState);
+			BankState = BankState >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank state, WHOLE (output), 3, BankState:x%08x\n", dio_chl->info.line, BankState);
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankState & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankState & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankState & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankState & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_delta_state(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankDeltaState = 0;
+	unsigned int InputDeltaReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input delta state, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputDeltaReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 1);
+			//printk("SUNIX: DIO (%d), get bank input delta state, INDIVIDUAL, 1, InputDeltaReg:x%08x\n", dio_chl->info.line, InputDeltaReg);
+			InputDeltaReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input delta state, INDIVIDUAL, 2, InputDeltaReg:x%08x\n", dio_chl->info.line, InputDeltaReg);
+			InputDeltaReg &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input delta state, INDIVIDUAL, 3, InputDeltaReg:x%08x\n", dio_chl->info.line, InputDeltaReg);
+			BankDeltaState = InputDeltaReg >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input delta state, INDIVIDUAL, 4, BankDeltaState:x%08x\n", dio_chl->info.line, BankDeltaState);
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputDeltaReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 1);
+			//printk("SUNIX: DIO (%d), get bank input delta state, WHOLE (input), 1, InputDeltaReg:x%08x\n", dio_chl->info.line, InputDeltaReg);
+			BankDeltaState = InputDeltaReg & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input delta state, WHOLE (input), 2, BankDeltaState:x%08x\n", dio_chl->info.line, BankDeltaState);
+			BankDeltaState = BankDeltaState >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input delta state, WHOLE (input), 3, BankDeltaState:x%08x\n", dio_chl->info.line, BankDeltaState);
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankDeltaState & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankDeltaState & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankDeltaState & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankDeltaState & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_invert_enable(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputInvertEnable = 0;
+	unsigned int InputInvertEnableReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input inver enable, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputInvertEnableReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 2);
+			BankInputInvertEnable = InputInvertEnableReg;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, INDIVIDUAL, 1, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			BankInputInvertEnable &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, INDIVIDUAL, 2, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			BankInputInvertEnable &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, INDIVIDUAL, 3, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			BankInputInvertEnable = BankInputInvertEnable >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, INDIVIDUAL, 4, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+
+			dio_chl->InputInvertEnableReg = InputInvertEnableReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputInvertEnableReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 2);
+			BankInputInvertEnable = InputInvertEnableReg;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, WHOLE (input), 1, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			BankInputInvertEnable &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, WHOLE (input), 2, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			BankInputInvertEnable = BankInputInvertEnable >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input inver enable, WHOLE (input), 3, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+
+			dio_chl->InputInvertEnableReg = InputInvertEnableReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputInvertEnable & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputInvertEnable & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputInvertEnable & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputInvertEnable & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_invert_enable(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputInvertEnable = 0;
+	unsigned int InputInvertEnableRegByBank = 0;
+	unsigned int InputInvertEnableReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputInvertEnable  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputInvertEnable |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputInvertEnable |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputInvertEnable |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input inver enable, BankIndex:x%02x, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankIndex, BankInputInvertEnable);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			BankInputInvertEnable &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, 1, BankInputInvertEnable:x%08x\n", dio_chl->info.line, BankInputInvertEnable);
+			InputInvertEnableRegByBank = BankInputInvertEnable << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, 2, InputInvertEnableRegByBank:x%08x\n", dio_chl->info.line, InputInvertEnableRegByBank);
+			InputInvertEnableRegByBank &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, 3, InputInvertEnableRegByBank:x%08x\n", dio_chl->info.line, InputInvertEnableRegByBank);
+			InputInvertEnableReg = dio_chl->InputInvertEnableReg & ~cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, 4, InputInvertEnableReg:x%08x\n", dio_chl->info.line, InputInvertEnableReg);
+			InputInvertEnableReg |= InputInvertEnableRegByBank;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, INDIVIDUAL, 5, InputInvertEnableReg:x%08x\n", dio_chl->info.line, InputInvertEnableReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 2, InputInvertEnableReg);
+			dio_chl->InputInvertEnableReg = InputInvertEnableReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputInvertEnableRegByBank = BankInputInvertEnable << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, WHOLE (input), 1, InputInvertEnableRegByBank:x%08x\n", dio_chl->info.line, InputInvertEnableRegByBank);
+			InputInvertEnableRegByBank &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, WHOLE (input), 2, InputInvertEnableRegByBank:x%08x\n", dio_chl->info.line, InputInvertEnableRegByBank);
+			InputInvertEnableReg = dio_chl->InputInvertEnableReg & ~cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, WHOLE (input), 3, InputInvertEnableReg:x%08x\n", dio_chl->info.line, InputInvertEnableReg);
+			InputInvertEnableReg |= InputInvertEnableRegByBank;
+			//printk("SUNIX: DIO (%d), set bank input inver enable, WHOLE (input), 4, InputInvertEnableReg:x%08x\n", dio_chl->info.line, InputInvertEnableReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 2, InputInvertEnableReg);
+			dio_chl->InputInvertEnableReg = InputInvertEnableReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_latch_rising_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputLatch = 0;
+	unsigned int InputLatchRegPositiveEdgeR = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input latch rising edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputLatchRegPositiveEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 3);
+			BankInputLatch = InputLatchRegPositiveEdgeR;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, INDIVIDUAL, 1, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, INDIVIDUAL, 2, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, INDIVIDUAL, 3, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch = BankInputLatch >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, INDIVIDUAL, 4, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+
+			dio_chl->InputLatchRegPositiveEdgeR = InputLatchRegPositiveEdgeR;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputLatchRegPositiveEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 3);
+			BankInputLatch = InputLatchRegPositiveEdgeR;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, WHOLE (input), 1, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, WHOLE (input), 2, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch = BankInputLatch >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input latch rising edge, WHOLE (input), 3, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+
+			dio_chl->InputLatchRegPositiveEdgeR = InputLatchRegPositiveEdgeR;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_latch_rising_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputLatch = 0;
+	unsigned int InputLatchRegPositiveEdgeW = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputLatch  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input latch rising edge, BankIndex:x%02x, BankInputLatch:x%08x\n", dio_chl->info.line, BankIndex, BankInputLatch);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input latch rising edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputLatchRegPositiveEdgeW = BankInputLatch << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, INDIVIDUAL, 1, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+			InputLatchRegPositiveEdgeW &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, INDIVIDUAL, 2, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+			InputLatchRegPositiveEdgeW &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, INDIVIDUAL, 3, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+			InputLatchRegPositiveEdgeW |= (dio_chl->InputLatchRegPositiveEdgeW & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, INDIVIDUAL, 4, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 3, InputLatchRegPositiveEdgeW);
+			dio_chl->InputLatchRegPositiveEdgeW = InputLatchRegPositiveEdgeW;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputLatchRegPositiveEdgeW = BankInputLatch << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, WHOLE (input), 1, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+			InputLatchRegPositiveEdgeW &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, WHOLE (input), 2, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+			InputLatchRegPositiveEdgeW |= (dio_chl->InputLatchRegPositiveEdgeW & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input latch rising edge, WHOLE (input), 3, InputLatchRegPositiveEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegPositiveEdgeW);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 3, InputLatchRegPositiveEdgeW);
+			dio_chl->InputLatchRegPositiveEdgeW = InputLatchRegPositiveEdgeW;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_latch_falling_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputLatch = 0;
+	unsigned int InputLatchRegNegativeEdgeR = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input latch falling edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputLatchRegNegativeEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 4);
+			BankInputLatch = InputLatchRegNegativeEdgeR;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, INDIVIDUAL, 1, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, INDIVIDUAL, 2, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, INDIVIDUAL, 3, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch = BankInputLatch >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, INDIVIDUAL, 4, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+
+			dio_chl->InputLatchRegNegativeEdgeR = InputLatchRegNegativeEdgeR;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputLatchRegNegativeEdgeR = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 4);
+			BankInputLatch = InputLatchRegNegativeEdgeR;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, WHOLE (input), 1, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, WHOLE (input), 2, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+			BankInputLatch = BankInputLatch >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input latch falling edge, WHOLE (input), 3, BankInputLatch:x%08x\n", dio_chl->info.line, BankInputLatch);
+
+			dio_chl->InputLatchRegNegativeEdgeR = InputLatchRegNegativeEdgeR;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputLatch & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_latch_falling_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputLatch = 0;
+	unsigned int InputLatchRegNegativeEdgeW = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputLatch  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputLatch |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input latch falling edge, BankIndex:x%02x, BankInputLatch:x%08x\n", dio_chl->info.line, BankIndex, BankInputLatch);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input latch falling edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputLatchRegNegativeEdgeW = BankInputLatch << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, INDIVIDUAL, 1, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+			InputLatchRegNegativeEdgeW &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, INDIVIDUAL, 2, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+			InputLatchRegNegativeEdgeW &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, INDIVIDUAL, 3, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+			InputLatchRegNegativeEdgeW |= (dio_chl->InputLatchRegNegativeEdgeW & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, INDIVIDUAL, 4, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 4, InputLatchRegNegativeEdgeW);
+			dio_chl->InputLatchRegNegativeEdgeW = InputLatchRegNegativeEdgeW;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputLatchRegNegativeEdgeW = BankInputLatch << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, WHOLE (input), 1, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+			InputLatchRegNegativeEdgeW &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, WHOLE (input), 2, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+			InputLatchRegNegativeEdgeW |= (dio_chl->InputLatchRegNegativeEdgeW & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input latch falling edge, WHOLE (input), 3, InputLatchRegNegativeEdgeW:x%08x\n", dio_chl->info.line, InputLatchRegNegativeEdgeW);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 4, InputLatchRegNegativeEdgeW);
+			dio_chl->InputLatchRegNegativeEdgeW = InputLatchRegNegativeEdgeW;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_counter_reset(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputCounterReset = 0;
+	unsigned int InputCounterResetReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputCounterReset  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputCounterReset |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputCounterReset |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputCounterReset |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input counter reset, BankIndex:x%02x, BankInputCounterReset:x%08x\n", dio_chl->info.line, BankIndex, BankInputCounterReset);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input counter reset, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputCounterResetReg = BankInputCounterReset << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter reset, INDIVIDUAL, 1, InputCounterResetReg:x%08x\n", dio_chl->info.line, InputCounterResetReg);
+			InputCounterResetReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter reset, INDIVIDUAL, 2, InputCounterResetReg:x%08x\n", dio_chl->info.line, InputCounterResetReg);
+			InputCounterResetReg &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input counter reset, INDIVIDUAL, 3, InputCounterResetReg:x%08x\n", dio_chl->info.line, InputCounterResetReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 5, InputCounterResetReg);
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputCounterResetReg = BankInputCounterReset << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter reset, WHOLE (input), 1, InputCounterResetReg:x%08x\n", dio_chl->info.line, InputCounterResetReg);
+			InputCounterResetReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter reset, WHOLE (input), 2, InputCounterResetReg:x%08x\n", dio_chl->info.line, InputCounterResetReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 5, InputCounterResetReg);
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_counter_increment_rising_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputCounterInc = 0;
+	unsigned int InputCounterIncrementCtrlRegPositiveEdge = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputCounterIncrementCtrlRegPositiveEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 6);
+			BankInputCounterInc = InputCounterIncrementCtrlRegPositiveEdge;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, INDIVIDUAL, 1, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, INDIVIDUAL, 2, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, INDIVIDUAL, 3, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc = BankInputCounterInc >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, INDIVIDUAL, 4, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+
+			dio_chl->InputCounterIncrementCtrlRegPositiveEdge = InputCounterIncrementCtrlRegPositiveEdge;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputCounterIncrementCtrlRegPositiveEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 6);
+			BankInputCounterInc = InputCounterIncrementCtrlRegPositiveEdge;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, WHOLE (input), 1, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, WHOLE (input), 2, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc = BankInputCounterInc >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input counter increment rising edge, WHOLE (input), 3, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+
+			dio_chl->InputCounterIncrementCtrlRegPositiveEdge = InputCounterIncrementCtrlRegPositiveEdge;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_counter_increment_rising_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputCounterInc = 0;
+	unsigned int InputCounterIncrementCtrlRegPositiveEdge = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputCounterInc  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, BankIndex:x%02x, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankIndex, BankInputCounterInc);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputCounterIncrementCtrlRegPositiveEdge = BankInputCounterInc << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, INDIVIDUAL, 1, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+			InputCounterIncrementCtrlRegPositiveEdge &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, INDIVIDUAL, 2, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+			InputCounterIncrementCtrlRegPositiveEdge &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, INDIVIDUAL, 3, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+			InputCounterIncrementCtrlRegPositiveEdge |= (dio_chl->InputCounterIncrementCtrlRegPositiveEdge & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, INDIVIDUAL, 4, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 6, InputCounterIncrementCtrlRegPositiveEdge);
+			dio_chl->InputCounterIncrementCtrlRegPositiveEdge = InputCounterIncrementCtrlRegPositiveEdge;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputCounterIncrementCtrlRegPositiveEdge = BankInputCounterInc << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, WHOLE (input), 1, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+			InputCounterIncrementCtrlRegPositiveEdge &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, WHOLE (input), 2, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+			InputCounterIncrementCtrlRegPositiveEdge |= (dio_chl->InputCounterIncrementCtrlRegPositiveEdge & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input counter increment rising edge, WHOLE (input), 3, InputCounterIncrementCtrlRegPositiveEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegPositiveEdge);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 6, InputCounterIncrementCtrlRegPositiveEdge);
+			dio_chl->InputCounterIncrementCtrlRegPositiveEdge = InputCounterIncrementCtrlRegPositiveEdge;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_counter_increment_falling_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputCounterInc = 0;
+	unsigned int InputCounterIncrementCtrlRegNegativeEdge = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputCounterIncrementCtrlRegNegativeEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 7);
+			BankInputCounterInc = InputCounterIncrementCtrlRegNegativeEdge;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, INDIVIDUAL, 1, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, INDIVIDUAL, 2, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, INDIVIDUAL, 3, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc = BankInputCounterInc >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, INDIVIDUAL, 4, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+
+			dio_chl->InputCounterIncrementCtrlRegNegativeEdge = InputCounterIncrementCtrlRegNegativeEdge;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputCounterIncrementCtrlRegNegativeEdge = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 7);
+			BankInputCounterInc = InputCounterIncrementCtrlRegNegativeEdge;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, WHOLE (input), 1, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, WHOLE (input), 2, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+			BankInputCounterInc = BankInputCounterInc >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input counter increment falling edge, WHOLE (input), 3, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankInputCounterInc);
+
+			dio_chl->InputCounterIncrementCtrlRegNegativeEdge = InputCounterIncrementCtrlRegNegativeEdge;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputCounterInc & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_counter_increment_falling_edge(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputCounterInc = 0;
+	unsigned int InputCounterIncrementCtrlRegNegativeEdge = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputCounterInc  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputCounterInc |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, BankIndex:x%02x, BankInputCounterInc:x%08x\n", dio_chl->info.line, BankIndex, BankInputCounterInc);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputCounterIncrementCtrlRegNegativeEdge = BankInputCounterInc << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, INDIVIDUAL, 1, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+			InputCounterIncrementCtrlRegNegativeEdge &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, INDIVIDUAL, 2, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+			InputCounterIncrementCtrlRegNegativeEdge &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, INDIVIDUAL, 3, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+			InputCounterIncrementCtrlRegNegativeEdge |= (dio_chl->InputCounterIncrementCtrlRegNegativeEdge & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, INDIVIDUAL, 4, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 7, InputCounterIncrementCtrlRegNegativeEdge);
+			dio_chl->InputCounterIncrementCtrlRegNegativeEdge = InputCounterIncrementCtrlRegNegativeEdge;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputCounterIncrementCtrlRegNegativeEdge = BankInputCounterInc << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, WHOLE (input), 1, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+			InputCounterIncrementCtrlRegNegativeEdge &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, WHOLE (input), 2, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+			InputCounterIncrementCtrlRegNegativeEdge |= (dio_chl->InputCounterIncrementCtrlRegNegativeEdge & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input counter increment falling edge, WHOLE (input), 3, InputCounterIncrementCtrlRegNegativeEdge:x%08x\n", dio_chl->info.line, InputCounterIncrementCtrlRegNegativeEdge);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 7, InputCounterIncrementCtrlRegNegativeEdge);
+			dio_chl->InputCounterIncrementCtrlRegNegativeEdge = InputCounterIncrementCtrlRegNegativeEdge;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_rising_event_ctrl(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputEventCtrl = 0;
+	unsigned int InputRisingEventCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input rising event ctrl, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputRisingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 8);
+			BankInputEventCtrl = InputRisingEventCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, INDIVIDUAL, 1, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, INDIVIDUAL, 2, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, INDIVIDUAL, 3, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl = BankInputEventCtrl >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, INDIVIDUAL, 4, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+
+			dio_chl->InputRisingEventCtrlReg = InputRisingEventCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputRisingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 8);
+			BankInputEventCtrl = InputRisingEventCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, WHOLE (input), 1, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, WHOLE (input), 2, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl = BankInputEventCtrl >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input rising event ctrl, WHOLE (input), 3, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+
+			dio_chl->InputRisingEventCtrlReg = InputRisingEventCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_rising_event_ctrl(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputEventCtrl = 0;
+	unsigned int InputRisingEventCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputEventCtrl  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input rising event ctrl, BankIndex:x%02x, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankIndex, BankInputEventCtrl);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].rising_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input rising event ctrl, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputRisingEventCtrlReg = BankInputEventCtrl << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, INDIVIDUAL, 1, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+			InputRisingEventCtrlReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, INDIVIDUAL, 2, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+			InputRisingEventCtrlReg &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, INDIVIDUAL, 3, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+			InputRisingEventCtrlReg |= (dio_chl->InputRisingEventCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, INDIVIDUAL, 4, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 8, InputRisingEventCtrlReg);
+			dio_chl->InputRisingEventCtrlReg = InputRisingEventCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputRisingEventCtrlReg = BankInputEventCtrl << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, WHOLE (input), 1, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+			InputRisingEventCtrlReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, WHOLE (input), 2, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+			InputRisingEventCtrlReg |= (dio_chl->InputRisingEventCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input rising event ctrl, WHOLE (input), 3, InputRisingEventCtrlReg:x%08x\n", dio_chl->info.line, InputRisingEventCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 8, InputRisingEventCtrlReg);
+			dio_chl->InputRisingEventCtrlReg = InputRisingEventCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_falling_event_ctrl(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputEventCtrl = 0;
+	unsigned int InputFallingEventCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input falling event ctrl, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputFallingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 9);
+			BankInputEventCtrl = InputFallingEventCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, INDIVIDUAL, 1, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, INDIVIDUAL, 2, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, INDIVIDUAL, 3, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl = BankInputEventCtrl >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, INDIVIDUAL, 4, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+
+			dio_chl->InputFallingEventCtrlReg = InputFallingEventCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputFallingEventCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 9);
+			BankInputEventCtrl = InputFallingEventCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, WHOLE (input), 1, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, WHOLE (input), 2, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+			BankInputEventCtrl = BankInputEventCtrl >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank input falling event ctrl, WHOLE (input), 3, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankInputEventCtrl);
+
+			dio_chl->InputFallingEventCtrlReg = InputFallingEventCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankInputEventCtrl & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_input_falling_event_ctrl(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankInputEventCtrl = 0;
+	unsigned int InputFallingEventCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankInputEventCtrl  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankInputEventCtrl |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank input falling event ctrl, BankIndex:x%02x, BankInputEventCtrl:x%08x\n", dio_chl->info.line, BankIndex, BankInputEventCtrl);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].falling_edge_trigger_cap != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank input falling event ctrl, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			InputFallingEventCtrlReg = BankInputEventCtrl << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, INDIVIDUAL, 1, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+			InputFallingEventCtrlReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, INDIVIDUAL, 2, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+			InputFallingEventCtrlReg &= ~dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, INDIVIDUAL, 3, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+			InputFallingEventCtrlReg |= (dio_chl->InputFallingEventCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, INDIVIDUAL, 4, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 9, InputFallingEventCtrlReg);
+			dio_chl->InputFallingEventCtrlReg = InputFallingEventCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			InputFallingEventCtrlReg = BankInputEventCtrl << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, WHOLE (input), 1, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+			InputFallingEventCtrlReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, WHOLE (input), 2, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+			InputFallingEventCtrlReg |= (dio_chl->InputFallingEventCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank input falling event ctrl, WHOLE (input), 3, InputFallingEventCtrlReg:x%08x\n", dio_chl->info.line, InputFallingEventCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 9, InputFallingEventCtrlReg);
+			dio_chl->InputFallingEventCtrlReg = InputFallingEventCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_state(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankState = 0;
+	unsigned int OutputWriteEnableReg = 0;
+	unsigned int OutputReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankState  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankState |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankState |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankState |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank state, BankIndex:x%02x, BankState:x%08x\n", dio_chl->info.line, BankIndex, BankState);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_outputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_OUTPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank state, INDIVIDUAL, no output\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_OUTPUT_IO;
+				break;
+			}
+
+			OutputWriteEnableReg = dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank state, INDIVIDUAL, 1, OutputWriteEnableReg:x%08x\n", dio_chl->info.line, OutputWriteEnableReg);
+			OutputReg = BankState << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank state, INDIVIDUAL, 2, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+			OutputReg &= OutputWriteEnableReg;
+			//printk("SUNIX: DIO (%d), set bank state, INDIVIDUAL, 3, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 10, OutputWriteEnableReg);
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 11, OutputReg);
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			nStatus = SDCDIO_NO_OUTPUT_IO;
+			break;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			OutputWriteEnableReg = cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank state, WHILE (output), 1, OutputWriteEnableReg:x%08x\n", dio_chl->info.line, OutputWriteEnableReg);
+			OutputReg = BankState << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank state, WHILE (output), 2, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+			OutputReg &= OutputWriteEnableReg;
+			//printk("SUNIX: DIO (%d), set bank state, WHILE (output), 3, OutputReg:x%08x\n", dio_chl->info.line, OutputReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 10, OutputWriteEnableReg);
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 11, OutputReg);
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_direction(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankDirection = 0;
+	unsigned int DirectionCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank direction, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+
+			DirectionCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 12);
+			BankDirection = DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank direction, INDIVIDUAL, 1, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank direction, INDIVIDUAL, 2, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection = BankDirection >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank direction, INDIVIDUAL, 3, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			DirectionCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 12);
+			BankDirection = DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (input), 1, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (input), 2, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection = BankDirection >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (input), 3, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			DirectionCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 12);
+			BankDirection = DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (output), 1, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (output), 2, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+			BankDirection = BankDirection >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank direction, WHOLE (output), 3, BankDirection:x%08x\n", dio_chl->info.line, BankDirection);
+
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankDirection & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankDirection & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_direction(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankDirection = 0;
+	unsigned int DirectionCtrlReg = 0;
+	unsigned int DirectionCtrlRegByBank = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankDirection  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankDirection |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankDirection |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankDirection |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank direction, BankIndex:x%02x, BankDirection:x%08x\n", dio_chl->info.line, BankIndex, BankDirection);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			DirectionCtrlRegByBank = (BankDirection << cib_info->dio_bank_cap[BankIndex].io_shift_bits) & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, INDIVIDUAL, 1, DirectionCtrlRegByBank:x%08x\n", dio_chl->info.line, DirectionCtrlRegByBank);
+			DirectionCtrlReg = dio_chl->DirectionCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, INDIVIDUAL, 2, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+			DirectionCtrlReg |= DirectionCtrlRegByBank;
+			//printk("SUNIX: DIO (%d), set bank direction, INDIVIDUAL, 3, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 12, DirectionCtrlReg);
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			if ((BankDirection & (cib_info->dio_bank_cap[BankIndex].io_mask >> cib_info->dio_bank_cap[BankIndex].io_shift_bits)) != 0x00000000)
+			{
+				nStatus = SDCDIO_UNSUPPORT_OUTPUT_CAP;
+				break;
+			}
+
+			DirectionCtrlRegByBank = (BankDirection << cib_info->dio_bank_cap[BankIndex].io_shift_bits) & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (input), 1, DirectionCtrlRegByBank:x%08x\n", dio_chl->info.line, DirectionCtrlRegByBank);
+			DirectionCtrlReg = dio_chl->DirectionCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (input), 2, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+			DirectionCtrlReg |= DirectionCtrlRegByBank;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (input), 3, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 12, DirectionCtrlReg);
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			if ((BankDirection & (cib_info->dio_bank_cap[BankIndex].io_mask >> cib_info->dio_bank_cap[BankIndex].io_shift_bits)) != 
+				(0xffffffff & (cib_info->dio_bank_cap[BankIndex].io_mask >> cib_info->dio_bank_cap[BankIndex].io_shift_bits)))
+			{
+				nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+				break;
+			}
+
+			DirectionCtrlRegByBank = (BankDirection << cib_info->dio_bank_cap[BankIndex].io_shift_bits) & cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (output), 1, DirectionCtrlRegByBank:x%08x\n", dio_chl->info.line, DirectionCtrlRegByBank);
+			DirectionCtrlReg = dio_chl->DirectionCtrlReg & ~cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (output), 2, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+			DirectionCtrlReg |= DirectionCtrlRegByBank;
+			//printk("SUNIX: DIO (%d), set bank direction, WHOLE (output), 3, DirectionCtrlReg:x%08x\n", dio_chl->info.line, DirectionCtrlReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 12, DirectionCtrlReg);
+			dio_chl->DirectionCtrlReg = DirectionCtrlReg;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_output_initial_value(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankOutputInitialValue = 0;
+	unsigned int OutputInitialValueReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_outputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_OUTPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank output initial value, INDIVIDUAL, no output\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_OUTPUT_IO;
+				break;
+			}
+
+			OutputInitialValueReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 13);
+			BankOutputInitialValue = OutputInitialValueReg;
+			//printk("SUNIX: DIO (%d), get bank output initial value, INDIVIDUAL, 1, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+			BankOutputInitialValue &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank output initial value, INDIVIDUAL, 2, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+			BankOutputInitialValue &= dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), get bank output initial value, INDIVIDUAL, 3, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+			BankOutputInitialValue = BankOutputInitialValue >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank output initial value, INDIVIDUAL, 4, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+
+			dio_chl->OutputInitialValueReg = OutputInitialValueReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			nStatus = SDCDIO_NO_OUTPUT_IO;
+			break;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			OutputInitialValueReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 13);
+			BankOutputInitialValue = OutputInitialValueReg;
+			//printk("SUNIX: DIO (%d), get bank output initial value, WHOLE (output), 1, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+			BankOutputInitialValue &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), get bank output initial value, WHOLE (output), 2, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+			BankOutputInitialValue = BankOutputInitialValue >> cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), get bank output initial value, WHOLE (output), 3, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankOutputInitialValue);
+
+			dio_chl->OutputInitialValueReg = OutputInitialValueReg;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankOutputInitialValue & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankOutputInitialValue & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankOutputInitialValue & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankOutputInitialValue & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_output_initial_value(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankOutputInitialValue = 0;
+	unsigned int OutputInitialValueReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankOutputInitialValue  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankOutputInitialValue |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankOutputInitialValue |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankOutputInitialValue |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank output initial value, BankIndex:x%02x, BankOutputInitialValue:x%08x\n", dio_chl->info.line, BankIndex, BankOutputInitialValue);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_outputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_OUTPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), set bank output initial value, INDIVIDUAL, no output\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_OUTPUT_IO;
+				break;
+			}
+
+			OutputInitialValueReg = BankOutputInitialValue << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank output initial value, INDIVIDUAL, 1, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+			OutputInitialValueReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank output initial value, INDIVIDUAL, 2, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+			OutputInitialValueReg &= dio_chl->DirectionCtrlReg;
+			//printk("SUNIX: DIO (%d), set bank output initial value, INDIVIDUAL, 3, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+			OutputInitialValueReg |= (dio_chl->OutputInitialValueReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank output initial value, INDIVIDUAL, 4, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 13, OutputInitialValueReg);
+			dio_chl->OutputInitialValueReg = OutputInitialValueReg;
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			nStatus = SDCDIO_NO_OUTPUT_IO;
+			break;
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			OutputInitialValueReg = BankOutputInitialValue << cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			//printk("SUNIX: DIO (%d), set bank output initial value, WHOLE (output), 1, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+			OutputInitialValueReg &= cib_info->dio_bank_cap[BankIndex].io_mask;
+			//printk("SUNIX: DIO (%d), set bank output initial value, WHOLE (output), 2, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+			OutputInitialValueReg |= (dio_chl->OutputInitialValueReg & ~cib_info->dio_bank_cap[BankIndex].io_mask);
+			//printk("SUNIX: DIO (%d), set bank output initial value, WHOLE (output), 3, OutputInitialValueReg:x%08x\n", dio_chl->info.line, OutputInitialValueReg);
+
+			mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 13, OutputInitialValueReg);
+			dio_chl->OutputInitialValueReg = OutputInitialValueReg;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_ctrl_register(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+
+		BankCtrlReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 16 + BankIndex);
+		//printk("SUNIX: DIO (%d), get bank ctrl register, BankCtrlReg:x%08x\n", dio_chl->info.line, BankCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?5:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankCtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankCtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankCtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankCtrlReg & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_bank_ctrl_register(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned int BankCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 5)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankCtrlReg  =  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 1) << 24);
+		BankCtrlReg |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 2) << 16);
+		BankCtrlReg |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 3) << 8);
+		BankCtrlReg |=  (unsigned int)(*(RxBuff + sizeof(DIO_HEADER) + 4));
+		//printk("SUNIX: DIO (%d), set bank ctrl register, BankIndex:x%02x, BankCtrlReg:x%08x\n", dio_chl->info.line, BankIndex, BankCtrlReg);
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+
+		mem_tx32(dio_chl->info.membase, dio_chl->info.memoffset, 16 + BankIndex, BankCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?1:1;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_bank_input_port_counter(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &dio_chl->info.cib_info;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	unsigned char * RxBuff = dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BankIndex = 0;
+	unsigned char BankPortIndex = 0;
+	unsigned int BankPortInputCounterValue = 0;
+	unsigned char PortIndex = 0x00;
+	unsigned int PortInputCounterValueReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 2)
+		{
+			nStatus = SDCDIO_LENGTH_INVALID;
+			break;
+		}
+		BankIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 0);
+		BankPortIndex = (unsigned char)*(RxBuff + sizeof(DIO_HEADER) + 1);
+
+		if (!(BankIndex < cib_info->dio_number_of_banks))
+		{
+			nStatus = SDCDIO_BANK_NOT_FOUND;
+			break;
+		}
+
+		if (!(BankPortIndex < cib_info->dio_bank_cap[BankIndex].number_of_io))
+		{
+			nStatus = SDCDIO_BANK_IO_NOT_FOUND;
+			break;
+		}
+
+		if (cib_info->dio_bank_cap[BankIndex].support_inputs != 0x01)
+		{
+			nStatus = SDCDIO_UNSUPPORT_INPUT_CAP;
+			break;
+		}
+
+
+		if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is individual input/output
+			if ((~dio_chl->DirectionCtrlReg & cib_info->dio_bank_cap[BankIndex].io_mask) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input port counter, INDIVIDUAL, no input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+			PortIndex = BankPortIndex;
+			if (cib_info->dio_bank_cap[BankIndex].io_shift_bits > 0)
+			{
+				PortIndex += cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			}
+			if (PortIndex > 32)
+			{
+				//printk("SUNIX: DIO (%d), get bank input port counter, INDIVIDUAL, PortIndex:%d invalid\n", dio_chl->info.line, PortIndex);
+				nStatus = SDCDIO_BANK_IO_NOT_FOUND;
+				break;
+			}
+			if ((~dio_chl->DirectionCtrlReg & (0x00000001 << PortIndex)) == 0x00000000)
+			{
+				//printk("SUNIX: DIO (%d), get bank input port counter, INDIVIDUAL, not input\n", dio_chl->info.line);
+				nStatus = SDCDIO_NO_INPUT_IO;
+				break;
+			}
+			PortInputCounterValueReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 32 + PortIndex);
+			BankPortInputCounterValue = PortInputCounterValueReg;
+			//printk("SUNIX: DIO (%d), get bank input port counter, INDIVIDUAL, 1, PortIndex:x%02x\n", dio_chl->info.line, PortIndex);
+			//printk("SUNIX: DIO (%d), get bank input port counter, INDIVIDUAL, 2, BankPortInputCounterValue:x%08x\n", dio_chl->info.line, BankPortInputCounterValue);
+		}
+		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x01) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x00))
+		{
+			// bank is whole input
+			PortIndex = BankPortIndex;
+			if (cib_info->dio_bank_cap[BankIndex].io_shift_bits > 0)
+			{
+				PortIndex += cib_info->dio_bank_cap[BankIndex].io_shift_bits;
+			}
+			if (PortIndex > 32)
+			{
+				//printk("SUNIX: DIO (%d), get bank input port counter, WHOLE (input), PortIndex:%d invalid\n", dio_chl->info.line, PortIndex);
+				nStatus = SDCDIO_BANK_IO_NOT_FOUND;
+				break;
+			}
+			PortInputCounterValueReg = mem_rx32(dio_chl->info.membase, dio_chl->info.memoffset, 32 + PortIndex);
+			BankPortInputCounterValue = PortInputCounterValueReg;
+			//printk("SUNIX: DIO (%d), get bank input port counter, WHOLE (input), 1, PortIndex:x%02x\n", dio_chl->info.line, PortIndex);
+			//printk("SUNIX: DIO (%d), get bank input port counter, WHOLE (input), 2, BankPortInputCounterValue:x%08x\n", dio_chl->info.line, BankPortInputCounterValue);
+		}
+ 		else if ((cib_info->dio_bank_cap[BankIndex].support_inputs == 0x00) && (cib_info->dio_bank_cap[BankIndex].support_outputs == 0x01))
+		{
+			// bank is whole output
+			nStatus = SDCDIO_NO_INPUT_IO;
+			break;
+		}
+		else
+		{
+			nStatus = SDCDIO_UNDEFINE_ERROR;
+			break;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCDIO_STATUS_SUCCESS)?6:2;
+	TrLength = sizeof(DIO_HEADER);
+	if (pTrHeader->ResponseStatus == SDCDIO_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = BankPortIndex;
+		TrBuff[TrLength++] = (unsigned char)((BankPortInputCounterValue & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((BankPortInputCounterValue & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((BankPortInputCounterValue & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((BankPortInputCounterValue & 0x000000ff));
+	}
+	else
+	{
+		TrBuff[TrLength++] = BankIndex;
+		TrBuff[TrLength++] = BankPortIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void unsupport(struct sunix_sdc_dio_channel *dio_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	unsigned int nStatus = SDCDIO_UNSUPPORT_COMMAND;
+	unsigned int TrLength = 0;
+
+
+	memset(TrBuff, 0, SUNIX_SDC_DIO_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = 0;
+	TrLength = sizeof(DIO_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+int sunix_dio_handle_outcome(struct sunix_sdc_dio_channel *dio_chl, size_t count, unsigned int * outcomeLength)
+{
+	int status = 0;
+	PDIO_PACKAGE pPack = NULL;
+	DLIST *	pListHead = &dio_chl->packList;
+	DLIST * e = NULL;
+	unsigned char * TxBuff = dio_chl->outcomeBuff;
+	unsigned int TrLength = 0;
+
+
+	do
+	{
+		do
+		{
+			if (!SxxListEmpty(pListHead))
+			{
+				e = pListHead->Flink;
+			}
+			else
+			{
+				break;
+			}
+
+			while ((e != NULL) && (e != pListHead))
+			{
+				if (e != NULL)
+				{
+					pPack = SUNIX_SDC_DIO_PACK_PTR(e);
+					if (pPack != NULL)
+					{
+						break;
+					}
+
+					e = e->Flink;
+				}
+			}
+
+		} while (false);
+
+		if (pPack == NULL)
+		{
+			*outcomeLength = 0;
+			break;
+		}
+
+
+		SxxListRemoveEntry(&pPack->Entry);
+
+
+		memset(TxBuff, 0, SUNIX_SDC_DIO_BUFF);
+		memcpy(TxBuff, &pPack->Header, sizeof(DIO_HEADER));
+		TrLength = sizeof(DIO_HEADER);
+		if (pPack->DataPtr != NULL)
+		{
+			memcpy(TxBuff + sizeof(DIO_HEADER), pPack->DataPtr, pPack->Header.Length);
+			TrLength += pPack->Header.Length;
+		}
+
+		*outcomeLength = TrLength;
+
+		//printk("SUNIX: DIO FREE pack, line:%d, pack:x%p, DataPtrx%p\n", dio_chl->info.line, pPack, pPack->DataPtr);
+		if (pPack->DataPtr != NULL)
+		{
+			kfree(pPack->DataPtr);
+			pPack->DataPtr = NULL;
+		}
+		kmem_cache_free(sunix_sdc_dio_pack_cache, pPack);
+		pPack = NULL;
+			
+	} while (false);
+
+	return status;
+}
+
+
+int sunix_dio_handle_income(struct sunix_sdc_dio_channel *dio_chl, size_t count)
+{
+	int status = 0;
+	PDIO_HEADER pRxHeader = (PDIO_HEADER)dio_chl->incomeBuff;
+	PDIO_HEADER pTrHeader = (PDIO_HEADER)dio_chl->translateBuff;
+	unsigned char * TrBuff = dio_chl->translateBuff;
+	PDIO_PACKAGE pPack = NULL;
+	unsigned int translateLength = 0;
+	unsigned long Flags;
+
+
+	do
+	{
+		// debug
+		/*
+		printk("++++++++++++++++++++++++++++++++++++++++++++++\n");
+		printk("SUNIX: DIO_RX, Version              :x%02x\n", pRxHeader->Version);
+		printk("SUNIX: DIO_RX, CmdResponseEventData :x%04x\n", pRxHeader->CmdResponseEventData);
+		printk("SUNIX: DIO_RX, Length               :x%08x\n", pRxHeader->Length);
+		printk("++++++++++++++++++++++++++++++++++++++++++++++\n");
+		*/
+
+
+		switch (pRxHeader->CmdResponseEventData)
+		{
+			case SDCDIO_CMD_GET_INFO :
+				get_info(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_STATE :
+				get_bank_state(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_DELTA_STATE :
+				get_bank_input_delta_state(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_INVERT_ENABLE :
+				get_bank_input_invert_enable(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_INVERT_ENABLE :
+				set_bank_input_invert_enable(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_LATCH_RISING_EDGE :
+				get_bank_input_latch_rising_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_LATCH_RISING_EDGE :
+				set_bank_input_latch_rising_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_LATCH_FALLING_EDGE :
+				get_bank_input_latch_falling_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_LATCH_FALLING_EDGE :
+				set_bank_input_latch_falling_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_COUNTER_RESET :
+				set_bank_input_counter_reset(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_COUNTER_INCREMENT_RISING_EDGE :
+				get_bank_input_counter_increment_rising_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_COUNTER_INCREMENT_RISING_EDGE :
+				set_bank_input_counter_increment_rising_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_COUNTER_INCREMENT_FALLING_EDGE :
+				get_bank_input_counter_increment_falling_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_COUNTER_INCREMENT_FALLING_EDGE :
+				set_bank_input_counter_increment_falling_edge(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_RISING_EVENT_CTRL :
+				get_bank_input_rising_event_ctrl(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_RISING_EVENT_CTRL :
+				set_bank_input_rising_event_ctrl(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_FALLING_EVENT_CTRL :
+				get_bank_input_falling_event_ctrl(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_INPUT_FALLING_EVENT_CTRL :
+				set_bank_input_falling_event_ctrl(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_STATE :
+				set_bank_state(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_DIRECTION :
+				get_bank_direction(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_DIRECTION :
+				set_bank_direction(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_OUTPUT_INITIAL_VALUE :
+				get_bank_output_initial_value(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_OUTPUT_INITIAL_VALUE :
+				set_bank_output_initial_value(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_CTRL_REGISTER :
+				get_bank_ctrl_register(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_SET_BANK_CTRL_REGISTER :
+				set_bank_ctrl_register(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCDIO_CMD_GET_BANK_INPUT_PORT_COUNTER :
+				get_bank_input_port_counter(dio_chl, (unsigned int)count, &translateLength);
+				break;
+
+			default :
+				unsupport(dio_chl, (unsigned int)count, &translateLength);
+				break;
+		}
+
+
+		// debug
+		/*
+		printk("----------------------------------------------\n");
+		printk("SUNIX: DIO_TR, translateLength      :%d\n", translateLength);
+		printk("SUNIX: DIO_TR, Version              :x%02x\n", pTrHeader->Version);
+		printk("SUNIX: DIO_TR, CmdResponseEventData :x%04x\n", pTrHeader->CmdResponseEventData);
+		printk("SUNIX: DIO_TR, ResponseStatus       :x%04x\n", pTrHeader->ResponseStatus);
+		printk("SUNIX: DIO_TR, Length               :x%08x\n", pTrHeader->Length);
+		{
+			int i;
+			for (i = 0; i < pTrHeader->Length; i++)
+				printk("x%02x ", (unsigned char)*(TrBuff + sizeof(DIO_HEADER) + i));
+		}
+		printk("----------------------------------------------\n");
+		*/
+
+
+		if (pTrHeader->Length > DIO_MAX_DATA_LENGTH)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		if (translateLength > (DIO_MAX_DATA_LENGTH + sizeof(DIO_HEADER)))
+		{
+			status = -ENOMEM;
+			break;
+		}
+
+		pPack = kmem_cache_alloc(sunix_sdc_dio_pack_cache, GFP_ATOMIC);
+		if (pPack == NULL)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		memset(pPack, 0, sizeof(DIO_PACKAGE));
+		pPack->DataPtr = (unsigned char *)kmalloc(DIO_MAX_DATA_LENGTH, GFP_KERNEL);
+		if (pPack->DataPtr == NULL)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		memset(pPack->DataPtr, 0, DIO_MAX_DATA_LENGTH);
+
+
+		SxxListInit(&pPack->Entry);
+		memcpy(&pPack->Header, pTrHeader, sizeof(DIO_HEADER));
+		memcpy(pPack->DataPtr, TrBuff + sizeof(DIO_HEADER), pTrHeader->Length);
+
+		spin_lock_irqsave(&dio_chl->packLock, Flags);
+		//printk("SUNIX: DIO ALOC pack, line:%d, pack:x%p, DataPtrx%p\n", dio_chl->info.line, pPack, pPack->DataPtr);
+		SxxListInsertTail(&dio_chl->packList, &pPack->Entry);
+
+		dio_chl->readDataReady = 1;
+		wake_up_interruptible(&dio_chl->readWQ);
+		spin_unlock_irqrestore(&dio_chl->packLock, Flags);
+
+	} while (false);
+
+	if (status != 0)
+	{
+		if (pPack != NULL)
+		{
+			if (pPack->DataPtr != NULL)
+			{
+				kfree(pPack->DataPtr);
+				pPack->DataPtr = NULL;
+			}
+
+			kmem_cache_free(sunix_sdc_dio_pack_cache, pPack);
+		}
+	}
+
+	return status;
+}
+
+
diff --git a/drivers/mfd/list.c b/drivers/mfd/list.c
new file mode 100644
index 000000000000..8126eaed9671
--- /dev/null
+++ b/drivers/mfd/list.c
@@ -0,0 +1,99 @@
+
+
+#include "sdc_include.h"
+
+
+void
+SxxListInit(
+	DLIST *					pListHead
+	)
+{
+	pListHead->Flink = pListHead->Blink = pListHead;
+}
+
+
+bool
+SxxListEmpty(
+	DLIST *					pListHead
+	)
+{
+	if (pListHead->Flink == pListHead)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+
+DLIST *
+SxxListGetNext(
+	DLIST *					pListHead
+	)
+{
+	return pListHead->Flink;
+}
+
+
+DLIST *
+SxxListGetPrev(
+	DLIST *					pListHead
+	)
+{
+	return pListHead->Blink;
+}
+
+
+void
+SxxListRemoveEntry(
+	DLIST *					pEntry
+	)
+{
+	DLIST *					_DlistMEntry = pEntry;
+
+
+	_DlistMEntry->Blink->Flink = _DlistMEntry->Flink;
+	_DlistMEntry->Flink->Blink = _DlistMEntry->Blink;
+	_DlistMEntry->Flink = _DlistMEntry->Blink = _DlistMEntry;
+}
+
+
+void
+SxxListInsertEntry(
+	DLIST *					pEntry,
+	DLIST * 				pNewEntry
+	)
+{
+	DLIST *					_DlistMEntry = pEntry;
+	DLIST *					_DlistMNewEntry = pNewEntry;
+
+
+	_DlistMNewEntry->Flink = _DlistMEntry->Flink;
+	_DlistMNewEntry->Blink = _DlistMEntry;
+	_DlistMEntry->Flink->Blink = _DlistMNewEntry;
+	_DlistMEntry->Flink = _DlistMNewEntry;
+}
+
+
+void
+SxxListInsertHead(
+	DLIST *					pListHead,
+	DLIST *					pEntry
+	)
+{
+	SxxListInsertEntry(pListHead, pEntry);
+}
+
+
+void
+SxxListInsertTail(
+	DLIST *					pListHead,
+	DLIST *					pEntry
+	)
+{
+	SxxListInsertEntry(pListHead->Blink, pEntry);
+}
+
+
diff --git a/drivers/mfd/mem.c b/drivers/mfd/mem.c
new file mode 100644
index 000000000000..1158439d0aef
--- /dev/null
+++ b/drivers/mfd/mem.c
@@ -0,0 +1,37 @@
+
+
+#include "sdc_include.h"
+
+
+void __iomem * mem_get_bar_ioremap(struct pci_dev *pdev, unsigned int bar_num)
+{
+	void __iomem * membase;
+	unsigned long phy_base_start;
+	unsigned long phy_base_end;
+	unsigned long phy_base_len;
+
+
+	phy_base_start = pci_resource_start(pdev, bar_num);
+	phy_base_end = pci_resource_end(pdev, bar_num);
+	phy_base_len = (phy_base_end - phy_base_start) + 1;
+	membase = ioremap(phy_base_start, phy_base_len);
+	return membase;
+}
+
+
+unsigned int mem_rx32(void __iomem *membase, unsigned int base_offset, unsigned int reg_offset)
+{
+	unsigned int data;
+
+
+	data = readl(membase + base_offset + (reg_offset * 4));
+	return data;
+}
+
+
+void mem_tx32(void __iomem *membase, unsigned int base_offset, unsigned int reg_offset, unsigned int data)
+{
+	writel(data, membase + base_offset + (reg_offset * 4));
+}
+
+
diff --git a/drivers/mfd/sdc_define.h b/drivers/mfd/sdc_define.h
new file mode 100644
index 000000000000..365a52809da5
--- /dev/null
+++ b/drivers/mfd/sdc_define.h
@@ -0,0 +1,668 @@
+
+
+#ifndef _SDC_DRVR_DEFINE_H_
+#define _SDC_DRVR_DEFINE_H_
+
+
+/*******************************************************
+	
+*******************************************************/
+
+#define ENABLE_DEBUG_SDC_BUS					0
+#define ENABLE_DEBUG_SDC_DIO					0
+
+
+/*******************************************************
+	bus
+*******************************************************/
+
+#define SUNIX_SDC_BOARD_MAX  					4
+
+struct dio_bank
+{
+	unsigned char			number_of_io;
+	unsigned char			support_inputs;
+	unsigned char			support_outputs;
+	unsigned char			rising_edge_trigger_cap;
+	unsigned char			falling_edge_trigger_cap;
+
+	unsigned int			io_mask;
+	unsigned int			io_shift_bits;
+};
+
+struct spi_device
+{
+	unsigned char			type;
+	unsigned char			number_of_gpio_input;
+	unsigned char			number_of_gpio_output;
+	unsigned char			name[32];
+};
+
+struct sdc_cib
+{
+	// cib
+	unsigned char			num;
+	unsigned char			type;
+	unsigned char			version;
+	unsigned char			lengthInDW;
+	unsigned char			capability;
+	unsigned char			event_header_type;
+	unsigned int			io_addr_offset;
+	unsigned char			io_space_size;
+	unsigned int			mem_addr_offset;
+	unsigned int			mem_space_size;
+	// configuration controller
+	unsigned char			cfg_ic_model;
+	// uart controller
+	unsigned short			uart_tx_fifo_size;
+	unsigned short			uart_rx_fifo_size;
+	unsigned int			uart_significand_of_clock;
+	unsigned char			uart_exponent_of_clock;
+	unsigned char			uart_RS232_cap;
+	unsigned char			uart_RS422_cap;
+	unsigned char			uart_RS485_cap;
+	unsigned char			uart_AHDC_cap;
+	unsigned char			uart_CS_cap;
+	unsigned char			uart_auto_RS422485_cap;
+	unsigned char			uart_RS422_termination_cap;
+	unsigned char			uart_RS485_termination_cap;
+	unsigned char			uart_RI_5V_cap;
+	unsigned char			uart_RI_12V_cap;
+	unsigned char			uart_DCD_5V_cap;
+	unsigned char			uart_DCD_12V_cap;
+	// dio controller
+	unsigned char			dio_number_of_banks;
+	unsigned char			dio_shares_same_direction_ctrl_cap;
+	unsigned char			dio_writing_setting_to_flash_cap;
+	struct dio_bank			dio_bank_cap[32];
+	// spi controller
+	unsigned int			spi_significand_of_clock;
+	unsigned char			spi_exponent_of_clock;
+	unsigned char			spi_number_of_device;
+	struct spi_device		spi_device_cap[16];
+};
+
+struct sunix_sdc_uart_channel;
+struct sunix_sdc_dio_channel;
+struct sunix_sdc_spi_channel;
+
+struct sunix_sdc_board
+{
+	struct pci_dev			*pdev;
+	int						board_enum;
+	int						board_number;
+	unsigned int			bus_number;
+	unsigned int			dev_number;
+	unsigned int			irq;
+
+	void __iomem *			bar0_membase;
+	unsigned int			bar1_iobase;
+	void __iomem *			bar2_membase;
+
+	unsigned char 			major_version;
+	unsigned char 			minor_version;
+	unsigned char 			aval_channels;
+	unsigned char 			model_name[32];
+	struct sdc_cib			channel[32];
+
+	unsigned int			uart_amount;
+	unsigned int			uart_start_index;
+	int						(*uart_isr)(struct sunix_sdc_uart_channel *);
+
+	unsigned int			dio_amount;
+	unsigned int			dio_start_index;
+	int						(*dio_isr)(struct sunix_sdc_dio_channel *, unsigned int);
+
+	unsigned int			spi_amount;
+	unsigned int			spi_start_index;
+	int						(*spi_isr)(struct sunix_sdc_spi_channel *, unsigned int);
+};
+
+
+/*******************************************************
+
+*******************************************************/
+
+typedef struct _DLIST
+{
+	struct _DLIST *			Flink;
+	struct _DLIST *			Blink;
+
+} DLIST;
+
+
+#define STRUCT_BASE_POINTER(Fieldptr, Type, Field) \
+	((Type *)(((char *)(Fieldptr)) - ((char *)(&(((Type *)0)->Field)))))
+
+
+/*******************************************************
+	uart
+*******************************************************/
+
+#define SUNIX_SDC_UART_MAX					32
+
+#define SNX_SDC_UART_IOCTL					0xa00
+#define SNX_SDC_UART_GET_INFO				(SNX_SDC_UART_IOCTL + 50)
+#define SNX_SDC_UART_GET_ADDITIONAL_REG_0E	(SNX_SDC_UART_IOCTL + 51)
+#define SNX_SDC_UART_SET_ADDITIONAL_REG_0E	(SNX_SDC_UART_IOCTL + 52)
+
+struct snx_sdc_uart_info 
+{
+	unsigned char			model_name[32];
+	unsigned int			bus_number;
+	unsigned int			dev_number;
+	unsigned int			line;
+	unsigned int			iobase;
+	unsigned int			irq;
+	unsigned char			version;
+	unsigned short			tx_fifo_size;
+	unsigned short			rx_fifo_size;
+	unsigned int			significand_of_clock;
+	unsigned char			exponent_of_clock;
+	unsigned char			RS232_cap;
+	unsigned char			RS422_cap;
+	unsigned char			RS485_cap;
+	unsigned char			AHDC_cap;
+	unsigned char			CS_cap;
+	unsigned char			auto_RS422485_cap;
+	unsigned char			RS422_termination_cap;
+	unsigned char			RS485_termination_cap;
+	unsigned char			RI_5V_cap;
+	unsigned char			RI_12V_cap;
+	unsigned char			DCD_5V_cap;
+	unsigned char			DCD_12V_cap;
+};
+
+
+
+
+#define UART_LSR_ERR_IN_RFIFO  				0x80
+#define UART_MCR_AFE  						0x20
+#define UART_IIR_CTO  						0x0C
+
+#define INTERRUPT_COUNT						128
+#define WAKEUP_CHARS						256
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19))
+#define SNXTERMIOS  ktermios
+#else
+#define SNXTERMIOS  termios
+#endif
+
+#define SNX_SER_BAUD_SETSERIAL				1
+#define SNX_SER_BAUD_NOTSETSER				0
+
+#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 SNX_USF_CLOSING_WAIT_INF			(0)
+#define SNX_USF_CLOSING_WAIT_NONE			(65535)
+#define SNX_UART_CONFIG_TYPE				(1 << 0)
+#define SNX_UART_CONFIG_IRQ					(1 << 1)
+
+#define SNX_UART_XMIT_SIZE 					4096
+
+#define snx_ser_circ_empty(circ)		   	((circ)->head == (circ)->tail)
+#define snx_ser_circ_clear(circ)		    ((circ)->head = (circ)->tail = 0)
+#define snx_ser_circ_chars_pending(circ)    (CIRC_CNT((circ)->head, (circ)->tail, SNX_UART_XMIT_SIZE))
+#define snx_ser_circ_chars_free(circ)       (CIRC_SPACE((circ)->head, (circ)->tail, SNX_UART_XMIT_SIZE))
+#define snx_ser_tx_stopped(port)            ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
+
+#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
+#define SNX_SERIAL_INLINE
+#endif
+
+#ifdef SNX_SERIAL_INLINE
+#define _INLINE_ inline
+#else
+#define _INLINE_
+#endif
+
+#define SNX_UPIO_PORT				(0)
+#define SNX_UPIO_MEM				(1)
+
+#define SNX_UPF_SAK					(1 << 2)
+#define SNX_UPF_SPD_MASK			(0x1030)
+#define SNX_UPF_SPD_HI				(0x0010)
+#define SNX_UPF_SPD_VHI				(0x0020)
+#define SNX_UPF_SPD_CUST			(0x0030)
+#define SNX_UPF_SPD_SHI				(0x1000)
+#define SNX_UPF_SPD_WARP			(0x1010)
+#define SNX_UPF_SKIP_TEST			(1 << 6)
+#define SNX_UPF_HARDPPS_CD			(1 << 11)
+#define SNX_UPF_LOW_LATENCY			(1 << 13)
+#define SNX_UPF_BUGGY_UART			(1 << 14)
+#define SNX_UPF_MAGIC_MULTIPLIER	(1 << 16)
+
+#define SNX_UPF_CHANGE_MASK			(0x17fff)
+#define SNX_UPF_USR_MASK			(SNX_UPF_SPD_MASK | SNX_UPF_LOW_LATENCY)
+
+#define SNX_UIF_CHECK_CD			(1 << 25)
+#define SNX_UIF_CTS_FLOW			(1 << 26)
+
+#define SNX_UIF_NORMAL_ACTIVE		(1 << 29)
+#define SNX_UIF_INITIALIZED			(1 << 31)
+
+#define SNX_ENABLE_MS(port, cflag)    ((port)->flags & SNX_UPF_HARDPPS_CD || (cflag) & CRTSCTS || !((cflag) & CLOCAL))
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define SNX_SER_DEVNUM(x)   ((x)->index)
+#else
+#define SNX_SER_DEVNUM(x)   (MINOR((x)->device) - (x)->driver.minor_start)
+#endif
+
+struct snx_ser_info;
+struct snx_ser_port;
+
+struct snx_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 snx_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 snx_ser_driver
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct module			*owner;
+	const char 				*driver_name;
+#endif
+
+	const char				*dev_name;
+	int			 			major;
+	int			 			minor;
+	int			 			nr;
+	struct snx_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 snx_ser_port
+{
+	spinlock_t				lock;
+	unsigned int			iobase;
+	unsigned int			iosize;
+	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 snx_ser_info		*info;
+	struct snx_ser_icount	icount;
+
+	unsigned int			flags;
+	unsigned int			mctrl;
+	unsigned int			timeout;
+
+	unsigned int			type;
+	unsigned int			custom_divisor;
+	unsigned int			line;
+
+	int						board_enum;
+	unsigned int			bus_number;
+	unsigned int			dev_number;
+	unsigned char 			model_name[32];
+
+	unsigned int			baud_base;
+	int						rx_trigger;
+	unsigned char			ldisc_stop_rx;
+	unsigned int			setserial_flag;
+    unsigned char			suspended;
+	struct device			*dev;
+
+	struct sdc_cib			cib_info;
+};
+
+struct snx_ser_state
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct tty_port			tport;
+#endif
+
+	unsigned int			close_delay;
+	unsigned int			closing_wait;
+	int						count;
+	struct snx_ser_info	    *info;
+	struct snx_ser_port		*port;
+	struct semaphore		sem;
+};
+
+static inline int snx_ser_handle_break(struct snx_ser_port *port)
+{
+	struct snx_ser_info *info = port->info;
+
+	if (info->flags & SNX_UPF_SAK)
+	{
+		do_SAK(info->tty);
+	}
+	return 0;
+}
+
+static inline void snx_ser_handle_dcd_change(struct snx_ser_port *port, unsigned int status)
+{
+	struct snx_ser_info *info = port->info;
+
+	port->icount.dcd++;
+
+	if (info->flags & SNX_UIF_CHECK_CD)
+	{
+		if (status)
+		{
+			wake_up_interruptible(&info->open_wait);
+		}
+		else if (info->tty)
+		{
+			tty_hangup(info->tty);
+		}
+	}
+}
+
+struct sunix_sdc_uart_channel
+{
+	struct snx_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;
+};
+
+
+/*******************************************************
+	dio
+*******************************************************/
+
+#define SUNIX_SDC_DIO_MAX					32
+#define SUNIX_SDC_DIO_BUFF					1024
+
+struct snx_dio_info 
+{
+ 	unsigned char			model_name[32];
+	unsigned int			bus_number;
+	unsigned int			dev_number;
+	unsigned int			line;
+	unsigned long			phy2_base_start;
+	void __iomem *			membase;
+	unsigned int			memoffset;
+	unsigned int			memsize;
+	unsigned int			irq;
+
+	struct sdc_cib			cib_info;
+};
+
+struct sunix_sdc_dio_channel 
+{
+	struct snx_dio_info		info;
+	struct cdev				cdev;
+
+	DLIST					packList;
+	spinlock_t				packLock;
+	wait_queue_head_t		readWQ;
+	unsigned int			readDataReady;
+	struct semaphore		sem;
+
+	bool					isOpened;
+
+	unsigned char *			incomeBuff;
+	unsigned char *			outcomeBuff;
+	unsigned char *			translateBuff;
+
+	unsigned int			InputInvertEnableReg;
+	unsigned int			InputLatchRegPositiveEdgeR;
+	unsigned int			InputLatchRegPositiveEdgeW;
+	unsigned int			InputLatchRegNegativeEdgeR;
+	unsigned int			InputLatchRegNegativeEdgeW;
+	unsigned int			InputCounterIncrementCtrlRegPositiveEdge;
+	unsigned int			InputCounterIncrementCtrlRegNegativeEdge;
+	unsigned int			InputRisingEventCtrlReg;
+	unsigned int			InputFallingEventCtrlReg;
+	unsigned int			DirectionCtrlReg;
+	unsigned int			OutputInitialValueReg;
+};
+
+#define DIO_MAX_DATA_LENGTH		128
+
+typedef struct _DIO_HEADER
+{
+	unsigned char		Version;
+	unsigned char		Reserved;
+	unsigned short		CmdResponseEventData;
+	unsigned short		ResponseStatus;
+	unsigned int		Length;
+
+} DIO_HEADER, *PDIO_HEADER;
+
+typedef struct _DIO_PACKAGE
+{
+	DLIST				Entry;
+	DIO_HEADER			Header;
+	unsigned char * 	DataPtr;
+
+} DIO_PACKAGE, *PDIO_PACKAGE;
+
+#define SDCDIO_CMD_GET_INFO											0x0001
+#define SDCDIO_CMD_GET_BANK_STATE									0x0002
+#define SDCDIO_CMD_GET_BANK_INPUT_DELTA_STATE						0x0003
+#define SDCDIO_CMD_GET_BANK_INPUT_INVERT_ENABLE						0x0004
+#define SDCDIO_CMD_SET_BANK_INPUT_INVERT_ENABLE						0x0005
+#define SDCDIO_CMD_GET_BANK_INPUT_LATCH_RISING_EDGE					0x0006
+#define SDCDIO_CMD_SET_BANK_INPUT_LATCH_RISING_EDGE					0x0007
+#define SDCDIO_CMD_GET_BANK_INPUT_LATCH_FALLING_EDGE				0x0008
+#define SDCDIO_CMD_SET_BANK_INPUT_LATCH_FALLING_EDGE				0x0009
+#define SDCDIO_CMD_SET_BANK_INPUT_COUNTER_RESET						0x000a
+#define SDCDIO_CMD_GET_BANK_INPUT_COUNTER_INCREMENT_RISING_EDGE		0x000b
+#define SDCDIO_CMD_SET_BANK_INPUT_COUNTER_INCREMENT_RISING_EDGE		0x000c
+#define SDCDIO_CMD_GET_BANK_INPUT_COUNTER_INCREMENT_FALLING_EDGE	0x000d
+#define SDCDIO_CMD_SET_BANK_INPUT_COUNTER_INCREMENT_FALLING_EDGE	0x000e
+#define SDCDIO_CMD_GET_BANK_INPUT_RISING_EVENT_CTRL					0x000f
+#define SDCDIO_CMD_SET_BANK_INPUT_RISING_EVENT_CTRL					0x0010
+#define SDCDIO_CMD_GET_BANK_INPUT_FALLING_EVENT_CTRL				0x0011
+#define SDCDIO_CMD_SET_BANK_INPUT_FALLING_EVENT_CTRL				0x0012
+#define SDCDIO_CMD_SET_BANK_STATE									0x0013
+#define SDCDIO_CMD_GET_BANK_DIRECTION								0x0014
+#define SDCDIO_CMD_SET_BANK_DIRECTION								0x0015
+#define SDCDIO_CMD_GET_BANK_OUTPUT_INITIAL_VALUE					0x0016
+#define SDCDIO_CMD_SET_BANK_OUTPUT_INITIAL_VALUE					0x0017
+#define SDCDIO_CMD_GET_BANK_CTRL_REGISTER							0x0018
+#define SDCDIO_CMD_SET_BANK_CTRL_REGISTER							0x0019
+#define SDCDIO_CMD_GET_BANK_INPUT_PORT_COUNTER						0x001a
+#define SDCDIO_EVENT												0x1001
+
+
+
+
+#define SDCDIO_STATUS_SUCCESS										0x0000
+#define SDCDIO_LENGTH_INVALID										0x0001
+#define SDCDIO_DATA_INVALID											0x0002
+#define SDCDIO_CONTROLLER_VERSION_UNSUPPORT							0x0003
+#define SDCDIO_UNSUPPORT_COMMAND									0x0004
+#define SDCDIO_ALLOC_MEMORY_FAIL									0x0005
+
+#define SDCDIO_BANK_NOT_FOUND										0x0010
+#define SDCDIO_BANK_IO_NOT_FOUND									0x0011
+#define SDCDIO_UNSUPPORT_INPUT_CAP									0x0012
+#define SDCDIO_UNSUPPORT_OUTPUT_CAP									0x0013
+#define SDCDIO_NO_INPUT_IO											0x0014
+#define SDCDIO_NO_OUTPUT_IO											0x0015
+#define SDCDIO_UNSUPPORT_RISING_EDGE_TRIGGER_CAP					0x0016
+#define SDCDIO_UNSUPPORT_FALLING_EDGE_TRIGGER_CAP					0x0017
+
+#define SDCDIO_UNDEFINE_ERROR										0xffff
+
+#define SUNIX_SDC_DIO_PACK_PTR(ListEntry) \
+	STRUCT_BASE_POINTER(ListEntry, DIO_PACKAGE, Entry)
+
+
+/*******************************************************
+	spi
+*******************************************************/
+
+#define SUNIX_SDC_SPI_MAX					32
+#define SUNIX_SDC_SPI_BUFF					1024
+
+struct snx_spi_info 
+{
+ 	unsigned char			model_name[32];
+	unsigned int			bus_number;
+	unsigned int			dev_number;
+	unsigned int			line;
+	unsigned long			phy2_base_start;
+	void __iomem *			membase;
+	unsigned int			memoffset;
+	unsigned int			memsize;
+	unsigned int			irq;
+
+	struct sdc_cib			cib_info;
+};
+
+struct sunix_sdc_spi_channel 
+{
+	struct snx_spi_info		info;
+	struct cdev				cdev;
+
+	DLIST					packList;
+	spinlock_t				packLock;
+	wait_queue_head_t		readWQ;
+	unsigned int			readDataReady;
+	struct semaphore		sem;
+
+	bool					isOpened;
+
+	unsigned char *			incomeBuff;
+	unsigned char *			outcomeBuff;
+	unsigned char *			translateBuff;
+};
+
+#define SPI_MAX_DATA_LENGTH		512
+
+typedef struct _SPI_HEADER
+{
+	unsigned char		Version;
+	unsigned char		Reserved;
+	unsigned short		CmdResponseEventData;
+	unsigned short		ResponseStatus;
+	unsigned int		Length;
+
+} SPI_HEADER, *PSPI_HEADER;
+
+typedef struct _SPI_PACKAGE
+{
+	DLIST				Entry;
+	SPI_HEADER			Header;
+	unsigned char * 	DataPtr;
+
+} SPI_PACKAGE, *PSPI_PACKAGE;
+
+#define SDCSPI_CMD_GET_INFO											0x0001
+#define SDCSPI_CMD_GET_BASIC_CTRL									0x0002
+#define SDCSPI_CMD_SET_BASIC_CTRL									0x0003
+#define SDCSPI_CMD_GET_MODE_CTRL									0x0004
+#define SDCSPI_CMD_SET_MODE_CTRL									0x0005
+#define SDCSPI_CMD_GET_DIVISOR										0x0006
+#define SDCSPI_CMD_SET_DIVISOR										0x0007
+#define SDCSPI_CMD_GET_STATUS										0x0008
+#define SDCSPI_CMD_GET_CS_SCLK_SETUP_DELAY_TIME_UNIT				0x0009
+#define SDCSPI_CMD_SET_CS_SCLK_SETUP_DELAY_TIME_UNIT				0x000a
+#define SDCSPI_CMD_GET_CS_SCLK_HOLD_DELAY_TIME_UNIT					0x000b
+#define SDCSPI_CMD_SET_CS_SCLK_HOLD_DELAY_TIME_UNIT					0x000c
+#define SDCSPI_CMD_GET_QUIET_DELAY_TIME_UNIT						0x000d
+#define SDCSPI_CMD_SET_QUIET_DELAY_TIME_UNIT						0x000e
+#define SDCSPI_CMD_GET_PORT_IRQ_ENABLE_REG							0x000f
+#define SDCSPI_CMD_SET_PORT_IRQ_ENABLE_REG							0x0010
+#define SDCSPI_CMD_GET_PORT_IRQ_STATUS_REG							0x0011
+#define SDCSPI_CMD_GET_GPIO_OUTPUT_WRITE_ENABLE_REG					0x0012
+#define SDCSPI_CMD_SET_GPIO_OUTPUT_WRITE_ENABLE_REG					0x0013
+#define SDCSPI_CMD_GET_GPIO_OUTPUT_REG								0x0014
+#define SDCSPI_CMD_SET_GPIO_OUTPUT_REG								0x0015
+#define SDCSPI_CMD_GET_TRANSCATION_CTRL_REG0						0x0016
+#define SDCSPI_CMD_SET_TRANSCATION_CTRL_REG0						0x0017
+#define SDCSPI_CMD_GET_TRANSCATION_CTRL_REG1						0x0018
+#define SDCSPI_CMD_SET_TRANSCATION_CTRL_REG1						0x0019
+#define SDCSPI_CMD_GET_PORT_EXTEND_CTRL_REG0						0x001a
+#define SDCSPI_CMD_SET_PORT_EXTEND_CTRL_REG0						0x001b
+#define SDCSPI_CMD_GET_PORT_EXTEND_CTRL_REG1						0x001c
+#define SDCSPI_CMD_SET_PORT_EXTEND_CTRL_REG1						0x001d
+#define SDCSPI_CMD_GET_RAM											0x001e
+#define SDCSPI_CMD_SET_RAM											0x001f
+
+
+
+
+#define SDCSPI_STATUS_SUCCESS										0x0000
+#define SDCSPI_LENGTH_INVALID										0x0001
+#define SDCSPI_DATA_INVALID											0x0002
+#define SDCSPI_CONTROLLER_VERSION_UNSUPPORT							0x0003
+#define SDCSPI_UNSUPPORT_COMMAND									0x0004
+#define SDCSPI_ALLOC_MEMORY_FAIL									0x0005
+
+#define SDCSPI_UNDEFINE_ERROR										0xffff
+
+#define SUNIX_SDC_SPI_PACK_PTR(ListEntry) \
+	STRUCT_BASE_POINTER(ListEntry, SPI_PACKAGE, Entry)
+
+
+/*******************************************************
+
+*******************************************************/
+
+
+extern struct sunix_sdc_board			sunix_sdc_board_table[SUNIX_SDC_BOARD_MAX];
+extern struct sunix_sdc_uart_channel	sunix_sdc_uart_table[SUNIX_SDC_UART_MAX + 1];
+extern struct sunix_sdc_dio_channel		sunix_sdc_dio_table[SUNIX_SDC_DIO_MAX];
+extern struct sunix_sdc_spi_channel		sunix_sdc_spi_table[SUNIX_SDC_SPI_MAX];
+
+extern struct kmem_cache *				sunix_sdc_dio_pack_cache;
+extern struct kmem_cache *				sunix_sdc_spi_pack_cache;
+
+extern unsigned int						sunix_sdc_board_amount;
+extern unsigned int						sunix_sdc_board_amount;
+extern unsigned int						sunix_sdc_uart_amount;
+extern unsigned int						sunix_sdc_dio_amount;
+extern unsigned int						sunix_sdc_spi_amount;
+
+
+#endif
+
+
diff --git a/drivers/mfd/sdc_function.h b/drivers/mfd/sdc_function.h
new file mode 100644
index 000000000000..0a56348123d8
--- /dev/null
+++ b/drivers/mfd/sdc_function.h
@@ -0,0 +1,96 @@
+
+
+#ifndef _SDC_DRVR_FUNCTION_H_
+#define _SDC_DRVR_FUNCTION_H_
+
+
+// bus.c
+
+
+// mem.c
+void __iomem * mem_get_bar_ioremap(struct pci_dev *pdev, unsigned int bar_num);
+void __iomem * mem_get_chl_ioremap(struct pci_dev *pdev, unsigned int bar_num, unsigned int offset, unsigned int size);
+unsigned int mem_rx32(void __iomem *membase, unsigned int base_offset, unsigned int reg_offset);
+void mem_tx32(void __iomem *membase, unsigned int base_offset, unsigned int reg_offset, unsigned int data);
+
+
+// list.c
+void
+SxxListInit(
+	DLIST *					pListHead
+	);
+
+bool
+SxxListEmpty(
+	DLIST *					pListHead
+	);
+
+DLIST *
+SxxListGetNext(
+	DLIST *					pListHead
+	);
+
+DLIST *
+SxxListGetPrev(
+	DLIST *					pListHead
+	);
+
+void
+SxxListRemoveEntry(
+	DLIST *					pEntry
+	);
+
+void
+SxxListInsertEntry(
+	DLIST *					pEntry,
+	DLIST * 				pNewEntry
+	);
+
+void
+SxxListInsertHead(
+	DLIST *					pListHead,
+	DLIST *					pEntry
+	);
+
+void
+SxxListInsertTail(
+	DLIST *					pListHead,
+	DLIST *					pEntry
+	);
+
+
+// uart.c
+int snx_ser_startup(struct snx_ser_state *, int);
+void snx_ser_update_termios(struct snx_ser_state *);
+int sunix_ser_register_ports(void);
+void sunix_ser_unregister_ports(void);
+int sunix_ser_register_driver(void);
+void sunix_ser_unregister_driver(void);
+int sunix_ser_interrupt(struct sunix_sdc_uart_channel *);
+
+
+// dio.c
+int sunix_dio_register_channel(void);
+void sunix_dio_unregister_channel(void);
+int sunix_dio_interrupt(struct sunix_sdc_dio_channel *dio_chl, unsigned int event_header);
+
+
+// dio_pack.c
+int sunix_dio_handle_outcome(struct sunix_sdc_dio_channel *dio_chl, size_t count, unsigned int * outcomeLength);
+int sunix_dio_handle_income(struct sunix_sdc_dio_channel *dio_chl, size_t count);
+
+
+// spi.c
+int sunix_spi_register_channel(void);
+void sunix_spi_unregister_channel(void);
+int sunix_spi_interrupt(struct sunix_sdc_spi_channel *spi_chl, unsigned int event_header);
+
+
+// spi_pack.c
+int sunix_spi_handle_outcome(struct sunix_sdc_spi_channel *spi_chl, size_t count, unsigned int * outcomeLength);
+int sunix_spi_handle_income(struct sunix_sdc_spi_channel *spi_chl, size_t count);
+
+
+#endif
+
+
diff --git a/drivers/mfd/sdc_include.h b/drivers/mfd/sdc_include.h
new file mode 100644
index 000000000000..de342b8a363a
--- /dev/null
+++ b/drivers/mfd/sdc_include.h
@@ -0,0 +1,108 @@
+#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 <linux/tty_flip.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>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39))
+#include <linux/smp_lock.h>
+#endif
+
+#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/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/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
+
+
+#include <linux/sched.h>
+#include <linux/serial_8250.h>
+#include <linux/cdev.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
+#include <linux/sched/signal.h>
+#endif
+
+#include <linux/fs.h>
+
+#include "sdc_define.h"
+#include "sdc_function.h"
+
+
diff --git a/drivers/mfd/spi.c b/drivers/mfd/spi.c
new file mode 100644
index 000000000000..4c7c1c494255
--- /dev/null
+++ b/drivers/mfd/spi.c
@@ -0,0 +1,420 @@
+
+
+#include "sdc_include.h"
+
+
+static int sunix_spi_open(struct inode *inode, struct file *file)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_SPI_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		spi_chl = &sunix_sdc_spi_table[line];
+		if (spi_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (spi_chl->isOpened == true)
+		{
+			status = -EBUSY;
+			break;
+		}
+
+
+		spi_chl->readDataReady = 0;
+		spi_chl->isOpened = true;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		try_module_get(THIS_MODULE);
+#else
+		MOD_INC_USE_COUNT;
+#endif
+
+	} while (false);
+
+	return status;
+}
+
+
+static int sunix_spi_release(struct inode *inode, struct file *file)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+	DLIST * pListHead = NULL;
+	DLIST * e = NULL;
+	unsigned long Flags;
+	PSPI_PACKAGE pPack = NULL;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_SPI_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		spi_chl = &sunix_sdc_spi_table[line];
+		if (spi_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (spi_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+		pListHead = &spi_chl->packList;
+		spin_lock_irqsave(&spi_chl->packLock, Flags);
+		while (!SxxListEmpty(pListHead))
+		{
+			e = SxxListGetNext(pListHead);
+			pPack = SUNIX_SDC_SPI_PACK_PTR(e);
+			if (pPack != NULL)
+			{
+				printk("SUNIX: SPI FREE pack, line:%d, pack:x%p, DataPtrx%p\n", spi_chl->info.line, pPack, pPack->DataPtr);
+				SxxListRemoveEntry(e);
+				if (pPack->DataPtr != NULL)
+				{
+					kfree(pPack->DataPtr);
+					pPack->DataPtr = NULL;
+				}
+				kmem_cache_free(sunix_sdc_spi_pack_cache, pPack);
+				pPack = NULL;
+			}
+		}
+		spin_unlock_irqrestore(&spi_chl->packLock, Flags);
+
+		spi_chl->isOpened = false;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		module_put(THIS_MODULE);
+#else
+		MOD_DEC_USE_COUNT;
+#endif
+
+	} while (false);
+
+	return status;
+}
+
+
+static long sunix_spi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_SPI_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		spi_chl = &sunix_sdc_spi_table[line];
+		if (spi_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (spi_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+	} while (false);
+
+	return status;
+}
+
+
+static ssize_t sunix_spi_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+	unsigned int outcomeLength = 0;
+
+
+	if (line > SUNIX_SDC_SPI_MAX)
+	{
+		return -ENODEV;
+	}
+	spi_chl = &sunix_sdc_spi_table[line];
+	if (spi_chl->info.memsize == 0)
+	{
+		return -ENODEV;
+	}
+	if (spi_chl->isOpened == false)
+	{
+		return -ENODEV;
+	}
+
+	if (count < sizeof(SPI_HEADER))
+	{
+		return -ENOMEM;
+	}
+
+
+	if (down_interruptible(&spi_chl->sem))
+	{
+		return -ERESTARTSYS;
+	}
+
+	if (spi_chl->readDataReady == 0)
+	{
+		up(&spi_chl->sem);
+
+		if ((file->f_flags & O_NONBLOCK))
+		{
+			return -EAGAIN;
+		}
+
+		wait_event_interruptible(spi_chl->readWQ, spi_chl->readDataReady == 1);
+
+		if (down_interruptible(&spi_chl->sem))
+		{
+			return -ERESTARTSYS;
+		}
+	}
+
+
+	do
+	{
+		status = sunix_spi_handle_outcome(spi_chl, count, &outcomeLength);
+		if (status != 0)
+		{
+			break;
+		}
+		if (count < outcomeLength)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		if (outcomeLength <= 0)
+		{
+			status = 0;
+			break;
+		}
+
+		if (copy_to_user((void *)buf, spi_chl->outcomeBuff, outcomeLength))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		status = outcomeLength;
+
+	} while (false);
+
+
+	spi_chl->readDataReady = 0;
+	up(&spi_chl->sem);
+
+	return status;
+}
+
+
+static ssize_t sunix_spi_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	int status = 0;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_SPI_MAX)
+		{
+			status = -ENODEV;
+			break;
+		}
+		spi_chl = &sunix_sdc_spi_table[line];
+		if (spi_chl->info.memsize == 0)
+		{
+			status = -ENODEV;
+			break;
+		}
+		if (spi_chl->isOpened == false)
+		{
+			status = -ENODEV;
+			break;
+		}
+
+		if (count < sizeof(SPI_HEADER))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		if (count > (sizeof(SPI_HEADER) + SPI_MAX_DATA_LENGTH))
+		{
+			status = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(spi_chl->incomeBuff, (void *)buf, count))
+		{
+			status = -EFAULT;
+			break;
+		}
+
+		status = sunix_spi_handle_income(spi_chl, count);
+		if (status == 0)
+		{
+			status = count;
+		}
+
+	} while (false);
+
+	return status;
+}
+
+
+unsigned int sunix_spi_poll(struct file *file, poll_table *wait)
+{
+	int line = MINOR(file->f_path.dentry->d_inode->i_rdev);
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+	unsigned int mask = POLLOUT | POLLWRNORM;
+
+
+	do
+	{
+		if (line > SUNIX_SDC_SPI_MAX)
+		{
+			mask = -ENODEV;
+			break;
+		}
+		spi_chl = &sunix_sdc_spi_table[line];
+		if (spi_chl->info.memsize == 0)
+		{
+			mask = -ENODEV;
+			break;
+		}
+		if (spi_chl->isOpened == false)
+		{
+			mask = -ENODEV;
+			break;
+		}
+
+
+		down(&spi_chl->sem);
+
+		poll_wait(file, &spi_chl->readWQ, wait);
+
+		if (spi_chl->readDataReady == 1)
+		{
+			mask |= (POLLIN | POLLRDNORM);
+		}
+
+		up(&spi_chl->sem);
+
+	} while (false);
+
+	return mask;
+}
+
+
+static int sunix_spi_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	add_uevent_var(env, "DEVMODE=%#o", 0666);
+	return 0;
+}
+
+
+static const struct file_operations sunix_sdc_spi_fops =
+{
+	.owner				= THIS_MODULE,
+	.open				= sunix_spi_open,
+	.release			= sunix_spi_release,
+	.unlocked_ioctl		= sunix_spi_ioctl,
+	.read				= sunix_spi_read,
+	.write 				= sunix_spi_write,
+	.poll				= sunix_spi_poll
+};
+
+
+static int sunix_sdc_spi_dev_major = 0;
+static struct class *sunix_sdc_spi_dev_class = NULL;
+
+
+int sunix_spi_register_channel(void)
+{
+	int err, i;
+	dev_t dev;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+
+
+	err = alloc_chrdev_region(&dev, 0, sunix_sdc_spi_amount, "sunix_sdc_spi");
+	if (err != 0)
+	{
+		return err;
+	}
+
+	sunix_sdc_spi_dev_major = MAJOR(dev);
+
+	sunix_sdc_spi_dev_class = class_create(THIS_MODULE, "sunix_sdc_spi");
+	sunix_sdc_spi_dev_class->dev_uevent = sunix_spi_uevent;
+
+	for (i = 0; i < sunix_sdc_spi_amount; i++)
+	{
+		spi_chl = &sunix_sdc_spi_table[i];
+		if ((spi_chl != NULL) && (spi_chl->info.membase != NULL))
+		{
+			request_mem_region(spi_chl->info.phy2_base_start + spi_chl->info.memoffset, spi_chl->info.memsize, "sunix_sdc_spi");
+
+			cdev_init(&spi_chl->cdev, &sunix_sdc_spi_fops);
+			spi_chl->cdev.owner = THIS_MODULE;
+
+			cdev_add(&spi_chl->cdev, MKDEV(sunix_sdc_spi_dev_major, i), 1);
+
+			device_create(sunix_sdc_spi_dev_class, NULL, MKDEV(sunix_sdc_spi_dev_major, i), NULL, "SDCSPI%d", i);
+		}
+	}
+
+	return 0;
+}
+
+
+void sunix_spi_unregister_channel(void)
+{
+	int i;
+	struct sunix_sdc_spi_channel *spi_chl = NULL;
+
+
+	for (i = 0; i < sunix_sdc_spi_amount; i++)
+	{
+		spi_chl = &sunix_sdc_spi_table[i];
+		if ((spi_chl != NULL) && (spi_chl->info.membase != NULL))
+		{
+			device_destroy(sunix_sdc_spi_dev_class, MKDEV(sunix_sdc_spi_dev_major, i));
+
+			release_mem_region(spi_chl->info.phy2_base_start + spi_chl->info.memoffset, spi_chl->info.memsize);
+		}
+	}
+
+	class_unregister(sunix_sdc_spi_dev_class);
+	class_destroy(sunix_sdc_spi_dev_class);
+
+	unregister_chrdev_region(MKDEV(sunix_sdc_spi_dev_major, 0), MINORMASK);
+}
+
+
+int sunix_spi_interrupt(struct sunix_sdc_spi_channel *spi_chl, unsigned int event_header)
+{
+    return 0;
+}
diff --git a/drivers/mfd/spi_pack.c b/drivers/mfd/spi_pack.c
new file mode 100644
index 000000000000..ff49e0ae94ac
--- /dev/null
+++ b/drivers/mfd/spi_pack.c
@@ -0,0 +1,1506 @@
+
+
+#include "sdc_include.h"
+
+
+static void get_info(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	struct sdc_cib * cib_info = &spi_chl->info.cib_info;
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int Address = 0;
+	int i = 0;
+
+
+	do
+	{
+		Address = spi_chl->info.phy2_base_start + spi_chl->info.memoffset;
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?(31 + (cib_info->spi_number_of_device * 12)):0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		memcpy(&TrBuff[TrLength], spi_chl->info.model_name, 16);
+		TrLength += 16;
+		TrBuff[TrLength++] = spi_chl->info.bus_number;
+		TrBuff[TrLength++] = spi_chl->info.dev_number;
+		TrBuff[TrLength++] = spi_chl->info.line;
+		TrBuff[TrLength++] = (unsigned char)((Address & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((Address & 0x000000ff));
+		TrBuff[TrLength++] = (unsigned char)(spi_chl->info.irq);
+
+		TrBuff[TrLength++] = cib_info->version;
+		TrBuff[TrLength++] = (unsigned char)((cib_info->spi_significand_of_clock & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((cib_info->spi_significand_of_clock & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((cib_info->spi_significand_of_clock & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((cib_info->spi_significand_of_clock & 0x000000ff));
+		TrBuff[TrLength++] = cib_info->spi_exponent_of_clock;
+		TrBuff[TrLength++] = cib_info->spi_number_of_device;
+
+		for (i = 0; i < cib_info->spi_number_of_device; i++)
+		{
+			TrBuff[TrLength++] = i;
+			TrBuff[TrLength++] = cib_info->spi_device_cap[i].type;
+			TrBuff[TrLength++] = cib_info->spi_device_cap[i].number_of_gpio_input;
+			TrBuff[TrLength++] = cib_info->spi_device_cap[i].number_of_gpio_output;
+			memcpy(&TrBuff[TrLength], cib_info->spi_device_cap[i].name, 8);
+			TrLength += 8;
+		}
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_basic_ctrl(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BasicCtrl = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), get basic ctrl, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		BasicCtrl = (CtrlReg & 0x000000ff);
+		//printk("SUNIX: SPI (%d), get basic ctrl, 2, BasicCtrl:x%02x\n", spi_chl->info.line, BasicCtrl);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = BasicCtrl;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_basic_ctrl(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char BasicCtrl = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		BasicCtrl = (unsigned char)*(RxBuff + sizeof(SPI_HEADER) + 0);
+		//printk("SUNIX: SPI (%d), set basic ctrl, BasicCtrl:x%02x\n", spi_chl->info.line, BasicCtrl);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), set basic ctrl, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0xffffff00;
+		//printk("SUNIX: SPI (%d), set basic ctrl, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= BasicCtrl;
+		//printk("SUNIX: SPI (%d), set basic ctrl, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 0, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_mode_ctrl(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char ModeCtrl = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), get mode ctrl, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		ModeCtrl = ((CtrlReg & 0x0000ff00) >> 8);
+		//printk("SUNIX: SPI (%d), get mode ctrl, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = ModeCtrl;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_mode_ctrl(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char ModeCtrl = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		ModeCtrl = (unsigned char)*(RxBuff + sizeof(SPI_HEADER) + 0);
+		//printk("SUNIX: SPI (%d), set mode ctrl, ModeCtrl:x%02x\n", spi_chl->info.line, ModeCtrl);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), set mode ctrl, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0xffff00ff;
+		//printk("SUNIX: SPI (%d), set mode ctrl, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= (ModeCtrl << 8);
+		//printk("SUNIX: SPI (%d), set mode ctrl, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 0, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_divisor(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned short Divisor = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), get divisor, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		Divisor = ((CtrlReg & 0xffff0000) >> 16);
+		//printk("SUNIX: SPI (%d), get divisor, 2, Divisor:x%04x\n", spi_chl->info.line, Divisor);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?2:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((Divisor & 0xff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((Divisor & 0x00ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_divisor(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned short Divisor = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 2)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		Divisor  = (unsigned short)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 8);
+		Divisor |= (unsigned short)(*(RxBuff + sizeof(SPI_HEADER) + 1));
+		//printk("SUNIX: SPI (%d), set divisor, Divisor:x%04x\n", spi_chl->info.line, Divisor);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 0);
+		//printk("SUNIX: SPI (%d), set divisor, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0x0000ffff;
+		//printk("SUNIX: SPI (%d), set divisor, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= (Divisor << 16);
+		//printk("SUNIX: SPI (%d), set divisor, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 0, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_status(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char Status = 0;
+	unsigned int StatusReg = 0;
+
+
+	do
+	{
+		StatusReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 1);
+		//printk("SUNIX: SPI (%d), get status, 1, StatusReg:x%08x\n", spi_chl->info.line, StatusReg);
+		Status = (StatusReg & 0x000000ff);
+		//printk("SUNIX: SPI (%d), get status, 2, Status:x%02x\n", spi_chl->info.line, Status);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = Status;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_CS_SCLK_setup_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), get CS/SCLK setup delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		TimeUnit = (CtrlReg & 0x000000ff);
+		//printk("SUNIX: SPI (%d), get CS/SCLK setup delay time unit, 2, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = TimeUnit;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_CS_SCLK_setup_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		TimeUnit = (unsigned char)*(RxBuff + sizeof(SPI_HEADER) + 0);
+		//printk("SUNIX: SPI (%d), set CS/SCLK setup delay time unit, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), set CS/SCLK setup delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0xffffff00;
+		//printk("SUNIX: SPI (%d), set CS/SCLK setup delay time unit, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= TimeUnit;
+		//printk("SUNIX: SPI (%d), set CS/SCLK setup delay time unit, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 2, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_CS_SCLK_hold_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), get CS/SCLK hold delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		TimeUnit = ((CtrlReg & 0x0000ff00) >> 8);
+		//printk("SUNIX: SPI (%d), get CS/SCLK hold delay time unit, 2, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = TimeUnit;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_CS_SCLK_hold_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		TimeUnit = (unsigned char)*(RxBuff + sizeof(SPI_HEADER) + 0);
+		//printk("SUNIX: SPI (%d), set CS/SCLK hold delay time unit, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), set CS/SCLK hold delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0xffff00ff;
+		//printk("SUNIX: SPI (%d), set CS/SCLK hold delay time unit, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= (TimeUnit << 8);
+		//printk("SUNIX: SPI (%d), set CS/SCLK hold delay time unit, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 2, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_quiet_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), get quiet delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		TimeUnit = ((CtrlReg & 0x00ff0000) >> 16);
+		//printk("SUNIX: SPI (%d), get quiet delay time unit, 2, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?1:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = TimeUnit;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_quiet_delay_time_unit(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned char TimeUnit = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 1)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		TimeUnit = (unsigned char)*(RxBuff + sizeof(SPI_HEADER) + 0);
+		//printk("SUNIX: SPI (%d), set quiet delay time unit, TimeUnit:x%02x\n", spi_chl->info.line, TimeUnit);
+
+
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 2);
+		//printk("SUNIX: SPI (%d), set quiet delay time unit, 1, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg &= 0xff00ffff;
+		//printk("SUNIX: SPI (%d), set quiet delay time unit, 2, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+		CtrlReg |= (TimeUnit << 16);
+		//printk("SUNIX: SPI (%d), set quiet delay time unit, 3, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 2, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_port_irq_enable_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 3);
+		//printk("SUNIX: SPI (%d), get port irq enable reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_port_irq_enable_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 4)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		CtrlReg  =  (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 24);
+		CtrlReg |=  (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 1) << 16);
+		CtrlReg |=  (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 2) << 8);
+		CtrlReg |=  (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 3));
+		//printk("SUNIX: SPI (%d), set port irq enable reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 3, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_port_irq_status_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int StatusReg = 0;
+
+
+	do
+	{
+		StatusReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 4);
+		//printk("SUNIX: SPI (%d), get port irq status reg, StatusReg:x%08x\n", spi_chl->info.line, StatusReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((StatusReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((StatusReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((StatusReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((StatusReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void get_gpio_output_write_enable_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 5);
+		//printk("SUNIX: SPI (%d), get gpio output write enable reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_gpio_output_write_enable_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 4)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		CtrlReg  = (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 24);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 1) << 16);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 2) << 8);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 3));
+		//printk("SUNIX: SPI (%d), set gpio output write enable reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 5, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_gpio_output_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		CtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 6);
+		//printk("SUNIX: SPI (%d), get gpio output reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((CtrlReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_gpio_output_reg(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int CtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 4)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		CtrlReg  = (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 24);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 1) << 16);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 2) << 8);
+		CtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 3));
+		//printk("SUNIX: SPI (%d), set gpio output reg, CtrlReg:x%08x\n", spi_chl->info.line, CtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 6, CtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_transcation_ctrl_reg0(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int TransCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 0)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+
+
+		TransCtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 7);
+		//printk("SUNIX: SPI (%d), get transcation ctrl reg0, TransCtrlReg:x%08x\n", spi_chl->info.line, TransCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_transcation_ctrl_reg0(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int TransCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 4)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		TransCtrlReg  = (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 24);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 1) << 16);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 2) << 8);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 3));
+		//printk("SUNIX: SPI (%d), set transcation ctrl reg0, TransCtrlReg:x%08x\n", spi_chl->info.line, TransCtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 7, TransCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_transcation_ctrl_reg1(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int TransCtrlReg = 0;
+
+
+	do
+	{
+		TransCtrlReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, 8);
+		//printk("SUNIX: SPI (%d), get transcation ctrl reg1, TransCtrlReg:x%08x\n", spi_chl->info.line, TransCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?4:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0xff000000) >> 24);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x00ff0000) >> 16);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x0000ff00) >> 8);
+		TrBuff[TrLength++] = (unsigned char)((TransCtrlReg & 0x000000ff));
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_transcation_ctrl_reg1(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int TransCtrlReg = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length != 4)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+		TransCtrlReg  = (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 0) << 24);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 1) << 16);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 2) << 8);
+		TransCtrlReg |= (unsigned int)(*(RxBuff + sizeof(SPI_HEADER) + 3));
+		//printk("SUNIX: SPI (%d), set transcation ctrl reg1, TransCtrlReg:x%08x\n", spi_chl->info.line, TransCtrlReg);
+
+		mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, 8, TransCtrlReg);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void get_ram(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int RamReg = 0;
+	unsigned int RamRegIndex = 0;
+	unsigned char Data[SPI_MAX_DATA_LENGTH] = {0};
+	int DataIndex = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length > SPI_MAX_DATA_LENGTH)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+
+
+		for (RamRegIndex = 128; RamRegIndex <= 255; RamRegIndex++)
+		{
+			RamReg = mem_rx32(spi_chl->info.membase, spi_chl->info.memoffset, RamRegIndex);
+			//printk("SUNIX: SPI (%d), get ram, RamRegIndex:%d, RamReg:x%08x\n", spi_chl->info.line, RamRegIndex, RamReg);
+			Data[DataIndex++] = (RamReg & 0x000000ff);
+			if (DataIndex >= pRxHeader->Length)
+			{
+				break;
+			}
+			Data[DataIndex++] = ((RamReg & 0x0000ff00) >> 8);
+			if (DataIndex >= pRxHeader->Length)
+			{
+				break;
+			}
+			Data[DataIndex++] = ((RamReg & 0x00ff0000) >> 16);
+			if (DataIndex >= pRxHeader->Length)
+			{
+				break;
+			}
+			Data[DataIndex++] = ((RamReg & 0xff000000) >> 24);
+			if (DataIndex >= pRxHeader->Length)
+			{
+				break;
+			}
+		}
+		//printk("SUNIX: SPI (%d), get ram, DataIndex:%d\n", spi_chl->info.line, DataIndex);
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?DataIndex:0;
+	TrLength = sizeof(SPI_HEADER);
+	if (pTrHeader->ResponseStatus == SDCSPI_STATUS_SUCCESS)
+	{
+		memcpy(&TrBuff[TrLength], Data, DataIndex);
+		TrLength += DataIndex;
+	}
+
+	*translateLength = TrLength;
+}
+
+
+static void set_ram(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	unsigned char * RxBuff = spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_STATUS_SUCCESS;
+	unsigned int TrLength = 0;
+	unsigned int RamReg = 0;
+	unsigned int RamRegIndex = 0;
+	unsigned char Data[SPI_MAX_DATA_LENGTH] = {0};
+	int DataIndex = 0, i = 0;
+	unsigned int LengthInDw = 0;
+
+
+	do
+	{
+		if (pRxHeader->Length > SPI_MAX_DATA_LENGTH)
+		{
+			nStatus = SDCSPI_LENGTH_INVALID;
+			break;
+		}
+
+
+		memcpy(Data, RxBuff + sizeof(SPI_HEADER), pRxHeader->Length);
+
+		if ((pRxHeader->Length % 4) != 0)
+		{
+			LengthInDw = (pRxHeader->Length / 4) + 1;
+		}
+		else
+		{
+			LengthInDw = (pRxHeader->Length / 4);
+		}
+		//printk("SUNIX: SPI (%d), set ram, Length:%d, LengthInDw:%d\n", spi_chl->info.line, pRxHeader->Length, LengthInDw);
+
+		DataIndex = 0;
+		RamRegIndex = 128;
+		for (i = 0; i < LengthInDw; i++)
+		{
+			RamReg  = Data[DataIndex++];
+			RamReg |= (Data[DataIndex++] << 8);
+			RamReg |= (Data[DataIndex++] << 16);
+			RamReg |= (Data[DataIndex++] << 24);
+
+			//printk("SUNIX: SPI (%d), set ram, RamRegIndex:%d, RamReg:x%08x\n", spi_chl->info.line, RamRegIndex, RamReg);
+			mem_tx32(spi_chl->info.membase, spi_chl->info.memoffset, RamRegIndex, RamReg);
+			RamRegIndex++;
+		}
+
+	} while (false);
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = (nStatus == SDCSPI_STATUS_SUCCESS)?0:0;
+	TrLength = sizeof(SPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+static void unsupport(struct sunix_sdc_spi_channel *spi_chl, unsigned int incomeLength, unsigned int * translateLength)
+{
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	unsigned int nStatus = SDCSPI_UNSUPPORT_COMMAND;
+	unsigned int TrLength = 0;
+
+
+	memset(TrBuff, 0, SUNIX_SDC_SPI_BUFF);
+	TrLength = 0;
+
+	pTrHeader->Version = 0x01;
+	pTrHeader->CmdResponseEventData = pRxHeader->CmdResponseEventData | 0x8000;
+	pTrHeader->ResponseStatus = nStatus;
+	pTrHeader->Length = 0;
+	TrLength = sizeof(PSPI_HEADER);
+
+	*translateLength = TrLength;
+}
+
+
+int sunix_spi_handle_outcome(struct sunix_sdc_spi_channel *spi_chl, size_t count, unsigned int * outcomeLength)
+{
+	int status = 0;
+	PSPI_PACKAGE pPack = NULL;
+	DLIST *	pListHead = &spi_chl->packList;
+	DLIST * e = NULL;
+	unsigned char * TxBuff = spi_chl->outcomeBuff;
+	unsigned int TrLength = 0;
+
+
+	do
+	{
+		do
+		{
+			if (!SxxListEmpty(pListHead))
+			{
+				e = pListHead->Flink;
+			}
+			else
+			{
+				break;
+			}
+
+			while ((e != NULL) && (e != pListHead))
+			{
+				if (e != NULL)
+				{
+					pPack = SUNIX_SDC_SPI_PACK_PTR(e);
+					if (pPack != NULL)
+					{
+						break;
+					}
+
+					e = e->Flink;
+				}
+			}
+
+		} while (false);
+
+		if (pPack == NULL)
+		{
+			*outcomeLength = 0;
+			break;
+		}
+
+
+		SxxListRemoveEntry(&pPack->Entry);
+
+
+		memset(TxBuff, 0, SUNIX_SDC_SPI_BUFF);
+		memcpy(TxBuff, &pPack->Header, sizeof(SPI_HEADER));
+		TrLength = sizeof(SPI_HEADER);
+		if (pPack->DataPtr != NULL)
+		{
+			memcpy(TxBuff + sizeof(SPI_HEADER), pPack->DataPtr, pPack->Header.Length);
+			TrLength += pPack->Header.Length;
+		}
+
+		*outcomeLength = TrLength;
+
+		//printk("SUNIX: SPI FREE pack, line:%d, pack:x%p, DataPtrx%p\n", spi_chl->info.line, pPack, pPack->DataPtr);
+		if (pPack->DataPtr != NULL)
+		{
+			kfree(pPack->DataPtr);
+			pPack->DataPtr = NULL;
+		}
+		kmem_cache_free(sunix_sdc_spi_pack_cache, pPack);
+		pPack = NULL;
+			
+	} while (false);
+
+	return status;
+}
+
+
+int sunix_spi_handle_income(struct sunix_sdc_spi_channel *spi_chl, size_t count)
+{
+	int status = 0;
+	PSPI_HEADER pRxHeader = (PSPI_HEADER)spi_chl->incomeBuff;
+	PSPI_HEADER pTrHeader = (PSPI_HEADER)spi_chl->translateBuff;
+	unsigned char * TrBuff = spi_chl->translateBuff;
+	PSPI_PACKAGE pPack = NULL;
+	unsigned int translateLength = 0;
+	unsigned long Flags;
+
+
+	do
+	{
+		// debug
+		/*
+		printk("++++++++++++++++++++++++++++++++++++++++++++++\n");
+		printk("SUNIX: SPI_RX, Version              :x%02x\n", pRxHeader->Version);
+		printk("SUNIX: SPI_RX, CmdResponseEventData :x%04x\n", pRxHeader->CmdResponseEventData);
+		printk("SUNIX: SPI_RX, Length               :x%08x\n", pRxHeader->Length);
+		printk("++++++++++++++++++++++++++++++++++++++++++++++\n");
+		*/
+
+
+		switch (pRxHeader->CmdResponseEventData)
+		{
+			case SDCSPI_CMD_GET_INFO :
+				get_info(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_BASIC_CTRL :
+				get_basic_ctrl(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_BASIC_CTRL :
+				set_basic_ctrl(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_MODE_CTRL :
+				get_mode_ctrl(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_MODE_CTRL :
+				set_mode_ctrl(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_DIVISOR :
+				get_divisor(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_DIVISOR :
+				set_divisor(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_STATUS :
+				get_status(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_CS_SCLK_SETUP_DELAY_TIME_UNIT :
+				get_CS_SCLK_setup_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_CS_SCLK_SETUP_DELAY_TIME_UNIT :
+				set_CS_SCLK_setup_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_CS_SCLK_HOLD_DELAY_TIME_UNIT :
+				get_CS_SCLK_hold_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_CS_SCLK_HOLD_DELAY_TIME_UNIT :
+				set_CS_SCLK_hold_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_QUIET_DELAY_TIME_UNIT :
+				get_quiet_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_QUIET_DELAY_TIME_UNIT :
+				set_quiet_delay_time_unit(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_PORT_IRQ_ENABLE_REG :
+				get_port_irq_enable_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_PORT_IRQ_ENABLE_REG :
+				set_port_irq_enable_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_PORT_IRQ_STATUS_REG :
+				get_port_irq_status_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_GPIO_OUTPUT_WRITE_ENABLE_REG :
+				get_gpio_output_write_enable_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_GPIO_OUTPUT_WRITE_ENABLE_REG :
+				set_gpio_output_write_enable_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_GPIO_OUTPUT_REG :
+				get_gpio_output_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_GPIO_OUTPUT_REG :
+				set_gpio_output_reg(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_TRANSCATION_CTRL_REG0 :
+				get_transcation_ctrl_reg0(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_TRANSCATION_CTRL_REG0 :
+				set_transcation_ctrl_reg0(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_TRANSCATION_CTRL_REG1 :
+				get_transcation_ctrl_reg1(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_TRANSCATION_CTRL_REG1 :
+				set_transcation_ctrl_reg1(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_PORT_EXTEND_CTRL_REG0 :
+				unsupport(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_PORT_EXTEND_CTRL_REG0 :
+				unsupport(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_PORT_EXTEND_CTRL_REG1 :
+				unsupport(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_PORT_EXTEND_CTRL_REG1 :
+				unsupport(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_GET_RAM :
+				get_ram(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			case SDCSPI_CMD_SET_RAM :
+				set_ram(spi_chl, (unsigned int)count, &translateLength);
+				break;
+
+			default :
+				unsupport(spi_chl, (unsigned int)count, &translateLength);
+				break;
+		}
+
+
+		// debug
+		/*
+		printk("----------------------------------------------\n");
+		printk("SUNIX: SPI_TR, translateLength      :%d\n", translateLength);
+		printk("SUNIX: SPI_TR, Version              :x%02x\n", pTrHeader->Version);
+		printk("SUNIX: SPI_TR, CmdResponseEventData :x%04x\n", pTrHeader->CmdResponseEventData);
+		printk("SUNIX: SPI_TR, ResponseStatus       :x%04x\n", pTrHeader->ResponseStatus);
+		printk("SUNIX: SPI_TR, Length               :x%08x\n", pTrHeader->Length);
+		{
+			int i;
+			for (i = 0; i < pTrHeader->Length; i++)
+				printk("x%02x ", (unsigned char)*(TrBuff + sizeof(SPI_HEADER) + i));
+		}
+		printk("----------------------------------------------\n");
+		*/
+
+
+		if (pTrHeader->Length > SPI_MAX_DATA_LENGTH)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		if (translateLength > (SPI_MAX_DATA_LENGTH + sizeof(SPI_HEADER)))
+		{
+			status = -ENOMEM;
+			break;
+		}
+
+		pPack = kmem_cache_alloc(sunix_sdc_spi_pack_cache, GFP_ATOMIC);
+		if (pPack == NULL)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		memset(pPack, 0, sizeof(SPI_PACKAGE));
+		pPack->DataPtr = (unsigned char *)kmalloc(SPI_MAX_DATA_LENGTH, GFP_KERNEL);
+		if (pPack->DataPtr == NULL)
+		{
+			status = -ENOMEM;
+			break;
+		}
+		memset(pPack->DataPtr, 0, SPI_MAX_DATA_LENGTH);
+
+
+		SxxListInit(&pPack->Entry);
+		memcpy(&pPack->Header, pTrHeader, sizeof(SPI_HEADER));
+		memcpy(pPack->DataPtr, TrBuff + sizeof(SPI_HEADER), pTrHeader->Length);
+
+		spin_lock_irqsave(&spi_chl->packLock, Flags);
+		//printk("SUNIX: SPI ALOC pack, line:%d, pack:x%p, DataPtrx%p\n", spi_chl->info.line, pPack, pPack->DataPtr);
+		SxxListInsertTail(&spi_chl->packList, &pPack->Entry);
+
+		spi_chl->readDataReady = 1;
+		wake_up_interruptible(&spi_chl->readWQ);
+		spin_unlock_irqrestore(&spi_chl->packLock, Flags);
+
+	} while (false);
+
+	if (status != 0)
+	{
+		if (pPack != NULL)
+		{
+			if (pPack->DataPtr != NULL)
+			{
+				kfree(pPack->DataPtr);
+				pPack->DataPtr = NULL;
+			}
+
+			kmem_cache_free(sunix_sdc_spi_pack_cache, pPack);
+		}
+	}
+
+	return status;
+}
+
+
diff --git a/drivers/mfd/uart.c b/drivers/mfd/uart.c
new file mode 100644
index 000000000000..bb88630fdca1
--- /dev/null
+++ b/drivers/mfd/uart.c
@@ -0,0 +1,3796 @@
+
+
+#include "sdc_include.h"
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
+static DEFINE_SEMAPHORE(ser_port_sem);
+#else
+static DECLARE_MUTEX(ser_port_sem);
+#endif
+
+
+#define SNX_HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)
+#define sunix_ser_users(state)	((state)->count + ((state)->info ? (state)->info->blocked_open : 0))
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+static struct tty_port snx_service_port;
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+struct serial_uart_config
+{
+	char	*name;
+	int		dfl_xmit_fifo_size;
+	int		flags;
+};
+#endif
+
+
+static const struct serial_uart_config snx_uart_config[PORT_SER_MAX_UART + 1] =
+{
+	{ "unknown",    1,      0 },
+	{ "8250",       1,      0 },
+	{ "16450",      1,      0 },
+	{ "16550",      1,      0 },
+	{ "16550A",     16,     UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "Cirrus",     1,    	0 },
+	{ "ST16650",    1,    	0 },
+	{ "ST16650V2",  32,    	UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "TI16750",    64,    	UART_CLEAR_FIFO | UART_USE_FIFO },
+};
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+static int		sunix_ser_refcount;
+static struct tty_struct			*sunix_ser_tty[SUNIX_SDC_UART_MAX + 1];
+static struct termios				*sunix_ser_termios[SUNIX_SDC_UART_MAX + 1];
+static struct termios				*sunix_ser_termios_locked[SUNIX_SDC_UART_MAX + 1];
+#endif
+
+
+static _INLINE_ void snx_ser_handle_cts_change(struct snx_ser_port *, unsigned int);
+static _INLINE_ void snx_ser_update_mctrl(struct snx_ser_port *, unsigned int, unsigned int);
+static void     snx_ser_write_wakeup(struct snx_ser_port *);
+static void     snx_ser_stop(struct tty_struct *);
+static void     __snx_ser_start(struct tty_struct *);
+static void     snx_ser_start(struct tty_struct *);
+static void     snx_ser_tasklet_action(unsigned long);
+
+
+static void     snx_ser_shutdown(struct snx_ser_state *);
+static _INLINE_ void __snx_ser_put_char(struct snx_ser_port *, struct circ_buf *, unsigned char);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26))
+static int      snx_ser_put_char(struct tty_struct *, unsigned char);
+#else
+static void     snx_ser_put_char(struct tty_struct *, unsigned char);
+#endif
+static void     snx_ser_flush_chars(struct tty_struct *);
+static int      snx_ser_chars_in_buffer(struct tty_struct *);
+static void     snx_ser_flush_buffer(struct tty_struct *);
+static void     snx_ser_send_xchar(struct tty_struct *, char);
+static void     snx_ser_throttle(struct tty_struct *);
+static void     snx_ser_unthrottle(struct tty_struct *);
+static int      snx_ser_get_info(struct snx_ser_state *, struct serial_struct *);
+static int      snx_ser_set_info(struct snx_ser_state *, struct serial_struct *);
+static int      snx_ser_write_room(struct tty_struct *);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10))
+static int      snx_ser_write(struct tty_struct *, const unsigned char *, int);
+#else
+static int      snx_ser_write(struct tty_struct *, int, const unsigned char *, int);
+#endif
+static int      snx_ser_get_lsr_info(struct snx_ser_state *, unsigned int *);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
+static int      snx_ser_tiocmget(struct tty_struct *);
+static int      snx_ser_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+static int      snx_ser_tiocmget(struct tty_struct *, struct file *);
+static int      snx_ser_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int);
+#else
+static int      snx_ser_get_modem_info(struct snx_ser_state *, unsigned int *);
+static int      snx_ser_set_modem_info(struct snx_ser_state *, unsigned int, unsigned int *);
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+static int      snx_ser_break_ctl(struct tty_struct *, int);
+#else
+static void     snx_ser_break_ctl(struct tty_struct *, int);
+#endif
+static int      snx_ser_wait_modem_status(struct snx_ser_state *, unsigned long);
+static int      snx_ser_get_count(struct snx_ser_state *, struct serial_icounter_struct *);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
+static int      snx_ser_ioctl(struct tty_struct *, unsigned int, unsigned long);
+#else
+static int      snx_ser_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long);
+#endif
+
+static void     snx_ser_hangup(struct tty_struct *);
+unsigned int    snx_ser_get_divisor(struct snx_ser_port *, unsigned int);
+extern void     snx_ser_change_speed(struct snx_ser_state *, struct SNXTERMIOS *);
+static void     snx_ser_set_termios(struct tty_struct *, struct SNXTERMIOS *);
+
+static void     snx_ser_update_timeout(struct snx_ser_port *, unsigned int, unsigned int);
+static struct   snx_ser_state *snx_ser_get(struct snx_ser_driver *, int);
+static int      snx_ser_block_til_ready(struct file *, struct snx_ser_state *);
+static void     snx_ser_wait_until_sent(struct tty_struct *, int);
+static int      snx_ser_open(struct tty_struct *, struct file *);
+static void     snx_ser_close(struct tty_struct *, struct file *);
+
+
+static void         sunix_ser_set_mctrl(struct snx_ser_port *, unsigned int);
+static unsigned int sunix_ser_tx_empty(struct snx_ser_port *);
+static unsigned int sunix_ser_get_mctrl(struct snx_ser_port *);
+static void         sunix_ser_stop_tx(struct snx_ser_port *, unsigned int);
+static void         sunix_ser_start_tx(struct snx_ser_port *, unsigned int);
+static void         sunix_ser_stop_rx(struct snx_ser_port *);
+static void         sunix_ser_enable_ms(struct snx_ser_port *);
+static void         sunix_ser_break_ctl(struct snx_ser_port *, int);
+static int          sunix_ser_startup(struct snx_ser_port *);
+static void         sunix_ser_shutdown(struct snx_ser_port *);
+static unsigned int sunix_ser_get_divisor(struct snx_ser_port *, unsigned int);
+static void         sunix_ser_set_termios(struct snx_ser_port *, struct SNXTERMIOS *, struct SNXTERMIOS *);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+	static void sunix_ser_timeout(struct timer_list *t);
+#else
+	static void sunix_ser_timeout(unsigned long data);
+#endif
+
+
+static _INLINE_ void sunix_ser_receive_chars(struct sunix_sdc_uart_channel *, unsigned char *);
+static _INLINE_ void sunix_ser_transmit_chars(struct sunix_sdc_uart_channel *);
+static _INLINE_ void sunix_ser_check_modem_status(struct sunix_sdc_uart_channel *, unsigned char);
+static _INLINE_ void sunix_ser_handle_port(struct sunix_sdc_uart_channel *, unsigned char);
+
+
+static void     sunix_ser_release_io(struct snx_ser_port *);
+static void     sunix_ser_request_io(struct snx_ser_port *);
+static void     sunix_ser_configure_port(struct snx_ser_driver *, struct snx_ser_state *, struct snx_ser_port *);
+static void     sunix_ser_unconfigure_port(struct snx_ser_driver *, struct snx_ser_state *);
+static int      sunix_ser_add_one_port(struct snx_ser_driver *, struct snx_ser_port *);
+static int      sunix_ser_remove_one_port(struct snx_ser_driver *, struct snx_ser_port *);
+
+
+static unsigned char READ_UART_RX(struct sunix_sdc_uart_channel *);
+static unsigned char READ_UART_IIR(struct sunix_sdc_uart_channel *);
+static unsigned char READ_UART_LCR(struct sunix_sdc_uart_channel *);
+static unsigned char READ_UART_LSR(struct sunix_sdc_uart_channel *);
+static unsigned char READ_UART_MSR(struct sunix_sdc_uart_channel *);
+static void WRITE_UART_TX(struct sunix_sdc_uart_channel *, unsigned char);
+static void WRITE_UART_IER(struct sunix_sdc_uart_channel *, unsigned char);
+static void WRITE_UART_FCR(struct sunix_sdc_uart_channel *, unsigned char);
+static void WRITE_UART_LCR(struct sunix_sdc_uart_channel *, unsigned char);
+static void WRITE_UART_MCR(struct sunix_sdc_uart_channel *, unsigned char);
+static void WRITE_UART_DLL(struct sunix_sdc_uart_channel *, int);
+static void WRITE_UART_DLM(struct sunix_sdc_uart_channel *, int);
+
+
+static void snx_ser_insert_char(struct snx_ser_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+	struct snx_ser_info *info = port->info;
+    struct tty_struct *tty = info->tty;
+    struct snx_ser_state *state = NULL;
+    struct tty_port *tport = NULL;
+
+
+	state = tty->driver_data;
+	tport = &state->tport;
+#else
+	struct tty_struct *tty = port->info->tty;
+#endif
+
+	if ((status & port->ignore_status_mask & ~overrun) == 0)
+	{
+#if	(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+		if (tty_insert_flip_char(tport, ch, flag) == 0)
+		{
+				++port->icount.buf_overrun;			
+		}		
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+		if (tty_insert_flip_char(tty, ch, flag) == 0)
+			++port->icount.buf_overrun;
+#else
+		tty_insert_flip_char(tty, ch, flag);	
+#endif
+    }
+
+
+	if (status & ~port->ignore_status_mask & overrun)
+	{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+		if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
+		{
+			++port->icount.buf_overrun;
+		}		
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+		if (tty_insert_flip_char(tty, 0, TTY_OVERRUN) == 0)
+			++port->icount.buf_overrun;
+#else
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+#endif
+    }
+}
+
+
+static unsigned char READ_UART_RX(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char data;
+
+
+	if (sp->port.iobase)
+	{
+		data = inb(sp->port.iobase + UART_RX);
+		return data;
+	}
+	return 0;
+}
+
+
+static void WRITE_UART_TX(struct sunix_sdc_uart_channel *sp, unsigned char data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_TX);
+	}
+}
+
+
+static void WRITE_UART_IER(struct sunix_sdc_uart_channel *sp, unsigned char data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_IER);
+	}
+}
+
+
+static unsigned char READ_UART_IIR(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char data;
+
+
+	if (sp->port.iobase)
+	{
+		data = inb(sp->port.iobase + UART_IIR);
+		return data;
+	}
+	return 0;
+}
+
+
+static void WRITE_UART_FCR(struct sunix_sdc_uart_channel *sp, unsigned char data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_FCR);
+	}
+}
+
+
+static unsigned char READ_UART_LCR(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char data;
+
+
+	if (sp->port.iobase)
+	{
+		data = inb(sp->port.iobase + UART_LCR);
+		return data;
+	}
+	return 0;
+}
+
+
+static void WRITE_UART_LCR(struct sunix_sdc_uart_channel *sp, unsigned char data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_LCR);
+	}
+}
+
+
+static void WRITE_UART_MCR(struct sunix_sdc_uart_channel *sp, unsigned char data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_MCR);
+	}
+}
+
+
+static unsigned char READ_UART_LSR(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char data;
+
+
+	if (sp->port.iobase)
+	{
+		data = inb(sp->port.iobase + UART_LSR);
+		return data;
+	}
+	return 0;
+}
+
+
+static unsigned char READ_UART_MSR(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char data;
+
+
+	if (sp->port.iobase)
+	{
+		data = inb(sp->port.iobase + UART_MSR);
+		return data;
+	}
+	return 0;
+}
+
+
+static void WRITE_UART_DLL(struct sunix_sdc_uart_channel *sp, int data)
+{
+    if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_DLL);
+    }
+}
+
+
+static void WRITE_UART_DLM(struct sunix_sdc_uart_channel *sp, int data)
+{
+	if (sp->port.iobase)
+	{
+		outb(data, sp->port.iobase + UART_DLM);
+	}
+}
+
+
+static _INLINE_ void snx_ser_handle_cts_change(struct snx_ser_port *port, unsigned int status)
+{
+	struct snx_ser_info *info = port->info;
+	struct tty_struct *tty = info->tty;
+
+
+	port->icount.cts++;
+
+	if (info->flags & SNX_UIF_CTS_FLOW)
+	{
+		if (tty->hw_stopped)
+		{
+			if (status)
+			{
+				tty->hw_stopped = 0;
+				sunix_ser_start_tx(port, 0);
+				snx_ser_write_wakeup(port);
+			}
+		}
+		else
+		{
+			if (!status)
+			{
+				tty->hw_stopped = 1;
+				sunix_ser_stop_tx(port, 0);
+			}
+		}
+	}
+}
+
+
+static _INLINE_ void snx_ser_update_mctrl(struct snx_ser_port *port, unsigned int set, unsigned int clear)
+{
+	unsigned long flags;
+	unsigned int old;
+
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	old = port->mctrl;
+	port->mctrl = (old & ~clear) | set;
+
+	if (old != port->mctrl)
+	{
+		sunix_ser_set_mctrl(port, port->mctrl);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+#define snx_set_mctrl(port, set)		snx_ser_update_mctrl(port, set, 0)
+#define snx_clear_mctrl(port, clear)	snx_ser_update_mctrl(port, 0, clear)
+
+
+static void snx_ser_write_wakeup(struct snx_ser_port *port)
+{
+	struct snx_ser_info *info = port->info;
+	tasklet_schedule(&info->tlet);
+}
+
+
+static void snx_ser_stop(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	unsigned long flags;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	spin_lock_irqsave(&port->lock, flags);
+	sunix_ser_stop_tx(port, 1);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static void __snx_ser_start(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = tty->driver_data;
+	struct snx_ser_port *port = state->port;
+
+
+	if (!snx_ser_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped)
+	{
+		sunix_ser_start_tx(port, 1);
+	}
+}
+
+
+static void snx_ser_start(struct tty_struct *tty)
+{
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	__snx_ser_start(tty);
+}
+
+
+static void snx_ser_tasklet_action(unsigned long data)
+{
+	struct snx_ser_state *state = (struct snx_ser_state *)data;
+	struct tty_struct *tty = NULL;
+
+
+	tty = state->info->tty;
+	if (tty)
+	{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc->ops->write_wakeup)
+		{
+			tty->ldisc->ops->write_wakeup(tty);
+		}
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30))
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.ops->write_wakeup)
+		{
+			tty->ldisc.ops->write_wakeup(tty);
+		}
+#else
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		{
+			tty->ldisc.write_wakeup(tty);
+		}
+#endif
+		wake_up_interruptible(&tty->write_wait);
+    }
+}
+
+
+int snx_ser_startup(struct snx_ser_state *state, int init_hw)
+{
+	struct snx_ser_info *info = state->info;
+	struct snx_ser_port *port = state->port;
+	unsigned long page;
+	int retval = 0;
+
+
+	if (info->flags & SNX_UIF_INITIALIZED)
+	{
+		return 0;
+	}
+
+
+	if (info->tty)
+	{
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+    }
+
+
+	if (port->type == PORT_UNKNOWN)
+	{
+		return 0;
+	}
+
+
+	if (!info->xmit.buf)
+	{
+		page = get_zeroed_page(GFP_KERNEL);
+
+		if (!page)
+		{
+			return -ENOMEM;
+		}
+
+		info->xmit.buf = (unsigned char *) page;
+
+		info->tmpbuf = info->xmit.buf + SNX_UART_XMIT_SIZE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
+		sema_init(&info->tmpbuf_sem, 1);
+#else
+		init_MUTEX(&info->tmpbuf_sem);
+#endif
+		snx_ser_circ_clear(&info->xmit);
+    }
+
+	retval = sunix_ser_startup(port);
+
+	if (retval == 0)
+	{
+		if (init_hw)
+		{
+			snx_ser_change_speed(state, NULL);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+			if (info->tty->termios.c_cflag & CBAUD)
+			{
+				snx_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+			}
+#else
+			if (info->tty->termios->c_cflag & CBAUD)
+			{
+				snx_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+			}
+#endif
+		}
+
+
+		info->flags |= SNX_UIF_INITIALIZED;
+
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+
+
+    if (retval && capable(CAP_SYS_ADMIN))
+	{
+		retval = 0;
+	}
+
+	return retval;
+}
+
+
+static void snx_ser_shutdown(struct snx_ser_state *state)
+{
+	struct snx_ser_info *info = state->info;
+	struct snx_ser_port *port = state->port;
+
+
+	if (!(info->flags & SNX_UIF_INITIALIZED))
+	{
+		return;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+    if (!info->tty || (info->tty->termios.c_cflag & HUPCL))
+	{
+		snx_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+	}
+#else
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+	{
+		snx_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+	}
+#endif
+
+	wake_up_interruptible(&info->delta_msr_wait);
+
+	sunix_ser_shutdown(port);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	synchronize_irq(port->irq);
+#endif
+	if (info->xmit.buf)
+	{
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
+		info->tmpbuf = NULL;
+	}
+
+	tasklet_kill(&info->tlet);
+
+	if (info->tty)
+	{
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+
+	info->flags &= ~SNX_UIF_INITIALIZED;
+}
+
+
+static _INLINE_ void __snx_ser_put_char(struct snx_ser_port *port, struct circ_buf *circ, unsigned char c)
+{
+	unsigned long flags;
+
+
+	if (!circ->buf)
+	{
+		return;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (snx_ser_circ_chars_free(circ) != 0)
+	{
+		circ->buf[circ->head] = c;
+		circ->head = (circ->head + 1) & (SNX_UART_XMIT_SIZE - 1);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26))
+static int  snx_ser_put_char(struct tty_struct *tty, unsigned char ch)
+#else
+static void snx_ser_put_char(struct tty_struct *tty, unsigned char ch)
+#endif
+{
+	struct snx_ser_state *state = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26))
+		return 0;
+#else
+		return;
+#endif
+	}
+
+	state = tty->driver_data;
+	__snx_ser_put_char(state->port, &state->info->xmit, ch);
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26))
+	return 0;
+#endif
+}
+
+
+static void snx_ser_flush_chars(struct tty_struct *tty)
+{
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	snx_ser_start(tty);
+}
+
+
+static int snx_ser_chars_in_buffer(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+	state = tty->driver_data;
+
+	return snx_ser_circ_chars_pending(&state->info->xmit);
+}
+
+
+static void snx_ser_flush_buffer(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	unsigned long flags;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	if (!state || !state->info)
+	{
+		return;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+	snx_ser_circ_clear(&state->info->xmit);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	wake_up_interruptible(&tty->write_wait);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc->ops->write_wakeup)
+	{
+		(tty->ldisc->ops->write_wakeup)(tty);
+	}
+
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30))
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.ops->write_wakeup)
+	{
+		(tty->ldisc.ops->write_wakeup)(tty);
+	}
+
+#else
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+	{
+		(tty->ldisc.write_wakeup)(tty);
+	}
+#endif
+}
+
+
+static void snx_ser_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	unsigned long flags;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+	port->x_char = ch;
+
+	if (ch)
+	{
+		spin_lock_irqsave(&port->lock, flags);
+		sunix_ser_start_tx(port, 0);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+
+
+static void snx_ser_throttle(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	port->ldisc_stop_rx = 1;
+
+	if (I_IXOFF(tty))
+	{
+		snx_ser_send_xchar(tty, STOP_CHAR(tty));
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	if (tty->termios.c_cflag & CRTSCTS)
+	{
+		snx_clear_mctrl(state->port, TIOCM_RTS);
+	}
+#else
+	if (tty->termios->c_cflag & CRTSCTS)
+	{
+		snx_clear_mctrl(state->port, TIOCM_RTS);
+	}
+#endif
+}
+
+
+static void snx_ser_unthrottle(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	port->ldisc_stop_rx = 0;
+
+	if (I_IXOFF(tty))
+	{
+		if (port->x_char)
+		{
+			port->x_char = 0;
+		}
+		else
+		{
+			snx_ser_send_xchar(tty, START_CHAR(tty));
+		}
+	}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	if (tty->termios.c_cflag & CRTSCTS)
+	{
+		snx_set_mctrl(port, TIOCM_RTS);
+	}
+#else
+	if (tty->termios->c_cflag & CRTSCTS)
+	{
+		snx_set_mctrl(port, TIOCM_RTS);
+	}
+#endif
+}
+
+
+static int snx_ser_get_info(struct snx_ser_state *state, struct serial_struct *retinfo)
+{
+	struct snx_ser_port *port = state->port;
+	struct serial_struct tmp;
+
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type            = port->type;
+	tmp.line            = port->line;
+	tmp.port            = port->iobase;
+
+    if (SNX_HIGH_BITS_OFFSET)
+	{
+		tmp.port_high = (long) port->iobase >> SNX_HIGH_BITS_OFFSET;
+	}
+
+	tmp.irq             = port->irq;
+	tmp.flags           = port->flags;
+	tmp.xmit_fifo_size  = port->fifosize;
+	tmp.baud_base       = port->uartclk / 16;
+	tmp.close_delay     = state->close_delay;
+	tmp.closing_wait    = state->closing_wait;
+
+	tmp.custom_divisor  = port->custom_divisor;
+	tmp.io_type         = port->iotype;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+	{
+		return -EFAULT;
+	}
+	return 0;
+}
+
+
+static int snx_ser_set_info(struct snx_ser_state *state, struct serial_struct *newinfo)
+{
+	struct serial_struct new_serial;
+	struct snx_ser_port *port = state->port;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+	struct tty_port		*tport = &state->tport;
+#endif
+	unsigned long new_port;
+	unsigned int change_irq;
+	unsigned int change_port;
+	unsigned int old_custom_divisor;
+	unsigned int closing_wait;
+	unsigned int close_delay;
+	unsigned int old_flags;
+	unsigned int new_flags;
+	int retval = 0;
+
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+	{
+		return -EFAULT;
+	}
+
+	new_port = new_serial.port;
+
+	if (SNX_HIGH_BITS_OFFSET)
+	{
+		new_port += (unsigned long) new_serial.port_high << SNX_HIGH_BITS_OFFSET;
+    }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	new_serial.irq = irq_canonicalize(new_serial.irq);
+#endif
+
+	close_delay = new_serial.close_delay;
+	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?	SNX_USF_CLOSING_WAIT_NONE : new_serial.closing_wait;
+
+	down(&state->sem);
+
+	change_irq  = new_serial.irq != port->irq;
+
+	change_port =   new_port != port->iobase ||
+					new_serial.io_type != port->iotype ||
+					new_serial.type != port->type;
+
+	old_flags = port->flags;
+	new_flags = new_serial.flags;
+	old_custom_divisor = port->custom_divisor;
+
+	if (!capable(CAP_SYS_ADMIN))
+	{
+		retval = -EPERM;
+		if (change_irq ||
+			change_port ||
+			(new_serial.baud_base != port->uartclk / 16) ||
+			(close_delay != state->close_delay) ||
+			(closing_wait != state->closing_wait) ||
+			(new_serial.xmit_fifo_size != port->fifosize) ||
+			(((new_flags ^ old_flags) & ~SNX_UPF_USR_MASK) != 0))
+		{
+			goto exit;
+		}
+
+		port->flags = ((port->flags & ~SNX_UPF_USR_MASK) | (new_flags & SNX_UPF_USR_MASK));
+		port->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (change_port || change_irq)
+	{
+		retval = -EBUSY;
+
+		if (sunix_ser_users(state) > 1)
+		{
+			goto exit;
+		}
+
+		snx_ser_shutdown(state);
+	}
+
+	if (change_port)
+	{
+		unsigned int old_type;
+		old_type = port->type;
+
+		if (old_type != PORT_UNKNOWN)
+		{
+			sunix_ser_release_io(port);
+		}
+
+		port->iobase = new_port;
+		port->type = new_serial.type;
+		port->iotype = new_serial.io_type;
+
+		retval = 0;
+	}
+
+	port->irq              = new_serial.irq;
+	port->uartclk          = new_serial.baud_base * 16;
+	port->flags            = ((port->flags & ~SNX_UPF_CHANGE_MASK) | (new_flags & SNX_UPF_CHANGE_MASK));
+	port->custom_divisor   = new_serial.custom_divisor;
+	state->close_delay     = close_delay;
+	state->closing_wait    = closing_wait;
+	port->fifosize         = new_serial.xmit_fifo_size;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+	tport->low_latency = (port->flags & SNX_UPF_LOW_LATENCY) ? 1 : 0;
+#else
+	if (state->info->tty)
+	{
+		state->info->tty->low_latency = (port->flags & SNX_UPF_LOW_LATENCY) ? 1 : 0;
+	}
+#endif
+
+
+check_and_exit:
+	retval = 0;
+	if (port->type == PORT_UNKNOWN)
+	{
+		goto exit;
+	}
+
+	if (state->info->flags & SNX_UIF_INITIALIZED)
+	{
+		if (((old_flags ^ port->flags) & SNX_UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor)
+		{
+			if (port->flags & SNX_UPF_SPD_MASK)
+			{
+				printk("SUNIX: %s sets custom speed on ttySDC%d. This is deprecated.\n", current->comm, port->line);
+			}
+			snx_ser_change_speed(state, NULL);
+		}
+	}
+	else
+	{
+		retval = snx_ser_startup(state, 1);
+	}
+exit:
+
+	up(&state->sem);
+
+	return retval;
+}
+
+
+static int snx_ser_write_room(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+	int status = 0;
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+    state = tty->driver_data;
+
+    status = snx_ser_circ_chars_free(&state->info->xmit);
+
+    return status;
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10))
+static int snx_ser_write(struct tty_struct *tty, const unsigned char *buf, int count)
+#else
+static int snx_ser_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+#endif
+{
+	struct snx_ser_state *state = tty->driver_data;
+	struct circ_buf *circ = NULL;
+	struct snx_ser_port *port = state->port;
+	unsigned long flags;
+	int c;
+	int ret = 0;
+
+
+	if (!state || !state->info)
+	{
+		return -EL3HLT;
+	}
+
+	circ = &state->info->xmit;
+
+	if (!circ->buf)
+	{
+		return 0;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (1)
+	{
+		c = CIRC_SPACE_TO_END(circ->head, circ->tail, SNX_UART_XMIT_SIZE);
+		if (count < c)
+		{
+			c = count;
+		}
+
+		if (c <= 0)
+		{
+			break;
+		}
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 9))
+	memcpy(circ->buf + circ->head, buf, c);
+#else
+		if (from_user)
+		{
+			if (copy_from_user((circ->buf + circ->head), buf, c) == c)
+			{
+				ret = -EFAULT;
+				break;
+			}
+		}
+		else
+		{
+			memcpy(circ->buf + circ->head, buf, c);
+		}
+#endif
+
+
+		circ->head = (circ->head + c) & (SNX_UART_XMIT_SIZE - 1);
+		buf += c;
+		count -= c;
+		ret += c;
+    }
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	snx_ser_start(tty);
+
+	return ret;
+}
+
+
+static int snx_ser_get_lsr_info(struct snx_ser_state *state, unsigned int *value)
+{
+	struct snx_ser_port *port = state->port;
+	unsigned int result = 0;
+
+
+	result = sunix_ser_tx_empty(port);
+
+	if ((port->x_char) ||
+		((snx_ser_circ_chars_pending(&state->info->xmit) > 0) &&
+		!state->info->tty->stopped && !state->info->tty->hw_stopped))
+	{
+		result &= ~TIOCSER_TEMT;
+	}
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 18))
+	if (copy_to_user(value, &result, sizeof(int)))
+	{
+		return -EFAULT;
+	}
+
+	return 0;
+#else
+	return put_user(result, value);
+#endif
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
+static int snx_ser_tiocmget(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int result = -EIO;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	down(&state->sem);
+
+	if (!(tty->flags & (1 << TTY_IO_ERROR)))
+	{
+		result = port->mctrl;
+		result |= sunix_ser_get_mctrl(port);
+	}
+
+	up(&state->sem);
+
+	return result;
+}
+
+
+static int snx_ser_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int ret = -EIO;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	down(&state->sem);
+
+	if (!(tty->flags & (1 << TTY_IO_ERROR)))
+	{
+		snx_ser_update_mctrl(port, set, clear);
+		ret = 0;
+	}
+
+	up(&state->sem);
+
+	return ret;
+}
+
+
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+static int snx_ser_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int result = -EIO;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	down(&state->sem);
+
+	if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR)))
+	{
+		result = port->mctrl;
+		result |= sunix_ser_get_mctrl(port);
+	}
+
+	up(&state->sem);
+
+	return result;
+}
+
+
+static int snx_ser_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int ret = -EIO;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return 0;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	down(&state->sem);
+
+	if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR)))
+	{
+		snx_ser_update_mctrl(port, set, clear);
+		ret = 0;
+	}
+
+	up(&state->sem);
+
+	return ret;
+}
+
+
+#else
+static int snx_ser_get_modem_info(struct snx_ser_state *state, unsigned int *value)
+{
+	struct snx_ser_port *port = NULL;
+	int line;
+	unsigned int result;
+
+
+	if (!state)
+	{
+		return -EIO;
+	}
+
+	port = state->port;
+
+	if (!port)
+	{
+		return -EIO;
+	}
+
+	line = port->line;
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return -EIO;
+	}
+
+	result = port->mctrl;
+	result |= sunix_ser_get_mctrl(port);
+
+	put_user(result, (unsigned long *)value);
+
+	return 0;
+}
+
+
+static int snx_ser_set_modem_info(struct snx_ser_state *state, unsigned int cmd, unsigned int *value)
+{
+	struct snx_ser_port *port = NULL;
+	int line;
+	unsigned int set = 0;
+	unsigned int clr = 0;
+	unsigned int arg;
+
+
+	if (!state)
+	{
+		return -EIO;
+	}
+
+	port = state->port;
+
+	if (!port)
+	{
+		return -EIO;
+	}
+
+	line = port->line;
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return -EIO;
+	}
+
+	get_user(arg, (unsigned long *)value);
+
+	switch (cmd)
+	{
+		case TIOCMBIS:
+		{
+			if (arg & TIOCM_RTS)
+			{
+				set |= TIOCM_RTS;
+			}
+
+			if (arg & TIOCM_DTR)
+			{
+				set |= TIOCM_DTR;
+			}
+
+			if (arg & TIOCM_LOOP)
+			{
+				set |= TIOCM_LOOP;
+			}
+			break;
+		}
+
+		case TIOCMBIC:
+		{
+			if (arg & TIOCM_RTS)
+			{
+				clr |= TIOCM_RTS;
+			}
+
+			if (arg & TIOCM_DTR)
+			{
+				clr |= TIOCM_DTR;
+			}
+
+			if (arg & TIOCM_LOOP)
+			{
+				clr |= TIOCM_LOOP;
+			}
+			break;
+		}
+
+		case TIOCMSET:
+		{
+			if (arg & TIOCM_RTS)
+			{
+				set |= TIOCM_RTS;
+			}
+			else
+			{
+				clr |= TIOCM_RTS;
+			}
+
+			if (arg & TIOCM_DTR)
+			{
+				set |= TIOCM_DTR;
+			}
+			else
+			{
+				clr |= TIOCM_DTR;
+			}
+
+			if (arg & TIOCM_LOOP)
+			{
+				set |= TIOCM_LOOP;
+			}
+			else
+			{
+				clr |= TIOCM_LOOP;
+			}
+			break;
+		}
+
+		default:
+		{
+			return -EINVAL;
+		}
+	}
+
+	snx_ser_update_mctrl(port, set, clr);
+
+	return 0;
+}
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+static int  snx_ser_break_ctl(struct tty_struct *tty, int break_state)
+#else
+static void snx_ser_break_ctl(struct tty_struct *tty, int break_state)
+#endif
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+		return 0;
+#else
+		return;
+#endif
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	down(&state->sem);
+
+	if (port->type != PORT_UNKNOWN)
+	{
+		sunix_ser_break_ctl(port, break_state);
+	}
+
+	up(&state->sem);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	return 0;
+#endif
+}
+
+
+static int snx_ser_wait_modem_status(struct snx_ser_state *state, unsigned long arg)
+{
+	struct snx_ser_port *port = state->port;
+	DECLARE_WAITQUEUE(wait, current);
+	struct snx_ser_icount cprev;
+	struct snx_ser_icount cnow;
+	int ret = 0;
+
+
+	spin_lock_irq(&port->lock);
+	memcpy(&cprev, &port->icount, sizeof(struct snx_ser_icount));
+
+	sunix_ser_enable_ms(port);
+
+	spin_unlock_irq(&port->lock);
+
+	add_wait_queue(&state->info->delta_msr_wait, &wait);
+
+	for (;;)
+	{
+		spin_lock_irq(&port->lock);
+		memcpy(&cnow, &port->icount, sizeof(struct snx_ser_icount));
+		spin_unlock_irq(&port->lock);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+			((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+			((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+			((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)))
+		{
+			ret = 0;
+			break;
+		}
+
+		schedule();
+
+		if (signal_pending(current))
+		{
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		cprev = cnow;
+	}
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&state->info->delta_msr_wait, &wait);
+
+	return ret;
+}
+
+
+static int snx_ser_get_count(struct snx_ser_state *state, struct serial_icounter_struct *icnt)
+{
+    struct serial_icounter_struct icount;
+    struct snx_ser_icount cnow;
+    struct snx_ser_port *port = state->port;
+
+
+    spin_lock_irq(&port->lock);
+    memcpy(&cnow, &port->icount, sizeof(struct snx_ser_icount));
+    spin_unlock_irq(&port->lock);
+
+    icount.cts         = cnow.cts;
+    icount.dsr         = cnow.dsr;
+    icount.rng         = cnow.rng;
+    icount.dcd         = cnow.dcd;
+    icount.rx          = cnow.rx;
+    icount.tx          = cnow.tx;
+    icount.frame       = cnow.frame;
+    icount.overrun     = cnow.overrun;
+    icount.parity      = cnow.parity;
+    icount.brk         = cnow.brk;
+    icount.buf_overrun = cnow.buf_overrun;
+
+    return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
+static int snx_ser_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+#else
+static int snx_ser_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+	struct snx_ser_state *state = NULL;
+	int ret = -ENOIOCTLCMD;
+	int line = SNX_SER_DEVNUM(tty);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+	int status = 0;
+#endif
+
+
+	if (line < SUNIX_SDC_UART_MAX)
+	{
+		state = tty->driver_data;
+	}
+
+
+	switch (cmd)
+	{
+		case TIOCGSERIAL:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				ret = snx_ser_get_info(state, (struct serial_struct *)arg);
+			}
+			break;
+		}
+
+
+		case TIOCSSERIAL:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				state->port->setserial_flag = SNX_SER_BAUD_SETSERIAL;
+				ret = snx_ser_set_info(state, (struct serial_struct *)arg);
+			}
+			break;
+		}
+
+
+		case TCSETS:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				state->port->flags &= ~(SNX_UPF_SPD_HI | SNX_UPF_SPD_VHI | SNX_UPF_SPD_SHI | SNX_UPF_SPD_WARP);
+				state->port->setserial_flag = SNX_SER_BAUD_NOTSETSER;
+				snx_ser_update_termios(state);
+			}
+			break;
+		}
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+		case TIOCMGET:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				ret = verify_area(VERIFY_WRITE, (void *)arg,	sizeof(unsigned int));
+
+				if (ret)
+				{
+					return ret;
+				}
+
+				status = snx_ser_get_modem_info(state, (unsigned int *)arg);
+				return status;
+			}
+			break;
+		}
+
+
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				status = snx_ser_set_modem_info(state, cmd, (unsigned int *)arg);
+				return status;
+			}
+			break;
+		}
+#endif
+
+
+		case TIOCSERGWILD:
+		case TIOCSERSWILD:
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				ret = 0;
+			}
+			break;
+		}
+
+		case SNX_SDC_UART_GET_INFO: 
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				struct snx_ser_port *port = NULL;
+				struct snx_sdc_uart_info info;
+
+				port = state->port;
+				if (port != NULL)
+				{
+					memset(&info, 0, sizeof(struct snx_sdc_uart_info));
+
+					memcpy(info.model_name, port->model_name, sizeof(port->model_name));
+					info.bus_number = port->bus_number;
+					info.dev_number = port->dev_number;
+					info.line = port->line;
+					info.iobase = port->iobase;
+					info.irq = port->irq;
+
+					info.version = port->cib_info.version;
+					info.tx_fifo_size = port->cib_info.uart_tx_fifo_size;
+					info.rx_fifo_size = port->cib_info.uart_rx_fifo_size;
+					info.significand_of_clock = port->cib_info.uart_significand_of_clock;
+					info.exponent_of_clock = port->cib_info.uart_exponent_of_clock;
+					info.RS232_cap = port->cib_info.uart_RS232_cap;
+					info.RS422_cap = port->cib_info.uart_RS422_cap;
+					info.RS485_cap = port->cib_info.uart_RS485_cap;
+					info.AHDC_cap = port->cib_info.uart_AHDC_cap;
+					info.CS_cap = port->cib_info.uart_CS_cap;
+					info.auto_RS422485_cap = port->cib_info.uart_auto_RS422485_cap;
+					info.RS422_termination_cap = port->cib_info.uart_RS422_termination_cap;
+					info.RS485_termination_cap = port->cib_info.uart_RS485_termination_cap;
+					info.RI_5V_cap = port->cib_info.uart_RI_5V_cap;
+					info.RI_12V_cap = port->cib_info.uart_RI_12V_cap;
+					info.DCD_5V_cap = port->cib_info.uart_DCD_5V_cap;
+					info.DCD_12V_cap = port->cib_info.uart_DCD_12V_cap;
+
+					if (copy_to_user((void *)arg, &info, sizeof(struct snx_sdc_uart_info)))
+					{
+						ret = -EFAULT;
+					}
+					else
+					{
+						ret = 0;
+					}
+				}
+			}
+			break;
+		}
+
+		case SNX_SDC_UART_GET_ADDITIONAL_REG_0E: 
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				struct snx_ser_port *port = NULL;
+				unsigned char data;
+
+				port = state->port;
+				if (port != NULL)
+				{
+					data = 	inb(port->iobase + 0x0e);
+					if (copy_to_user((void *)arg, &data, sizeof(unsigned char)))
+					{
+						ret = -EFAULT;
+					}
+					else
+					{
+						ret = 0;
+					}
+				}
+			}
+
+			break;
+		}
+
+		case SNX_SDC_UART_SET_ADDITIONAL_REG_0E: 
+		{
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				struct snx_ser_port *port = NULL;
+				unsigned char data = 0x00;
+
+				port = state->port;
+				if (port != NULL)
+				{
+					if (copy_from_user(&data, (void *)arg, sizeof(unsigned char)))
+					{
+						ret = -EFAULT;
+					}
+					else
+					{
+						outb(data, port->iobase + 0x0e);
+						ret = 0;
+					}
+				}
+			}
+
+			break;
+		}
+
+    }
+
+    if (ret != -ENOIOCTLCMD)
+	{
+		goto out;
+    }
+
+    if (tty->flags & (1 << TTY_IO_ERROR))
+	{
+		ret = -EIO;
+		goto out;
+	}
+
+	switch (cmd)
+	{
+		case TIOCMIWAIT:
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				ret = snx_ser_wait_modem_status(state, arg);
+			}
+			break;
+
+		case TIOCGICOUNT:
+			if (line < SUNIX_SDC_UART_MAX)
+			{
+				ret = snx_ser_get_count(state, (struct serial_icounter_struct *)arg);
+			}
+			break;
+	}
+
+
+	if (ret != -ENOIOCTLCMD)
+	{
+		goto out;
+	}
+
+
+    if (line < SUNIX_SDC_UART_MAX)
+	{
+		down(&state->sem);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39))
+		if (tty_hung_up_p(filp))
+		{
+			ret = -EIO;
+			goto out_up;
+		}
+#endif
+
+		switch (cmd)
+		{
+			case TIOCSERGETLSR:
+				ret = snx_ser_get_lsr_info(state, (unsigned int *)arg);
+				break;
+
+
+			default:
+				break;
+		}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39))
+out_up:
+#endif
+		up(&state->sem);
+	}
+
+out:
+	return ret;
+}
+
+
+static void snx_ser_hangup(struct tty_struct *tty)
+{
+	struct snx_ser_state *state = NULL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct tty_port *tport = &state->tport;
+#endif
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	tport = &state->tport;
+#endif
+
+	down(&state->sem);
+
+	if (state->info && state->info->flags & SNX_UIF_NORMAL_ACTIVE)
+	{
+		snx_ser_flush_buffer(tty);
+		snx_ser_shutdown(state);
+		state->count = 0;
+		state->info->flags &= ~SNX_UIF_NORMAL_ACTIVE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		tty_port_tty_set(tport, NULL);
+#endif
+
+		state->info->tty = NULL;
+		wake_up_interruptible(&state->info->open_wait);
+		wake_up_interruptible(&state->info->delta_msr_wait);
+	}
+
+	up(&state->sem);
+}
+
+
+unsigned int snx_ser_get_divisor(struct snx_ser_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+
+	if (baud == 38400 && (port->flags & SNX_UPF_SPD_MASK) == SNX_UPF_SPD_CUST)
+	{
+		quot = port->custom_divisor;
+	}
+	else
+	{
+		quot = port->uartclk / (16 * baud);
+	}
+
+	return quot;
+}
+
+
+unsigned int snx_ser_get_baud_rate(struct snx_ser_port *port, struct SNXTERMIOS *termios, struct SNXTERMIOS *old, unsigned int min, unsigned int max)
+{
+	unsigned int try;
+	unsigned int baud;
+	unsigned int altbaud = 0;
+	unsigned int flags = port->flags & SNX_UPF_SPD_MASK;
+
+
+	for (try = 0; try < 2; try++)
+	{
+		if ((port->setserial_flag == SNX_SER_BAUD_SETSERIAL) || (port->flags & SNX_UPF_SPD_MASK))
+		{
+			altbaud = 38400;
+
+			if (flags == SNX_UPF_SPD_HI)
+			{
+				altbaud = 57600;
+			}
+
+			if (flags == SNX_UPF_SPD_VHI)
+			{
+				altbaud = 115200;
+			}
+
+			if (flags == SNX_UPF_SPD_SHI)
+			{
+				altbaud = 230400;
+			}
+
+			if (flags == SNX_UPF_SPD_WARP)
+			{
+				altbaud = 460800;
+			}
+
+			baud = altbaud;
+		}
+		else
+		{
+			switch (termios->c_cflag & (CBAUD | CBAUDEX))
+			{
+				case B921600:
+					baud = 921600;
+					break;
+
+				case B460800:
+					baud = 460800;
+					break;
+
+				case B230400:
+					baud = 230400;
+					break;
+
+				case B115200:
+					baud = 115200;
+					break;
+
+				case B57600:
+					baud = 57600;
+					break;
+
+				case B38400:
+					baud = 38400;
+					break;
+
+				case B19200:
+					baud = 19200;
+					break;
+
+				case B9600:
+					baud = 9600;
+					break;
+
+				case B4800:
+					baud = 4800;
+					break;
+
+				case B2400:
+					baud = 2400;
+					break;
+
+				case B1800:
+					baud = 1800;
+					break;
+
+				case B1200:
+					baud = 1200;
+					break;
+
+				case B600:
+					baud = 600;
+					break;
+
+				case B300:
+					baud = 300;
+					break;
+
+				case B200:
+					baud = 200;
+					break;
+
+				case B150:
+					baud = 150;
+					break;
+
+				case B134:
+					baud = 134;
+					break;
+
+				case B110:
+					baud = 110;
+					break;
+
+				case B75:
+					baud = 75;
+					break;
+
+				case B50:
+					baud = 50;
+					break;
+
+				default:
+					baud = 9600;
+					break;
+			}
+		}
+
+		if (baud == 0)
+		{
+			baud = 9600;
+		}
+
+		if (baud >= min && baud <= max)
+		{
+			return baud;
+		}
+
+		termios->c_cflag &= ~CBAUD;
+
+		if (old)
+		{
+			termios->c_cflag |= old->c_cflag & CBAUD;
+			old = NULL;
+			continue;
+		}
+
+		termios->c_cflag |= B9600;
+	}
+
+	return 0;
+}
+
+
+extern void snx_ser_change_speed(struct snx_ser_state *state, struct SNXTERMIOS *old_termios)
+{
+	struct tty_struct *tty = state->info->tty;
+	struct snx_ser_port *port = state->port;
+	struct SNXTERMIOS *termios;
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	if (!tty || port->type == PORT_UNKNOWN)
+	{
+		return;
+	}
+#else
+	if (!tty || !tty->termios || port->type == PORT_UNKNOWN)
+	{
+		return;
+	}
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	termios = &tty->termios;
+#else
+	termios = tty->termios;
+#endif
+
+	if (termios->c_cflag & CRTSCTS)
+	{
+		state->info->flags |= SNX_UIF_CTS_FLOW;
+    }
+	else
+	{
+		state->info->flags &= ~SNX_UIF_CTS_FLOW;
+    }
+
+	if (termios->c_cflag & CLOCAL)
+	{
+		state->info->flags &= ~SNX_UIF_CHECK_CD;
+	}
+	else
+	{
+		state->info->flags |= SNX_UIF_CHECK_CD;
+	}
+
+	sunix_ser_set_termios(port, termios, old_termios);
+}
+
+
+static void snx_ser_set_termios(struct tty_struct *tty, struct SNXTERMIOS *old_termios)
+{
+	struct snx_ser_state *state = NULL;
+	unsigned long flags;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	unsigned int cflag = tty->termios.c_cflag;
+#else
+	unsigned int cflag = tty->termios->c_cflag;
+#endif
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+
+#define RELEVANT_IFLAG(iflag)	((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0)
+	{
+		return;
+	}
+#else
+	if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
+	{
+		return;
+	}
+#endif
+
+	snx_ser_change_speed(state, old_termios);
+
+	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+	{
+		snx_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
+	}
+
+	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD))
+	{
+		unsigned int mask = TIOCM_DTR;
+		if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
+		{
+			mask |= TIOCM_RTS;
+		}
+
+		snx_set_mctrl(state->port, mask);
+	}
+
+	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS))
+	{
+		spin_lock_irqsave(&state->port->lock, flags);
+		tty->hw_stopped = 0;
+		__snx_ser_start(tty);
+		spin_unlock_irqrestore(&state->port->lock, flags);
+	}
+}
+
+
+void snx_ser_update_termios(struct snx_ser_state *state)
+{
+	struct tty_struct *tty = state->info->tty;
+	struct snx_ser_port *port = state->port;
+
+
+	if (!(tty->flags & (1 << TTY_IO_ERROR)))
+	{
+		snx_ser_change_speed(state, NULL);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		if (tty->termios.c_cflag & CBAUD)
+		{
+			snx_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+		}
+#else
+		if (tty->termios->c_cflag & CBAUD)
+		{
+			snx_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+		}
+#endif
+	}
+}
+
+
+static void snx_ser_update_timeout(struct snx_ser_port *port, unsigned int cflag, unsigned int baud)
+{
+	unsigned int bits;
+
+
+	switch (cflag & CSIZE)
+	{
+		case CS5:
+			bits = 7;
+			break;
+
+		case CS6:
+			bits = 8;
+			break;
+
+		case CS7:
+			bits = 9;
+			break;
+
+		default:
+			bits = 10;
+			break;
+	}
+
+	if (cflag & CSTOPB)
+	{
+		bits++;
+	}
+
+	if (cflag & PARENB)
+	{
+		bits++;
+	}
+
+	bits = bits * port->fifosize;
+
+	port->timeout = (HZ * bits) / baud + HZ/50;
+}
+
+
+static struct snx_ser_state *snx_ser_get(struct snx_ser_driver *drv, int line)
+{
+	struct snx_ser_state *state = NULL;
+
+
+	down(&ser_port_sem);
+
+	state = drv->state + line;
+
+	if (down_interruptible(&state->sem))
+	{
+		state = ERR_PTR(-ERESTARTSYS);
+		goto out;
+    }
+
+	state->count++;
+
+	if (!state->port)
+	{
+		state->count--;
+		up(&state->sem);
+		state = ERR_PTR(-ENXIO);
+		goto out;
+	}
+
+	if (!state->port->iobase)
+	{
+		state->count--;
+		up(&state->sem);
+		state = ERR_PTR(-ENXIO);
+		goto out;
+	}
+
+	if (!state->info)
+	{
+		state->info = kmalloc(sizeof(struct snx_ser_info), GFP_KERNEL);
+
+		if (state->info)
+		{
+			memset(state->info, 0, sizeof(struct snx_ser_info));
+			init_waitqueue_head(&state->info->open_wait);
+			init_waitqueue_head(&state->info->delta_msr_wait);
+
+			state->port->info = state->info;
+
+			tasklet_init(&state->info->tlet, snx_ser_tasklet_action, (unsigned long)state);
+		}
+		else
+		{
+			state->count--;
+			up(&state->sem);
+			state = ERR_PTR(-ENOMEM);
+		}
+	}
+
+out:
+	up(&ser_port_sem);
+
+	return state;
+}
+
+
+static int snx_ser_block_til_ready(struct file *filp, struct snx_ser_state *state)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct snx_ser_info *info = state->info;
+	struct snx_ser_port *port = state->port;
+
+
+	info->blocked_open++;
+	state->count--;
+
+	add_wait_queue(&info->open_wait, &wait);
+
+	while (1)
+	{
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (tty_hung_up_p(filp) || info->tty == NULL)
+		{
+			break;
+		}
+
+		if (!(info->flags & SNX_UIF_INITIALIZED))
+		{
+			break;
+		}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		if ((filp->f_flags & O_NONBLOCK) ||
+			(info->tty->termios.c_cflag & CLOCAL) ||
+			(info->tty->flags & (1 << TTY_IO_ERROR)))
+		{
+			break;
+		}
+
+		if (info->tty->termios.c_cflag & CBAUD)
+		{
+			snx_set_mctrl(port, TIOCM_DTR);
+		}
+#else
+		if ((filp->f_flags & O_NONBLOCK) ||
+			(info->tty->termios->c_cflag & CLOCAL) ||
+			(info->tty->flags & (1 << TTY_IO_ERROR)))
+		{
+			break;
+		}
+
+		if (info->tty->termios->c_cflag & CBAUD)
+		{
+			snx_set_mctrl(port, TIOCM_DTR);
+		}
+#endif
+
+		if (sunix_ser_get_mctrl(port) & TIOCM_CAR)
+		{
+			break;
+		}
+
+		up(&state->sem);
+		schedule();
+		down(&state->sem);
+
+		if (signal_pending(current))
+		{
+			break;
+		}
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&info->open_wait, &wait);
+
+	state->count++;
+	info->blocked_open--;
+
+	if (signal_pending(current))
+	{
+		return -ERESTARTSYS;
+	}
+
+	if (!info->tty || tty_hung_up_p(filp))
+	{
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+
+static void snx_ser_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct snx_ser_state *state = NULL;
+	struct snx_ser_port *port = NULL;
+	unsigned long char_time;
+	unsigned long expire;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line >= SUNIX_SDC_UART_MAX)
+	{
+		return;
+	}
+
+	state = tty->driver_data;
+	port = state->port;
+
+	if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+	{
+		return;
+	}
+
+	char_time = (port->timeout - HZ/50) / port->fifosize;
+
+    char_time = char_time / 5;
+
+    if (char_time == 0)
+	{
+		char_time = 1;
+	}
+
+	if (timeout && timeout < char_time)
+	{
+		char_time = timeout;
+	}
+
+	if (timeout == 0 || timeout > 2 * port->timeout)
+	{
+		timeout = 2 * port->timeout;
+	}
+
+	expire = jiffies + timeout;
+
+	while (!sunix_ser_tx_empty(port))
+	{
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(char_time);
+
+		if (signal_pending(current))
+		{
+			break;
+		}
+
+		if (time_after(jiffies, expire))
+		{
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+}
+
+
+static int snx_ser_open(struct tty_struct *tty, struct file *filp)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	struct snx_ser_driver *drv = (struct snx_ser_driver *)tty->driver->driver_state;
+#else
+	struct snx_ser_driver *drv = (struct snx_ser_driver *)tty->driver.driver_state;
+#endif
+	struct snx_ser_state *state = NULL;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct tty_port *tport = NULL;
+#endif
+	int retval = 0;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line < SUNIX_SDC_UART_MAX)
+	{
+		retval = -ENODEV;
+
+		if (line >= SUNIX_SDC_UART_MAX)
+		{
+			goto fail;
+		}
+
+		state = snx_ser_get(drv, line);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		tport = &state->tport;
+#endif
+
+		if (IS_ERR(state))
+		{
+			retval = PTR_ERR(state);
+			goto fail;
+		}
+
+		if (!state)
+		{
+			goto fail;
+		}
+
+		state->port->suspended = 1;
+		tty->driver_data = state;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+		tport->low_latency = (state->port->flags & SNX_UPF_LOW_LATENCY) ? 1 : 0;
+#else
+		tty->low_latency = (state->port->flags & SNX_UPF_LOW_LATENCY) ? 1 : 0;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
+		tty->alt_speed = 0;
+#endif
+		state->info->tty = tty;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		tty_port_tty_set(tport, tty);
+#endif
+
+		if (tty_hung_up_p(filp))
+		{
+			retval = -EAGAIN;
+			state->count--;
+			up(&state->sem);
+			goto fail;
+		}
+
+		retval = snx_ser_startup(state, 0);
+
+		if (retval == 0)
+		{
+			retval = snx_ser_block_til_ready(filp, state);
+		}
+
+		up(&state->sem);
+
+		if (retval == 0 && !(state->info->flags & SNX_UIF_NORMAL_ACTIVE))
+		{
+			state->info->flags |= SNX_UIF_NORMAL_ACTIVE;
+
+			snx_ser_update_termios(state);
+		}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		try_module_get(THIS_MODULE);
+#else
+		MOD_INC_USE_COUNT;
+#endif
+
+	}
+	else
+	{
+	}
+
+fail:
+
+	return retval;
+}
+
+
+static void snx_ser_close(struct tty_struct *tty, struct file *filp)
+{
+	struct snx_ser_state *state = tty->driver_data;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct tty_port *tport;
+#endif
+	struct snx_ser_port *port = NULL;
+	int line = SNX_SER_DEVNUM(tty);
+
+
+	if (line < SUNIX_SDC_UART_MAX)
+	{
+		if (!state || !state->port)
+		{
+			return;
+		}
+
+		port = state->port;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		tport = &state->tport;
+#endif
+
+		down(&state->sem);
+
+		if (tty_hung_up_p(filp))
+		{
+			goto done;
+		}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		if ((tty->count == 1) && (state->count != 1))
+		{
+			printk("SUNIX: Bad serial port count; tty->count is 1, state->count is %d\n", state->count);
+			state->count = 1;
+		}
+#endif
+
+		if (--state->count < 0)
+		{
+			printk("SUNIX: Bad serial port count for ttySDC%d: %d\n", port->line, state->count);
+			state->count = 0;
+		}
+
+		if (state->count)
+		{
+			goto done;
+		}
+
+		tty->closing = 1;
+
+		port->suspended = 0;
+		if (state->closing_wait != SNX_USF_CLOSING_WAIT_NONE)
+		{
+			tty_wait_until_sent(tty, state->closing_wait);
+		}
+
+		if (state->info->flags & SNX_UIF_INITIALIZED)
+		{
+			unsigned long flags;
+
+			spin_lock_irqsave(&port->lock, flags);
+			sunix_ser_stop_rx(port);
+			spin_unlock_irqrestore(&port->lock, flags);
+
+			snx_ser_wait_until_sent(tty, port->timeout);
+		}
+
+		snx_ser_shutdown(state);
+		snx_ser_flush_buffer(tty);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
+		if (tty->ldisc->ops->flush_buffer)
+		{
+			tty->ldisc->ops->flush_buffer(tty);
+		}
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30))
+		if (tty->ldisc.ops->flush_buffer)
+		{
+			tty->ldisc.ops->flush_buffer(tty);
+		}
+#else
+		if (tty->ldisc.flush_buffer)
+		{
+			tty->ldisc.flush_buffer(tty);
+		}
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		tty_port_tty_set(tport, NULL);
+#endif
+
+		tty->closing = 0;
+		state->info->tty = NULL;
+
+		if (state->info->blocked_open)
+		{
+			if (state->close_delay)
+			{
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(state->close_delay);
+			}
+		}
+
+		state->info->flags &= ~SNX_UIF_NORMAL_ACTIVE;
+		wake_up_interruptible(&state->info->open_wait);
+
+done:
+		up(&state->sem);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		module_put(THIS_MODULE);
+#else
+		MOD_DEC_USE_COUNT;
+#endif
+	}
+	else
+	{
+	}
+}
+
+
+static void sunix_ser_set_mctrl(struct snx_ser_port *port, unsigned int mctrl)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+	unsigned char mcr = 0;
+
+
+	if (mctrl & TIOCM_RTS)
+	{
+		mcr |= UART_MCR_RTS;
+	}
+
+	if (mctrl & TIOCM_DTR)
+	{
+		mcr |= UART_MCR_DTR;
+	}
+
+	if (mctrl & TIOCM_OUT1)
+	{
+		mcr |= UART_MCR_OUT1;
+	}
+
+	if (mctrl & TIOCM_OUT2)
+	{
+		mcr |= UART_MCR_OUT2;
+	}
+
+	if (mctrl & TIOCM_LOOP)
+	{
+		mcr |= UART_MCR_LOOP;
+	}
+
+	mcr = (mcr & sp->mcr_mask) | sp->mcr_force | sp->mcr;
+
+	WRITE_UART_MCR(sp, mcr);
+}
+
+
+static unsigned int sunix_ser_tx_empty(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+
+	spin_lock_irqsave(&sp->port.lock, flags);
+	ret = READ_UART_LSR(sp) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&sp->port.lock, flags);
+
+	return ret;
+}
+
+
+static unsigned int sunix_ser_get_mctrl(struct snx_ser_port *port)
+{
+    struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+    unsigned long flags;
+    unsigned char status;
+    unsigned int ret;
+
+
+	ret = 0;
+
+	spin_lock_irqsave(&sp->port.lock, flags);
+	status = READ_UART_MSR(sp);
+	spin_unlock_irqrestore(&sp->port.lock, flags);
+
+	if (status & UART_MSR_DCD)
+	{
+		ret |= TIOCM_CAR;
+	}
+
+	if (status & UART_MSR_RI)
+	{
+		ret |= TIOCM_RNG;
+	}
+
+	if (status & UART_MSR_DSR)
+	{
+		ret |= TIOCM_DSR;
+	}
+
+	if (status & UART_MSR_CTS)
+	{
+		ret |= TIOCM_CTS;
+	}
+	return ret;
+}
+
+
+static void sunix_ser_stop_tx(struct snx_ser_port *port, unsigned int tty_stop)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	if (sp->ier & UART_IER_THRI)
+	{
+		sp->ier &= ~UART_IER_THRI;
+		WRITE_UART_IER(sp, sp->ier);
+	}
+}
+
+
+static void sunix_ser_start_tx(struct snx_ser_port *port, unsigned int tty_start)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	if (!(sp->ier & UART_IER_THRI))
+	{
+		sp->ier |= UART_IER_THRI;
+		WRITE_UART_IER(sp, sp->ier);
+	}
+}
+
+
+static void sunix_ser_stop_rx(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	sp->ier &= ~UART_IER_RLSI;
+	sp->port.read_status_mask &= ~UART_LSR_DR;
+	WRITE_UART_IER(sp, sp->ier);
+}
+
+
+static void sunix_ser_enable_ms(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	sp->ier |= UART_IER_MSI;
+	WRITE_UART_IER(sp, sp->ier);
+}
+
+
+static void sunix_ser_break_ctl(struct snx_ser_port *port, int break_state)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&sp->port.lock, flags);
+
+	if (break_state == -1)
+	{
+		sp->lcr |= UART_LCR_SBC;
+	}
+	else
+	{
+		sp->lcr &= ~UART_LCR_SBC;
+	}
+
+	WRITE_UART_LCR(sp, sp->lcr);
+	spin_unlock_irqrestore(&sp->port.lock, flags);
+}
+
+
+static int sunix_ser_startup(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	sp->capabilities = snx_uart_config[sp->port.type].flags;
+	sp->mcr = 0;
+
+	if (sp->capabilities & UART_CLEAR_FIFO)
+	{
+		WRITE_UART_FCR(sp, UART_FCR_ENABLE_FIFO);
+		WRITE_UART_FCR(sp, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+		WRITE_UART_FCR(sp, 0);
+	}
+
+	(void) READ_UART_LSR(sp);
+	(void) READ_UART_RX(sp);
+	(void) READ_UART_IIR(sp);
+	(void) READ_UART_MSR(sp);
+
+	if (!(sp->port.flags & SNX_UPF_BUGGY_UART) && (READ_UART_LSR(sp) == 0xff))
+	{
+		printk("SUNIX: ttySDC%d: LSR safety check engaged!\n", sp->port.line);
+		return -ENODEV;
+	}
+
+	WRITE_UART_LCR(sp, UART_LCR_WLEN8);
+
+	sp->ier = UART_IER_RLSI | UART_IER_RDI;
+	WRITE_UART_IER(sp, sp->ier);
+
+	(void) READ_UART_LSR(sp);
+	(void) READ_UART_RX(sp);
+	(void) READ_UART_IIR(sp);
+	(void) READ_UART_MSR(sp);
+
+	return 0;
+}
+
+
+static void sunix_ser_shutdown(struct snx_ser_port *port)
+{
+    struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+    sp->ier = 0;
+    WRITE_UART_IER(sp, 0);
+
+    WRITE_UART_LCR(sp, READ_UART_LCR(sp) & ~UART_LCR_SBC);
+
+    WRITE_UART_FCR(sp, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+    WRITE_UART_FCR(sp, 0);
+
+    (void) READ_UART_RX(sp);
+}
+
+
+static unsigned int sunix_ser_get_divisor(struct snx_ser_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+
+	if ((port->flags & SNX_UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/4))
+	{
+		quot = 0x8001;
+	}
+	else if ((port->flags & SNX_UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/8))
+	{
+		quot = 0x8002;
+	}
+	else
+	{
+		quot = snx_ser_get_divisor(port, baud);
+	}
+
+    return quot;
+}
+
+
+static void sunix_ser_set_termios(struct snx_ser_port *port, struct SNXTERMIOS *termios, struct SNXTERMIOS *old)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+	unsigned char cval;
+	unsigned char fcr = 0;
+	unsigned long flags;
+	unsigned int baud;
+	unsigned int quot;
+
+
+	switch (termios->c_cflag & CSIZE)
+	{
+		case CS5:
+			cval = 0x00;
+			break;
+
+		case CS6:
+			cval = 0x01;
+			break;
+
+		case CS7:
+			cval = 0x02;
+			break;
+
+		default:
+		case CS8:
+			cval = 0x03;
+			break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+	{
+		cval |= 0x04;
+	}
+
+	if (termios->c_cflag & PARENB)
+	{
+		cval |= UART_LCR_PARITY;
+	}
+
+	if (!(termios->c_cflag & PARODD))
+	{
+		cval |= UART_LCR_EPAR;
+	}
+
+#ifdef CMSPAR
+	if (termios->c_cflag & CMSPAR)
+	{
+		cval |= UART_LCR_SPAR;
+	}
+#endif
+
+	baud = snx_ser_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	quot = sunix_ser_get_divisor(port, baud);
+
+	if (sp->capabilities & UART_USE_FIFO)
+	{
+		if (baud < 2400)
+		{
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		}
+		else
+		{
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+		}
+	}
+
+	sp->mcr &= ~UART_MCR_AFE;
+
+	if (termios->c_cflag & CRTSCTS)
+	{
+		sp->mcr |= UART_MCR_AFE;
+	}
+
+	fcr |= 0x01;
+
+	spin_lock_irqsave(&sp->port.lock, flags);
+
+
+	snx_ser_update_timeout(port, termios->c_cflag, baud);
+
+
+	sp->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+
+	if (termios->c_iflag & INPCK)
+	{
+		sp->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	}
+
+	if (termios->c_iflag & (BRKINT | PARMRK))
+	{
+		sp->port.read_status_mask |= UART_LSR_BI;
+	}
+
+	sp->port.ignore_status_mask = 0;
+
+	if (termios->c_iflag & IGNPAR)
+	{
+		sp->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	}
+
+
+	if (termios->c_iflag & IGNBRK)
+	{
+		sp->port.ignore_status_mask |= UART_LSR_BI;
+
+		if (termios->c_iflag & IGNPAR)
+		{
+			sp->port.ignore_status_mask |= UART_LSR_OE;
+		}
+	}
+
+	if ((termios->c_cflag & CREAD) == 0)
+	{
+		sp->port.ignore_status_mask |= UART_LSR_DR;
+	}
+
+	sp->ier &= ~UART_IER_MSI;
+	if (SNX_ENABLE_MS(&sp->port, termios->c_cflag))
+	{
+		sp->ier |= UART_IER_MSI;
+	}
+
+	WRITE_UART_LCR(sp, cval | UART_LCR_DLAB);
+
+	WRITE_UART_DLL(sp, quot & 0xff);
+	WRITE_UART_DLM(sp, quot >> 8);
+
+	WRITE_UART_FCR(sp, fcr);
+
+	WRITE_UART_LCR(sp, cval);
+
+	sp->lcr = cval;
+
+	sunix_ser_set_mctrl(&sp->port, sp->port.mctrl);
+
+	WRITE_UART_IER(sp, sp->ier);
+
+	spin_unlock_irqrestore(&sp->port.lock, flags);
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+static void sunix_ser_timeout(struct timer_list *t)
+#else
+static void sunix_ser_timeout(unsigned long data)
+#endif
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+	struct sunix_sdc_uart_channel *sp = from_timer(sp, t, timer);
+#else
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)data;
+#endif
+	unsigned int timeout;
+	unsigned int iir;
+
+
+	iir = READ_UART_IIR(sp);
+
+	if (!(iir & UART_IIR_NO_INT))
+	{
+		spin_lock(&sp->port.lock);
+		sunix_ser_handle_port(sp, iir);
+		spin_unlock(&sp->port.lock);
+    }
+
+	timeout = sp->port.timeout;
+	timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+	mod_timer(&sp->timer, jiffies + timeout);
+}
+
+
+static _INLINE_ void sunix_ser_receive_chars(struct sunix_sdc_uart_channel *sp, unsigned char *status)
+{
+	struct tty_struct *tty = sp->port.info->tty;
+	unsigned char ch;
+	int max_count = 256;
+	unsigned char lsr = *status;
+	char flag;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+	struct snx_ser_state *state = NULL;
+	struct tty_port *tport = NULL;
+
+
+	state = tty->driver_data;
+	tport = &state->tport;
+#endif
+
+
+	do
+	{
+		ch = READ_UART_RX(sp);
+		flag = TTY_NORMAL;
+		sp->port.icount.rx++;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 18))
+		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE)))
+#else
+		if (lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE))
+#endif
+		{				
+			if (lsr & UART_LSR_BI)
+			{
+				lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+				sp->port.icount.brk++;
+
+				if (snx_ser_handle_break(&sp->port))
+				{					
+					goto ignore_char;
+				}
+			}
+			else if (lsr & UART_LSR_PE)
+			{
+				sp->port.icount.parity++;
+			}
+			else if (lsr & UART_LSR_FE)
+			{
+				sp->port.icount.frame++;
+			}
+
+			if (lsr & UART_LSR_OE)
+			{
+				sp->port.icount.overrun++;
+			}
+
+			lsr &= sp->port.read_status_mask;
+
+			if (lsr & UART_LSR_BI)
+			{
+				flag = TTY_BREAK;
+			}
+			else if (lsr & UART_LSR_PE)
+			{
+				flag = TTY_PARITY;
+			}
+			else if (lsr & UART_LSR_FE)
+			{
+				flag = TTY_FRAME;
+			}
+		}
+
+
+		if ((I_IXOFF(tty)) || I_IXON(tty))
+		{			
+			if (ch == START_CHAR(tty))
+			{
+				tty->stopped = 0;
+				sunix_ser_start_tx(&sp->port, 1);
+				goto ignore_char;
+			}
+			else if (ch == STOP_CHAR(tty))
+			{
+				tty->stopped = 1;
+				sunix_ser_stop_tx(&sp->port, 1);
+				goto ignore_char;
+			}
+		}
+
+		snx_ser_insert_char(&sp->port, lsr, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		lsr = READ_UART_LSR(sp);
+	
+		if (lsr == 0xff)
+		{
+			lsr = 0x01;
+		}
+
+	} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+
+	spin_unlock(&sp->port.lock);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
+	tty_flip_buffer_push(tport);
+#else
+	tty_flip_buffer_push(tty);
+#endif
+
+	spin_lock(&sp->port.lock);
+	*status = lsr;
+}
+
+
+static _INLINE_ void sunix_ser_transmit_chars(struct sunix_sdc_uart_channel *sp)
+{
+	struct circ_buf *xmit = &sp->port.info->xmit;
+	int count;
+
+
+	if ((!sp) || (!sp->port.iobase))
+	{
+		return;
+	}
+
+	if (!sp->port.info)
+	{
+		return;
+	}
+
+	if (!xmit)
+	{
+		return;
+	}
+
+	if (sp->port.x_char)
+	{
+		WRITE_UART_TX(sp, sp->port.x_char);
+		sp->port.icount.tx++;
+		sp->port.x_char = 0;
+		return;
+	}
+
+	if (snx_ser_circ_empty(xmit))
+	{
+		sunix_ser_stop_tx(&sp->port, 0);
+		return;
+	}
+	
+	if (snx_ser_tx_stopped(&sp->port))
+	{
+		sunix_ser_stop_tx(&sp->port, 0);
+		return;
+	}
+
+	count = sp->port.fifosize;
+
+	do
+	{
+		WRITE_UART_TX(sp, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (SNX_UART_XMIT_SIZE - 1);
+		sp->port.icount.tx++;
+
+		if (snx_ser_circ_empty(xmit))
+		{
+			break;
+		}
+
+	} while (--count > 0);
+
+	if (snx_ser_circ_chars_pending(xmit) < WAKEUP_CHARS)
+	{
+		snx_ser_write_wakeup(&sp->port);
+	}
+}
+
+
+static _INLINE_ void sunix_ser_check_modem_status(struct sunix_sdc_uart_channel *sp, unsigned char status)
+{		
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+	{
+		return;
+	}
+
+	if (!sp->port.info)
+	{
+		return;
+	}
+
+	if (status & UART_MSR_TERI)
+	{
+		sp->port.icount.rng++;
+	}
+
+	if (status & UART_MSR_DDSR)
+	{
+		sp->port.icount.dsr++;
+	}
+
+	if (status & UART_MSR_DDCD)
+	{
+		snx_ser_handle_dcd_change(&sp->port, status & UART_MSR_DCD);
+	}
+
+	if (status & UART_MSR_DCTS)
+	{
+		snx_ser_handle_cts_change(&sp->port, status & UART_MSR_CTS);
+	}
+
+	wake_up_interruptible(&sp->port.info->delta_msr_wait);
+}
+
+
+static _INLINE_ void sunix_ser_handle_port(struct sunix_sdc_uart_channel *sp, unsigned char iir)
+{
+	unsigned char lsr = READ_UART_LSR(sp);
+	unsigned char msr = 0;
+
+
+	if (lsr == 0xff)
+	{
+		lsr = 0x01;		
+	}
+
+	if ((iir == UART_IIR_RLSI) || (iir == UART_IIR_CTO) || (iir == UART_IIR_RDI))
+	{
+		sunix_ser_receive_chars(sp, &lsr);
+	}
+	
+	if ((iir == UART_IIR_THRI) && (lsr & UART_LSR_THRE))
+	{
+		sunix_ser_transmit_chars(sp);
+	}
+
+	msr = READ_UART_MSR(sp);
+
+	if (msr & UART_MSR_ANY_DELTA)
+	{
+		sunix_ser_check_modem_status(sp, msr);
+	}
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+static struct tty_operations sunix_tty_ops =
+{
+	.open               = snx_ser_open,
+	.close              = snx_ser_close,
+	.write              = snx_ser_write,
+	.put_char           = snx_ser_put_char,
+	.flush_chars        = snx_ser_flush_chars,
+	.write_room         = snx_ser_write_room,
+	.chars_in_buffer    = snx_ser_chars_in_buffer,
+	.flush_buffer       = snx_ser_flush_buffer,
+	.ioctl              = snx_ser_ioctl,
+	.throttle           = snx_ser_throttle,
+	.unthrottle         = snx_ser_unthrottle,
+	.send_xchar         = snx_ser_send_xchar,
+	.set_termios        = snx_ser_set_termios,
+	.stop               = snx_ser_stop,
+	.start              = snx_ser_start,
+	.hangup             = snx_ser_hangup,
+	.break_ctl          = snx_ser_break_ctl,
+	.wait_until_sent    = snx_ser_wait_until_sent,
+	.tiocmget           = snx_ser_tiocmget,
+	.tiocmset           = snx_ser_tiocmset,
+};
+#endif
+
+
+static struct snx_ser_driver sunix_ser_reg = 
+{
+	.dev_name = "ttySDC",
+	.major = 0,
+	.minor = 0,
+	.nr = (SUNIX_SDC_UART_MAX + 1),
+};
+
+
+int sunix_ser_register_driver(void)
+{
+	struct snx_ser_driver *drv = &sunix_ser_reg;
+	struct tty_driver *normal = NULL;
+	int i;
+	int ret = 0;
+
+
+	drv->state = kmalloc(sizeof(struct snx_ser_state) * drv->nr, GFP_KERNEL);
+
+	ret = -ENOMEM;
+
+	if (!drv->state)
+	{
+		printk("SUNIX: Allocate memory fail !\n");
+		goto out;
+	}
+
+	memset(drv->state, 0, sizeof(struct snx_ser_state) * drv->nr);
+
+	for (i = 0; i < drv->nr; i++)
+	{
+		struct snx_ser_state *state = drv->state + i;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+		struct tty_port *tport = &state->tport;
+		tty_port_init(tport);
+#endif
+
+		if (!state)
+		{
+			ret = -1;
+			printk("SUNIX: Memory error !\n");
+			goto out;
+		}
+
+		state->close_delay     = 5 * HZ / 100;
+		state->closing_wait    = 3 * HZ;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
+		sema_init(&state->sem, 1);
+#else
+		init_MUTEX(&state->sem);
+#endif
+    }
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	normal = tty_alloc_driver(SUNIX_SDC_UART_MAX + 1, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	normal = alloc_tty_driver(drv->nr);
+#else
+	normal = &drv->tty_driver;
+#endif
+
+
+	if (!normal)
+	{
+		printk("SUNIX: Allocate tty driver fail !\n");
+		goto out;
+	}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+
+#else
+	memset(normal, 0, sizeof(struct tty_driver));
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+    drv->tty_driver = normal;
+#endif
+
+	normal->magic                   = TTY_DRIVER_MAGIC;
+	normal->name                    = drv->dev_name;
+	normal->major                   = drv->major;
+	normal->minor_start             = drv->minor;
+	normal->num                     = (SUNIX_SDC_UART_MAX + 1);
+	normal->type                    = TTY_DRIVER_TYPE_SERIAL;
+	normal->subtype                 = SERIAL_TYPE_NORMAL;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+
+#else
+	normal->flags                   = TTY_DRIVER_REAL_RAW ;
+#endif
+
+	normal->init_termios            = tty_std_termios;
+	normal->init_termios.c_cflag    = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	normal->init_termios.c_iflag    = 0;
+
+	normal->driver_state            = drv;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	tty_set_operations(normal, &sunix_tty_ops);
+#else
+	normal->refcount                = &sunix_ser_refcount;
+	normal->table				    = sunix_ser_tty;
+
+	normal->termios				    = sunix_ser_termios;
+	normal->termios_locked		    = sunix_ser_termios_locked;
+
+	normal->open                    = snx_ser_open;
+	normal->close                   = snx_ser_close;
+	normal->write                   = snx_ser_write;
+	normal->put_char                = snx_ser_put_char;
+	normal->flush_chars             = snx_ser_flush_chars;
+	normal->write_room              = snx_ser_write_room;
+	normal->chars_in_buffer         = snx_ser_chars_in_buffer;
+	normal->flush_buffer            = snx_ser_flush_buffer;
+	normal->ioctl                   = snx_ser_ioctl;
+	normal->throttle                = snx_ser_throttle;
+	normal->unthrottle              = snx_ser_unthrottle;
+	normal->send_xchar              = snx_ser_send_xchar;
+	normal->set_termios             = snx_ser_set_termios;
+	normal->stop                    = snx_ser_stop;
+	normal->start                   = snx_ser_start;
+	normal->hangup                  = snx_ser_hangup;
+	normal->break_ctl               = snx_ser_break_ctl;
+	normal->wait_until_sent         = snx_ser_wait_until_sent;
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	tty_port_link_device(&snx_service_port, normal, SUNIX_SDC_UART_MAX);
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28))
+	kref_init(&normal->kref);
+#endif
+
+	ret = tty_register_driver(normal);
+
+	if (ret < 0)
+	{
+		printk("SUNIX: Register tty driver fail !\n");
+		goto out;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+	for (i = 0; i < drv->nr; i++)
+	{
+		struct snx_ser_state *state = drv->state + i;
+		struct tty_port *tport = &state->tport;
+
+		tty_port_destroy(tport);
+	}
+#endif
+
+out:
+	if (ret < 0)
+	{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+		put_tty_driver(normal);
+#endif
+		kfree(drv->state);
+	}
+
+	return (ret);
+}
+
+
+void sunix_ser_unregister_driver(void)
+{
+	struct snx_ser_driver *drv = &sunix_ser_reg;
+	struct tty_driver *normal = NULL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+	unsigned int i;
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	normal = drv->tty_driver;
+
+	if (!normal)
+	{
+		return;
+	}
+
+	tty_unregister_driver(normal);
+	put_tty_driver(normal);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+	for (i = 0; i < drv->nr; i++)
+	{
+		struct snx_ser_state *state = drv->state + i;
+		struct tty_port *tport = &state->tport;
+
+		tty_port_destroy(tport);
+	}
+#endif
+
+	drv->tty_driver = NULL;
+
+#else
+	normal = &drv->tty_driver;
+	if (!normal)
+	{
+		return;
+	}
+
+	tty_unregister_driver(normal);
+#endif
+
+	if (drv->state)
+	{
+		kfree(drv->state);
+	}
+}
+
+
+static void sunix_ser_request_io(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	switch (sp->port.iotype)
+	{
+		case SNX_UPIO_PORT:
+			request_region(sp->port.iobase, sp->port.iosize, "sunix_sdc_uart");
+			break;
+
+		default:
+			break;
+	}
+}
+
+
+static void sunix_ser_configure_port(struct snx_ser_driver *drv, struct snx_ser_state *state, struct snx_ser_port *port)
+{
+	unsigned long flags;
+
+
+	if (!port->iobase)
+	{
+		return;
+	}
+
+	flags = SNX_UART_CONFIG_TYPE;
+
+	if (port->type != PORT_UNKNOWN)
+	{
+		sunix_ser_request_io(port);
+
+		spin_lock_irqsave(&port->lock, flags);
+
+		sunix_ser_set_mctrl(port, 0);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+
+
+static int sunix_ser_add_one_port(struct snx_ser_driver *drv, struct snx_ser_port *port)
+{
+	struct snx_ser_state *state = NULL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	struct tty_port *tport;
+	struct device *tty_dev;
+#endif
+	int ret = 0;
+
+
+	if (port->line >= drv->nr)
+	{
+		return -EINVAL;
+	}
+
+	state = drv->state + port->line;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	tport = &state->tport;
+#endif
+
+	down(&ser_port_sem);
+
+	if (state->port)
+	{
+		ret = -EINVAL;
+		goto out;
+	}
+
+	state->port = port;
+
+	port->info = state->info;
+
+	sunix_ser_configure_port(drv, state, port);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+
+	tty_dev = tty_port_register_device(tport, drv->tty_driver, port->line, port->dev);
+
+	if (likely(!IS_ERR(tty_dev)))
+	{
+		device_set_wakeup_capable(tty_dev, 1);
+	}
+	else
+	{
+		printk("SUNIX: Cannot register tty device on line %d\n", port->line);
+	}
+
+#endif
+
+out:
+	up(&ser_port_sem);
+
+	return ret;
+}
+
+
+int sunix_ser_register_ports(void)
+{
+	struct snx_ser_driver *drv = &sunix_ser_reg;
+	struct sunix_sdc_board *sb = NULL;
+	int i;
+	int ret;
+
+
+	sb = &sunix_sdc_board_table[0];
+	if (sb == NULL)
+	{
+		return 0;
+	}
+
+	pci_set_drvdata(sb->pdev, sb);
+
+	for (i = 0; i < SUNIX_SDC_UART_MAX + 1; i++)
+	{
+		struct sunix_sdc_uart_channel *sp = &sunix_sdc_uart_table[i];
+
+		if (!sp)
+		{
+			return -1;
+		}
+
+		sp->port.line = i;
+
+		if (sp->port.iobase)
+		{
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+			timer_setup(&sp->timer, sunix_ser_timeout, 0);
+#else
+			init_timer(&sp->timer);
+			sp->timer.function = sunix_ser_timeout;
+#endif
+			sp->mcr_mask = ~0;
+			sp->mcr_force = 0;
+
+			ret = sunix_ser_add_one_port(drv, &sp->port);
+
+			if (ret != 0)
+			{
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+static void sunix_ser_release_io(struct snx_ser_port *port)
+{
+	struct sunix_sdc_uart_channel *sp = (struct sunix_sdc_uart_channel *)port;
+
+
+	switch (sp->port.iotype)
+	{
+		case SNX_UPIO_PORT:
+			release_region(sp->port.iobase, sp->port.iosize);
+			break;
+
+		default:
+			break;
+	}
+}
+
+
+static void sunix_ser_unconfigure_port(struct snx_ser_driver *drv, struct snx_ser_state *state)
+{
+	struct snx_ser_port *port = state->port;
+	struct snx_ser_info *info = state->info;
+
+
+	if (info && info->tty)
+	{
+		tty_hangup(info->tty);
+	}
+
+	down(&state->sem);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
+	tty_unregister_device(drv->tty_driver, port->line);
+#endif
+
+	state->info = NULL;
+
+	if (port->type != PORT_UNKNOWN)
+	{
+		sunix_ser_release_io(port);
+	}
+
+	port->type = PORT_UNKNOWN;
+
+	if (info)
+	{
+		tasklet_kill(&info->tlet);
+		kfree(info);
+	}
+
+	up(&state->sem);
+}
+
+
+static int sunix_ser_remove_one_port(struct snx_ser_driver *drv, struct snx_ser_port *port)
+{
+	struct snx_ser_state *state = drv->state + port->line;
+
+
+	if (state->port != port)
+	{
+		printk("SUNIX: Removing wrong port: %p != %p\n", state->port, port);
+	}
+
+	down(&ser_port_sem);
+
+	sunix_ser_unconfigure_port(drv, state);
+
+	state->port = NULL;
+
+	up(&ser_port_sem);
+
+	return 0;
+}
+
+
+void sunix_ser_unregister_ports(void)
+{
+	struct snx_ser_driver *drv = &sunix_ser_reg;
+	int i;
+
+
+	for (i = 0; i < SUNIX_SDC_UART_MAX + 1; i++)
+	{
+		struct sunix_sdc_uart_channel *sp = &sunix_sdc_uart_table[i];
+
+		if (sp->port.iobase)
+		{
+			sunix_ser_remove_one_port(drv, &sp->port);
+		}
+	}
+}
+
+
+int sunix_ser_interrupt(struct sunix_sdc_uart_channel *sp)
+{
+	unsigned char iir;
+
+
+	iir = READ_UART_IIR(sp) & 0x0f;
+
+	if (!(iir & UART_IIR_NO_INT))
+	{
+		spin_lock(&sp->port.lock);
+		sunix_ser_handle_port(sp, iir);
+		spin_unlock(&sp->port.lock);
+	}
+
+    return 0;
+}
+
-- 
2.20.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ