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>] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 15 Jun 2015 09:54:27 +0800
From:	Peter Hung <hpeter@...il.com>
To:	johan@...nel.org
Cc:	gregkh@...uxfoundation.org, linux-usb@...r.kernel.org,
	linux-kernel@...r.kernel.org, peter_hong@...tek.com.tw,
	tom_tsai@...tek.com.tw, Peter Hung <hpeter+linux_kernel@...il.com>
Subject: [PATCH V2 1/1] usb:serial:f81534 Add F81532/534 Driver

This driver is for Fintek F81532/F81534 USB to Serial Ports IC.

Features:
1. F81534 is 1-to-4 & F81532 is 1-to-2 serial ports IC
2. Support Baudrate from B50 to B1500000 (excluding B1000000).
3. The RTS signal can be transformed their behavior with configuration
   for transceiver (for RS232/RS485/RS422) (/sys/class/ttyUSBx/uart_mode)
4. There are 4x3 output-only GPIOs to control transceiver mode. It's
   can be controlled via sysfs (/sys/class/ttyUSBx/gpio)

Changelog from v1:
1. v1 version submit to staging tree, but Greg KH advised me to cleanup
   source code & re-submit it to correct subsystem
2. Remove all custom ioctl commands

If had any question, Please send email to
hpeter+linux_kernel@...il.com
peter_hong@...tek.com.tw

Signed-off-by: Peter Hung <hpeter+linux_kernel@...il.com>
---
 drivers/usb/serial/Kconfig  |   10 +
 drivers/usb/serial/Makefile |    1 +
 drivers/usb/serial/f81534.c | 3162 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 3173 insertions(+)
 create mode 100644 drivers/usb/serial/f81534.c

diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index b7cf198..4bf3011 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -255,6 +255,16 @@ config USB_SERIAL_F81232
 	  To compile this driver as a module, choose M here: the
 	  module will be called f81232.
 
+config USB_SERIAL_F8153X
+	tristate "USB Fintek F81532/534 Multi-Ports Serial Driver"
+	help
+	  Say Y here if you want to use the Fintek F81532/534 Multi-Ports
+	  usb to serial adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called f81534.
+
+
 config USB_SERIAL_GARMIN
        tristate "USB Garmin GPS driver"
        help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 349d9df..9e43b7b 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT)		+= io_edgeport.o
 obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI)		+= io_ti.o
 obj-$(CONFIG_USB_SERIAL_EMPEG)			+= empeg.o
 obj-$(CONFIG_USB_SERIAL_F81232)			+= f81232.o
+obj-$(CONFIG_USB_SERIAL_F8153X)			+= f81534.o
 obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
 obj-$(CONFIG_USB_SERIAL_GARMIN)			+= garmin_gps.o
 obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
new file mode 100644
index 0000000..8c817ee
--- /dev/null
+++ b/drivers/usb/serial/f81534.c
@@ -0,0 +1,3162 @@
+/*
+ * F81532/F81534 USB to Serial Ports Bridge
+ *
+ * F81532 => 2 Serial Ports
+ * F81534 => 4 Serial Ports
+ *
+ * Copyright (C) 2014 Tom Tsai (Tom_Tsai@...tek.com.tw)
+ *
+ */
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/kfifo.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+
+/* Serial Port register Address */
+#define SERIAL_BASE_ADDRESS		0x1200
+#define RECEIVE_BUFFER_REGISTER		(0x00 + SERIAL_BASE_ADDRESS)
+#define TRANSMIT_HOLDING_REGISTER	(0x00 + SERIAL_BASE_ADDRESS)
+#define INTERRUPT_ENABLE_REGISTER	(0x01 + SERIAL_BASE_ADDRESS)
+#define INTERRUPT_IDENT_REGISTER	(0x02 + SERIAL_BASE_ADDRESS)
+#define FIFO_CONTROL_REGISTER		(0x02 + SERIAL_BASE_ADDRESS)
+#define LINE_CONTROL_REGISTER		(0x03 + SERIAL_BASE_ADDRESS)
+#define MODEM_CONTROL_REGISTER		(0x04 + SERIAL_BASE_ADDRESS)
+#define LINE_STATUS_REGISTER		(0x05 + SERIAL_BASE_ADDRESS)
+#define MODEM_STATUS_REGISTER		(0x06 + SERIAL_BASE_ADDRESS)
+#define CLK_SEL_REGISTER		(0x08 + SERIAL_BASE_ADDRESS)
+#define CONFIG1_REGISTER		(0x09 + SERIAL_BASE_ADDRESS)
+#define SADDRESS_REGISTER		(0x0a + SERIAL_BASE_ADDRESS)
+#define SADEN_REGISTER			(0x0b + SERIAL_BASE_ADDRESS)
+#define DIVISOR_LATCH_LSB		(0x00 + SERIAL_BASE_ADDRESS)
+#define DIVISOR_LATCH_MSB		(0x01 + SERIAL_BASE_ADDRESS)
+#define SCRATCH_PAD_REGISTER		(0x07 + SERIAL_BASE_ADDRESS)
+
+#define F81534_RESERVE_ADDRESS_START	0x3000
+#define F81534_RESERVE_SIZE		8
+
+#define F81534_CUSTOM_ADDRESS_START	0x4000
+#define F81534_CUSTOM_TOTAL_SIZE	0x1000
+
+#define F81534_CUSTOM_DATA_SIZE	0x10
+#define F81534_CUSTOM_MAX_IDX \
+		(F81534_CUSTOM_TOTAL_SIZE/F81534_CUSTOM_DATA_SIZE)
+#define F81534_CUSTOM_NO_CUSTOM_DATA	(-1)
+
+#define F81534_MAX_DATA_BLOCK		64
+#define F81534_BUSY_STATUS	0x03
+#define F81534_MAX_BUS_RETRY		2000
+
+/* default urb timeout for usb operations */
+#define F81534_USB_MAX_RETRY	5
+#define F81534_USB_TIMEOUT	1000
+#define F81534_CONTROL_BYTE	0x1B
+#define F81534_SET_GET_REGISTER	0xA0
+
+#define F81534_NUM_PORT	4
+#define F81534_UNUSED_PORT	0xff
+#define F81534_DEFAULT_BAUD_RATE	9600
+#define F81534_WRITE_BUFFER_SIZE	(512L)	/* size of write buffer */
+#define F81534_READ_BUFFER_SIZE		(512L)
+#define IC_NAME			"f81534"
+#define DRIVER_DESC \
+	"Fintek USB to Serial Ports Driver (F81532/F81534-Evaluation Board)"
+#define FINTEK_VENDOR_ID	0x1934
+#define FINTEK_DEVICE_ID	0x1202	/* RS232 four port */
+#define F81534_MAX_TX_SIZE	124L
+#define F81534_FIFO_SIZE	128L
+
+#define MULTIDROP_ENABLE
+#define HIGHBAUDRATE_ENABLE
+
+#ifdef HIGHBAUDRATE_ENABLE
+#define F81534_MAX_BAUDRATE	1500000
+#else
+#define F81534_MAX_BAUDRATE	115200
+#endif
+
+struct internal_data {
+	unsigned int address;
+	unsigned int size;
+	unsigned char buf[F81534_MAX_DATA_BLOCK];
+};
+
+#define F81534_RS232_FLAG	0x00
+#define F81534_RS485_FLAG	0x03
+#define F81534_RS485_1_FLAG	0x01
+
+static int m_F81534_MAX_TX_SIZE = F81534_MAX_TX_SIZE;
+
+enum eUartMode {
+	eModeRS422,
+	eModeRS232,
+	eModeRS485,
+	eModeRS485_1,
+	eModeRS422_term,
+	eModeRS232_coexist,
+	eModeRS485_1_term,
+	eModeShutdown,
+
+	eModeInvalid,
+};
+
+struct f81534_pin_config_data {
+	char dev_path[32];
+	char dev_name[32];
+	enum eUartMode eForceUartMode;
+	enum eUartMode eGPIOMode;
+	u8 port_invisable;
+	int address[9];
+	int offset[9];
+};
+
+struct reg_value {
+	int reg_address;
+	int reg_offset;
+	int reg_bit;
+};
+
+struct pin_data {
+	struct reg_value port_mode_1;
+	struct reg_value port_mode_0;
+	struct reg_value port_io;
+};
+
+struct out_pin {
+	struct pin_data m1;
+	struct pin_data m2;
+	struct pin_data sd;
+};
+
+struct io_map_value {
+	int product_id;
+	int max_port;
+	enum eUartMode mode;
+	struct out_pin port[MAX_NUM_PORTS + 1];
+};
+
+static struct io_map_value f81534_rs232_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeRS232,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 0},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 0},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 1},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 0},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 0},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 1},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 0},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 0},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 1},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 0},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 0},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 1},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs485_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeRS485,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 0},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 1},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 0},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 0},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 1},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 0},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 0},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 1},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 0},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 0},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 1},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 0},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs485_1_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeRS485_1,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 0},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 1},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 1},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 0},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 1},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 1},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 0},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 1},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 1},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 0},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 1},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 1},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs422_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeRS422,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 0},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 0},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 0},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 0},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 0},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 0},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 0},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 0},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 0},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 0},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 0},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 0},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_shutdown_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeShutdown,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 1},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 1},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 1},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 1},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 1},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 1},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 1},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 1},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 1},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 1},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 1},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 1},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs422_term_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeShutdown,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 1},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 0},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 0},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 1},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 0},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 0},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 1},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 0},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 0},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 1},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 0},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 0},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs232_coexist_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeShutdown,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 1},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 0},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 1},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 1},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 0},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 1},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 1},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 0},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 1},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 1},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 0},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 1},},
+	  },
+	 },
+};
+
+static struct io_map_value f81534_rs485_1_term_control = {
+	FINTEK_DEVICE_ID, F81534_NUM_PORT, eModeShutdown,
+
+	{
+	 /* please reference f81439 io port */
+	 {
+	  {{0x2ad5, 4, 0}, {0x2ad4, 4, 1}, {0x2a90, 4, 1},},
+	  {{0x2ad5, 5, 0}, {0x2ad4, 5, 1}, {0x2a90, 5, 1},},
+	  {{0x2add, 7, 0}, {0x2adc, 7, 1}, {0x2ae8, 7, 0},},
+	  },
+	 {
+	  {{0x2add, 3, 0}, {0x2adc, 3, 1}, {0x2ae8, 3, 1},},
+	  {{0x2add, 0, 0}, {0x2adc, 0, 1}, {0x2ae8, 0, 1},},
+	  {{0x2add, 6, 0}, {0x2adc, 6, 1}, {0x2ae8, 6, 0},},
+	  },
+	 {
+	  {{0x2ad3, 6, 0}, {0x2ad2, 6, 1}, {0x2a80, 6, 1},},
+	  {{0x2add, 2, 0}, {0x2adc, 2, 1}, {0x2ae8, 2, 1},},
+	  {{0x2ad5, 0, 0}, {0x2ad4, 0, 1}, {0x2a90, 0, 0},},
+	  },
+	 {
+	  {{0x2ad5, 1, 0}, {0x2ad4, 1, 1}, {0x2a90, 1, 1},},
+	  {{0x2ad5, 2, 0}, {0x2ad4, 2, 1}, {0x2a90, 2, 1},},
+	  {{0x2ad5, 3, 0}, {0x2ad4, 3, 1}, {0x2a90, 3, 0},},
+	  },
+	 },
+};
+
+static struct io_map_value *f81534_mode_control[eModeInvalid] = {
+	&f81534_rs422_control,
+	&f81534_rs232_control,
+	&f81534_rs485_control,
+	&f81534_rs485_1_control,
+	&f81534_rs422_term_control,
+	&f81534_rs232_coexist_control,
+	&f81534_rs485_1_term_control,
+	&f81534_shutdown_control,
+};
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(FINTEK_VENDOR_ID, FINTEK_DEVICE_ID)},
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+enum eIC_Type {
+	eIC_F81530,
+	eIC_F81531,
+	eIC_F81532,
+	eIC_F81533,
+	eIC_F81534,
+};
+
+static const char * const m_ic_name[] = {
+	"F81530",
+	"F81531",
+	"F81532",
+	"F81533",
+	"F81534",
+};
+
+struct f81534_serial_private {
+	enum eIC_Type ic_type;
+	struct usb_serial *serial;
+	struct urb *read_urb[F81534_NUM_PORT];
+	char read_buffer[F81534_NUM_PORT][F81534_READ_BUFFER_SIZE];
+	bool phy_port_in_use[F81534_NUM_PORT];
+	bool write_urb_in_use[F81534_NUM_PORT];
+	struct urb *write_urb[F81534_NUM_PORT];
+	char write_buffer[F81534_NUM_PORT][F81534_WRITE_BUFFER_SIZE];
+	spinlock_t write_urb_lock;
+	spinlock_t msr_lock;
+	u8 shadowMSR[F81534_NUM_PORT];
+	unsigned long msr_time[F81534_NUM_PORT];
+	struct mutex msr_mutex;
+	u8 port_mapping[F81534_NUM_PORT];
+	struct mutex updating_mutex;
+	u8 reserve_data[F81534_RESERVE_SIZE];
+	u32 custom_idx;
+};
+
+struct f81534_port_private {
+	spinlock_t lock;
+	u8 shadowLSR;
+	u8 shadowMCR;
+	u8 shadowLCR;
+	struct usb_serial_port *port;
+	u32 tx, rx;
+	u32 currentLine;
+	u32 currentBaudRate;
+
+	struct f81534_pin_config_data port_pin_data;
+};
+
+static void f81534_update_lsr(struct usb_serial_port *port,
+		unsigned char *ch);
+static void f81534_update_msr(struct usb_serial_port *port,
+		unsigned char *ch);
+static void f81534_write_bulk_callback(struct urb *urb);
+static void f81534_read_bulk_callback(struct urb *urb);
+static int f81534_setup_urbs(struct usb_serial *serial);
+
+static int f81534_set_normal_register(struct usb_device *dev,
+			u16 reg, u8 data);
+
+static int f81534_get_normal_register(struct usb_device *dev,
+			u16 reg, u8 *data);
+
+static int f81534_getregister(struct usb_device *dev,
+			u8 uart, u16 reg, u8 *data);
+
+static void f81534_dtr_rts(struct usb_serial_port *port, int on);
+static int f81534_set_port_mode(struct usb_serial_port *port,
+			enum eUartMode eMode);
+static int f81534_free_urbs(struct usb_serial *serial);
+static int f81534_save_configure_data(struct usb_serial_port *port);
+
+static int f81534_logic_to_phy_port(struct usb_serial *usbserial, int logic)
+{
+	int index;
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(usbserial);
+
+	for (index = 0; index < F81534_NUM_PORT; ++index, --logic) {
+		if ((serial_priv->port_mapping[index] != F81534_UNUSED_PORT)
+		    && !logic)
+			return serial_priv->port_mapping[index];
+	}
+
+	dev_err(&usbserial->dev->dev, "%s could found mapping: logic: %x\n",
+		__func__, logic);
+	return F81534_UNUSED_PORT;
+}
+
+static int f81534_phy_to_logic_port(struct usb_serial *usbserial, int phy)
+{
+	int index;
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(usbserial);
+
+	for (index = 0; index < F81534_NUM_PORT; ++index)
+		if (serial_priv->port_mapping[index] == phy)
+			return index;
+
+	dev_err(&usbserial->dev->dev, "%s could found mapping: phy: %x\n",
+		__func__, phy);
+	return F81534_UNUSED_PORT;
+}
+
+static int f81534_port_to_phy_index(struct usb_serial_port *port)
+{
+	return f81534_logic_to_phy_port(port->serial, port->port_number);
+}
+
+static int f81534_port_index(struct usb_serial_port *port)
+{
+	return port->port_number;	/* no conversion, just return */
+}
+
+static int f81534_command_delay(struct usb_serial *usbserial)
+{
+	unsigned int count = F81534_MAX_BUS_RETRY;
+	unsigned char tmp;
+	int status;
+	struct usb_device *dev = usbserial->dev;
+
+	do {
+		status = f81534_get_normal_register(dev,
+					0x1003, &tmp);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n",
+					__func__, __LINE__);
+			return status;
+		}
+
+		if (tmp & 0x03)
+			continue;
+
+		if (tmp & 0x04)
+			break;
+
+	} while (--count);
+
+	if (!count) {
+		dev_err(&usbserial->dev->dev, "%s: max retry exceed !!!\n",
+			__func__);
+		return -EIO;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1003, tmp & ~0x04);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81534_read_data(struct usb_serial *usbserial, int address, int size,
+			    unsigned char *buf)
+{
+	unsigned int count = 0;
+	unsigned int read_size = 0;
+	unsigned int block = 0;
+	unsigned char *tmp_buf;
+	int status;
+	int offset;
+	struct usb_device *dev = usbserial->dev;
+
+	tmp_buf = kzalloc(F81534_MAX_DATA_BLOCK, GFP_KERNEL);
+	if (!tmp_buf)
+		return -ENOMEM;
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_set_normal_register(dev,
+				0x1002, 0x03);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_set_normal_register(dev,
+				0x1002, (address >> 16) & 0xff);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_set_normal_register(dev,
+				0x1002, (address >> 8) & 0xff);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	status = f81534_set_normal_register(dev,
+				0x1002, (address >> 0) & 0xff);
+	if (status) {
+		dev_err(&dev->dev, "%s: failed, %d\n",
+				__func__, __LINE__);
+		goto error;
+	}
+
+	/* continuous read mode */
+	do {
+		read_size = min(F81534_MAX_DATA_BLOCK, size);
+
+		for (count = 0; count < read_size; ++count) {
+
+			status = f81534_command_delay(usbserial);
+			if (status) {
+				dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+				goto error;
+			}
+
+			if ((size <= F81534_MAX_DATA_BLOCK) &&
+					(read_size == (count + 1))) {
+				status = f81534_set_normal_register(dev,
+						0x1001, 0xf1);
+				if (status) {
+					dev_err(&dev->dev,
+							"%s: failed, %d\n",
+							__func__, __LINE__);
+					goto error;
+				}
+			} else {
+				status = f81534_set_normal_register(dev,
+						0x1002, 0xf1);
+				if (status) {
+					dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+					goto error;
+				}
+			}
+
+			status = f81534_command_delay(usbserial);
+			if (status) {
+				dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+				goto error;
+			}
+
+			status = f81534_get_normal_register(dev,
+					0x1004, &tmp_buf[count]);
+			if (status) {
+				dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+				goto error;
+			}
+
+			status = f81534_command_delay(usbserial);
+			if (status) {
+				dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+				goto error;
+			}
+
+			offset = count + (block * F81534_MAX_DATA_BLOCK);
+			buf[offset] = tmp_buf[count];
+		}
+
+		size -= read_size;
+		block += 1;
+	} while (size);
+
+	kfree(tmp_buf);
+	return 0;
+
+error:
+	kfree(tmp_buf);
+	return status;
+
+}
+
+static int f81534_write_data(struct usb_serial *usbserial, int address,
+			     int size, unsigned char *buf)
+{
+	unsigned int count = 0;
+	unsigned int write_size = 0;
+	unsigned int block = 0;
+	int offset;
+	int status;
+	struct usb_device *dev = usbserial->dev;
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1001, 0x06);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002, 0x02);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002,
+				(address >> 16) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002,
+				(address >> 8) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002,
+						    (address >> 0) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	do {
+		write_size = min(F81534_MAX_DATA_BLOCK, size);
+
+		for (count = 0; count < write_size; ++count) {
+
+			status = f81534_command_delay(usbserial);
+			if (status) {
+				dev_err(&dev->dev,
+						"%s: failed, %d\n",
+						__func__, __LINE__);
+				return status;
+			}
+
+			offset = count + block * F81534_MAX_DATA_BLOCK;
+
+			if ((size <= F81534_MAX_DATA_BLOCK)
+					&& (write_size == (count + 1))) {
+
+				status = f81534_set_normal_register(dev, 0x1001,
+							buf[offset]);
+				if (status) {
+					dev_err(&dev->dev,
+							"%s: failed, %d\n",
+							__func__, __LINE__);
+					return status;
+				}
+
+			} else {
+				status = f81534_set_normal_register(dev, 0x1002,
+							buf[offset]);
+				if (status) {
+					dev_err(&dev->dev,
+							"%s: failed, %d\n",
+							__func__, __LINE__);
+					return status;
+				}
+			}
+		}
+
+		size -= write_size;
+		block += 1;
+	} while (size);
+
+	return 0;
+}
+
+static int f81534_erase_sector(struct usb_serial *usbserial, int address)
+{
+	u8 current_status = 0;
+	int status;
+	unsigned int count = F81534_MAX_BUS_RETRY;
+	struct usb_device *dev = usbserial->dev;
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1001, 0x06);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002, 0x20);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002,
+						    (address >> 16) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1002,
+						    (address >> 8) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_command_delay(usbserial);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	status = f81534_set_normal_register(dev, 0x1001,
+						    (address >> 0) & 0xff);
+	if (status) {
+		dev_err(&dev->dev,
+				"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	/* getting status */
+
+	while (--count) {
+		status = f81534_command_delay(usbserial);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		status = f81534_set_normal_register(dev, 0x1002, 0x05);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		status = f81534_command_delay(usbserial);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		status = f81534_set_normal_register(dev, 0x1001, 0xff);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		status = f81534_command_delay(usbserial);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		status = f81534_get_normal_register(dev, 0x1004,
+							    &current_status);
+		if (status) {
+			dev_err(&dev->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		if (!(F81534_BUSY_STATUS & current_status)) {
+			dev_info(&usbserial->dev->dev,
+				 "%s: data:%x, count:%d, ok\n", __func__,
+				 current_status, count);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void f81534_wakeup_all_port(struct usb_serial *serial)
+{
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		if (serial->port[i])
+			usb_serial_port_softint(serial->port[i]);
+	}
+}
+
+static int f81534_calc_baud_divisor(u32 baudrate, u32 clockrate, u32 *remain)
+{
+	u32 divisor, rem;
+
+	if (!baudrate)
+		return 0;
+
+	divisor = clockrate / baudrate;
+	rem = clockrate % baudrate;
+
+	if (remain)
+		*remain = rem;
+
+	/* Round to nearest divisor */
+	divisor = DIV_ROUND_CLOSEST(clockrate, baudrate);
+
+	return divisor;
+}
+
+static int f81534_get_normal_register(struct usb_device *dev,
+					     u16 reg, u8 *data)
+{
+	int count = F81534_USB_MAX_RETRY;
+	int status;
+
+	while (count--) {
+		status = usb_control_msg(dev,
+					 usb_rcvctrlpipe(dev, 0),
+					 F81534_SET_GET_REGISTER,
+					 0xc0,
+					 reg,
+					 0, data, sizeof(*data),
+					 F81534_USB_TIMEOUT);
+		if (status <= 0) {
+			if (status == 0)
+				status = -EIO;
+		} else {
+			break;
+		}
+	}
+
+	if ((count <= 0) && status) {
+		dev_err(&dev->dev,
+			"%s ERROR reg:%x status:%i failed\n",
+			__func__, reg, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81534_set_normal_register(struct usb_device *dev,
+					     u16 reg, u8 data)
+{
+	int count = F81534_USB_MAX_RETRY;
+	int status = 0;
+
+	while (count--) {
+		status = usb_control_msg(dev,
+					 usb_sndctrlpipe(dev, 0),
+					 F81534_SET_GET_REGISTER,
+					 0x40, reg, 0, &data, 1,
+					 F81534_USB_TIMEOUT);
+		if (status <= 0) {
+			if (status == 0)
+				status = -EIO;
+		} else {
+			break;
+		}
+	}
+
+	if ((count <= 0) && status) {
+		dev_err(&dev->dev,
+			"%s ERROR reg:%x data:0x%x status:%i failed\n",
+			__func__, reg, data, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81534_setregister(struct usb_device *dev,
+				     u8 uart, u16 reg, u8 data)
+{
+	/* Our device maybe not reply when heavily loading,
+	 * We'll retry for F81534_USB_MAX_RETRY times
+	 */
+
+	int count = F81534_USB_MAX_RETRY;
+	int status;
+
+	while (count--) {
+		status = usb_control_msg(dev,
+					 usb_sndctrlpipe(dev, 0),
+					 F81534_SET_GET_REGISTER,
+					 0x40,
+					 reg + uart * 0x10,
+					 0, &data, 1, F81534_USB_TIMEOUT);
+		if (status <= 0) {
+			if (status == 0)
+				status = -EIO;
+		} else {
+			break;
+		}
+	}
+
+	if ((count <= 0) && status) {
+		dev_err(&dev->dev,
+			"%s ERROR port_number:%d reg:%x data:0x%x status:%i failed\n",
+			__func__, uart, reg + uart * 0x10, data, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81534_set_port_config(struct usb_device *dev,
+					 unsigned char port_number,
+					 struct usb_serial_port *port,
+					 u32 baudrate, u16 lcr)
+{
+	struct usb_serial *serial = port->serial;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	u16 device_port = f81534_port_to_phy_index(port);
+	u32 divisor[3] = {0, 0, 0};
+	u32 rem[3] = {0, 0, 0};
+	u8 dll = 0;
+	u8 dlm = 0;
+	int val = 0x80;
+	int status;
+	u8 value;
+	bool is485Mode = false;
+	bool needInvert = false;
+
+	switch (port_priv->port_pin_data.eForceUartMode) {
+	case eModeRS232:
+	case eModeShutdown:
+	case eModeRS232_coexist:
+	case eModeInvalid:
+		break;
+
+	case eModeRS485:
+		needInvert = true;
+	default:
+		is485Mode = true;
+		break;
+
+	}
+
+	if (baudrate <= 115200) {
+		value = 0x01;	/* 1.846m fixed */
+		divisor[0] = f81534_calc_baud_divisor(baudrate, 115200, NULL);
+	} else {
+#ifdef HIGHBAUDRATE_ENABLE
+		int count;
+		u32 tmpIdx = 0xffffffff;
+		u32 minRem = 0xffffffff;
+		u32 baudrate_table[3] = { 921600, 1152000, 1500000 };
+		u8 clock_table[3] = { 0x07, 0x03, 0x05 };
+
+		for (count = 0; count < 3; ++count)
+			divisor[count] = f81534_calc_baud_divisor(baudrate,
+								  baudrate_table
+								  [count],
+								  &rem[count]);
+
+		for (count = 2; count >= 0; --count) {
+			if (!rem[count] && divisor[count])
+				break;	/* best clock */
+
+			if (divisor[count] && (minRem >= rem[count])) {
+				minRem = rem[count];
+				tmpIdx = count;
+			}
+		}
+
+		if (count != -1) {
+			tmpIdx = count;
+			dev_dbg(&dev->dev, "Best Index: %d, clock: %d\n",
+				tmpIdx, baudrate_table[tmpIdx] * 16);
+		} else {
+			dev_dbg(&dev->dev,
+				"Subsititude Index: %d, minRem:%d, clock:%d\n",
+				tmpIdx, minRem, baudrate_table[tmpIdx] * 16);
+		}
+
+		dev_dbg(&dev->dev, "\n");
+
+		for (count = 0; count < 3; ++count)
+			dev_dbg(&dev->dev, "Index: %d, divisor:%d, rem:%d\n",
+				count, divisor[count], rem[count]);
+
+		dev_dbg(&dev->dev, "\n");
+
+		divisor[0] = divisor[tmpIdx];
+		value = clock_table[tmpIdx];
+#else
+		dev_err(&dev->dev, "%s: baud rate error, max is:%d, current:%d\n",
+				__func__, F81534_MAX_BAUDRATE, baudrate);
+#endif
+	}
+
+	value &= 0xcf;		/* remove bit4 & 5 */
+	value |= is485Mode << 4;	/* rs485/422 mode */
+	value |= needInvert << 5;	/* invert mode */
+
+
+	status = f81534_setregister(serial->dev,
+					device_port, CLK_SEL_REGISTER, value);
+	if (status) {
+		dev_err(&port->dev,
+			"%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	if (baudrate <= 1200)
+		value = 0xc3;	/* 128 fifo & TL: 1x */
+	else
+		value = 0xcf;	/* 128 fifo & TL: 8x */
+
+
+	status = f81534_setregister(serial->dev, device_port,
+					    CONFIG1_REGISTER, value);
+	if (status) {
+		dev_err(&port->dev, "%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	if (baudrate <= 1200)
+		value = 0x01;	/* TL: 1 */
+	else if (baudrate >= 1152000)
+		value = 0x81;	/* TL: 8 */
+	else
+		value = 0xc1;	/* TL: 14 */
+
+	status = f81534_setregister(serial->dev, device_port,
+					    FIFO_CONTROL_REGISTER, value);
+	if (status) {
+		dev_err(&port->dev, "%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+	usb_control_msg(dev,
+			usb_sndctrlpipe(dev, 0),
+			F81534_SET_GET_REGISTER,
+			0x40,
+			LINE_CONTROL_REGISTER + port_number * 0x10,
+			0, &val, 1, F81534_USB_TIMEOUT);
+
+	dll = divisor[0] & 0xFF;
+	usb_control_msg(dev,
+			usb_sndctrlpipe(dev, 0),
+			F81534_SET_GET_REGISTER,
+			0x40,
+			DIVISOR_LATCH_LSB + port_number * 0x10,
+			0, &dll, 1, F81534_USB_TIMEOUT);
+
+	dlm = (divisor[0] >> 8) & 0xFF;
+	usb_control_msg(dev,
+			usb_sndctrlpipe(dev, 0),
+			F81534_SET_GET_REGISTER,
+			0x40,
+			DIVISOR_LATCH_MSB + port_number * 0x10,
+			0, &dlm, 1, F81534_USB_TIMEOUT);
+
+	usb_control_msg(dev,
+			usb_sndctrlpipe(dev, 0),
+			F81534_SET_GET_REGISTER,
+			0x40,
+			LINE_CONTROL_REGISTER + port_number * 0x10,
+			0, &lcr, 1, F81534_USB_TIMEOUT);
+
+	/* Enable all interrupts */
+	value = 0x0F;
+
+
+	status = f81534_setregister(dev, port_number,
+					INTERRUPT_ENABLE_REGISTER,
+					value);
+	if (status) {
+		dev_err(&port->dev, "%s: failed, %d\n", __func__, __LINE__);
+		return status;
+	}
+
+
+	return 0;
+}
+
+static int f81534_getregister(struct usb_device *dev,
+				     u8 uart, u16 reg, u8 *data)
+{
+	int count = F81534_USB_MAX_RETRY;
+	int status;
+
+	while (count--) {
+		status = usb_control_msg(dev,
+					 usb_rcvctrlpipe(dev, 0),
+					 F81534_SET_GET_REGISTER,
+					 0xc0,
+					 reg + uart * 0x10,
+					 0, data, sizeof(*data),
+					 F81534_USB_TIMEOUT);
+		if (status <= 0) {
+			if (status == 0)
+				status = -EIO;
+		} else {
+			break;
+		}
+	}
+
+	if ((count <= 0) && status) {
+		dev_err(&dev->dev,
+			"%s ERROR port_number:%d reg:%x status:%i failed\n",
+			__func__, uart, reg + uart * 0x10, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81534_update_mctrl(struct usb_serial_port *port,
+				      unsigned int set, unsigned int clear)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	u8 urb_value;
+	int status;
+
+	mutex_lock(&serial_priv->msr_mutex);
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+		dev_dbg(&dev->dev, "%s -DTR|RTS not being set|cleared\n",
+			__func__);
+		mutex_unlock(&serial_priv->msr_mutex);
+		return 0;	/* no change */
+	}
+
+	clear &= ~set;		/* 'set' takes precedence over 'clear' */
+	urb_value = 8 | port_priv->shadowMCR;
+
+	if (clear & TIOCM_DTR) {
+		urb_value &= ~UART_MCR_DTR;
+		dev_dbg(&dev->dev, "%s: port:%d clear DTR\n", __func__,
+			f81534_port_to_phy_index(port));
+	}
+
+	if (clear & TIOCM_RTS) {
+		urb_value &= ~UART_MCR_RTS;
+		dev_dbg(&dev->dev, "%s: port:%d clear RTS\n", __func__,
+			f81534_port_to_phy_index(port));
+
+	}
+
+	if (set & TIOCM_DTR) {
+		urb_value |= UART_MCR_DTR;
+		dev_dbg(&dev->dev, "%s: port:%d set DTR\n", __func__,
+			f81534_port_to_phy_index(port));
+
+	}
+
+	if (set & TIOCM_RTS) {
+		urb_value |= UART_MCR_RTS;
+		dev_dbg(&dev->dev, "%s: port:%d set RTS\n", __func__,
+			f81534_port_to_phy_index(port));
+	}
+
+	status = f81534_setregister(dev, f81534_port_to_phy_index(port),
+				    MODEM_CONTROL_REGISTER, urb_value);
+	if (status < 0) {
+		dev_err(&port->dev,
+			"%s- Error from MODEM_CTRL urb: %i\n",
+			__func__, status);
+
+		mutex_unlock(&serial_priv->msr_mutex);
+		return status;
+	}
+
+	port_priv->shadowMCR = urb_value;
+	mutex_unlock(&serial_priv->msr_mutex);
+
+	return 0;
+}
+
+static int f81534_calc_custom_idx(struct usb_serial *serial, u32 *index)
+{
+	int idx, status;
+	u8 custom_data;
+	int offset;
+
+	for (idx = F81534_CUSTOM_MAX_IDX - 1; idx >= 0; --idx) {
+		offset = F81534_CUSTOM_ADDRESS_START +
+				     F81534_CUSTOM_DATA_SIZE * idx;
+		status =
+		    f81534_read_data(serial, offset, 1,
+				&custom_data);
+		if (status) {
+			dev_err(&serial->dev->dev,
+				"%s: read error, idx:%d, status:%d\n", __func__,
+				idx, status);
+			return status;
+		}
+
+		/* need improve to bsearch */
+
+		/* if had custom setting, override
+		 * 1st byte is a indicater, 0xff is empty, 0x0f is had data
+		 */
+
+		if (custom_data != 0xff)	/* found */
+			break;
+	}
+
+	*index = idx;
+	return 0;
+}
+
+static int f81534_calc_num_ports(struct usb_serial *serial)
+{
+	struct f81534_serial_private *serial_priv = NULL;
+	int index;
+	u8 num_port = 0;
+	int status;
+	unsigned char reserve[F81534_CUSTOM_DATA_SIZE + 1];
+
+	serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
+	if (!serial_priv)
+		return 0;
+
+	usb_set_serial_data(serial, serial_priv);
+
+	/* oddy case for recovery bad usb */
+	if ((le16_to_cpu(serial->dev->descriptor.idProduct) == 0xffff) ||
+	    (le16_to_cpu(serial->dev->descriptor.idVendor) == 0xffff))
+		return 4;
+
+	/* check had custom setting */
+	status = f81534_calc_custom_idx(serial, &serial_priv->custom_idx);
+	if (status) {
+		dev_err(&serial->dev->dev,
+			"%s: f81534_calc_custom_idx read failed!!\n", __func__);
+		return 0;
+	}
+
+	/* read default board setting */
+	status = f81534_read_data(serial, F81534_RESERVE_ADDRESS_START,
+				  F81534_NUM_PORT, reserve);
+	if (status) {
+		dev_err(&serial->dev->dev,
+			"%s: f81534_read_data read failed!!\n", __func__);
+		return 0;
+	}
+
+	/* if had custom setting, override
+	 * 1st byte is a indicater, 0xff is empty, 0x0f is had data
+	 * skip with 1st data
+	 */
+
+	if (serial_priv->custom_idx != F81534_CUSTOM_NO_CUSTOM_DATA) {
+		status = f81534_read_data(serial,
+					  F81534_CUSTOM_ADDRESS_START +
+					  F81534_CUSTOM_DATA_SIZE *
+					  serial_priv->custom_idx + 1,
+					  sizeof(reserve), reserve);
+		if (status) {
+			dev_err(&serial->dev->dev,
+				"%s: get custom data failed!!\n", __func__);
+			return 0;
+		}
+
+		dev_info(&serial->dev->dev,
+			 "%s: read configure from block:%d\n", __func__,
+			 serial_priv->custom_idx);
+	} else
+		dev_info(&serial->dev->dev, "%s: read configure default\n",
+			 __func__);
+
+	for (index = 0; index < F81534_NUM_PORT; ++index) {
+		switch (reserve[index]) {
+		case 0x37:
+		case 0x38:
+		case 0x39:
+			num_port += 1;
+			break;
+		}
+	}
+
+	/* old style */
+	if (num_port) {
+		dev_dbg(&serial->dev->dev, "%s: old style wtih %d ports",
+			__func__, num_port);
+		return num_port;
+	}
+
+	/*new style, find all possible ports */
+	num_port = 0;
+	for (index = 0; index < F81534_NUM_PORT; ++index) {
+		if (reserve[index] & BIT(7))
+			continue;
+
+		num_port += 1;
+	}
+
+	if (num_port)
+		return num_port;
+
+	dev_err(&serial->dev->dev, "Read Failed!!, default 4 ports\n");
+	return 4;		/* nothing found */
+}
+
+static void f81534_set_termios(struct tty_struct *tty,
+			       struct usb_serial_port *port,
+			       struct ktermios *old_termios)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct f81534_port_private *port_priv;
+	u32 baud = 0;
+	u16 new_lcr = 0;
+	int status = 0;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	if (C_BAUD(tty) == B0)
+		f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+		f81534_update_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+
+	if (C_PARENB(tty)) {
+		new_lcr |= UART_LCR_PARITY;
+
+		if (!C_PARODD(tty))
+			new_lcr |= UART_LCR_EPAR;
+
+		if (C_CMSPAR(tty))
+			new_lcr |= UART_LCR_SPAR;
+	}
+
+	if (C_CSTOPB(tty))
+		new_lcr |= UART_LCR_STOP;
+
+	switch (C_CSIZE(tty)) {
+	case CS5:
+		new_lcr |= UART_LCR_WLEN5;
+		break;
+	case CS6:
+		new_lcr |= UART_LCR_WLEN6;
+		break;
+	case CS7:
+		new_lcr |= UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		new_lcr |= UART_LCR_WLEN8;
+		break;
+	}
+
+	baud = tty_get_baud_rate(tty);
+
+	if (baud) {
+		if ((baud == 1000000) || (baud > F81534_MAX_BAUDRATE)) {
+			if (old_termios)
+				baud = old_termios->c_ospeed;
+			else
+				baud = F81534_DEFAULT_BAUD_RATE;
+		}
+
+		dev_dbg(&dev->dev, "%s-baud: %d\n", __func__, baud);
+		tty_encode_baud_rate(tty, baud, baud);
+
+		port_priv->currentBaudRate = baud;
+	}
+
+	if (C_CRTSCTS(tty) && baud) {
+		dev_dbg(&dev->dev, "%s: port:%d CRTSCTS\n", __func__,
+			f81534_port_to_phy_index(port));
+		f81534_update_mctrl(port, TIOCM_RTS, 0);
+	}
+
+	port_priv->shadowLCR = new_lcr;
+	status =
+	    f81534_set_port_config(dev, f81534_port_to_phy_index(port), port,
+				   port_priv->currentBaudRate, new_lcr);
+	if (status < 0)
+		dev_err(&port->dev, "%s - f81534_set_port_config failed: %i\n",
+			__func__, status);
+
+}
+
+static int f81534_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	if (tty)
+		f81534_set_termios(tty, port, &tty->termios);
+
+	return 0;
+}
+
+static void f81534_close(struct usb_serial_port *port)
+{
+	/* nothing to do, a placeholder */
+}
+
+static void f81534_disconnect(struct usb_serial *serial)
+{
+	struct f81534_serial_private *serial_priv = usb_get_serial_data(serial);
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+	f81534_free_urbs(serial);
+	kfree(serial_priv);
+}
+
+static void f81534_release(struct usb_serial *serial)
+{
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+}
+
+static int f81534_get_serial_info(struct usb_serial_port *port,
+				  struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type = PORT_16550A;
+	tmp.line = port->minor;
+	tmp.port = port->port_number;
+	tmp.baud_base = 115200;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
+#define READ_AND_SET_NORMAL(dev, register, mask, value) \
+	({	\
+		int status_tmp = 0 ; \
+		do {	\
+			char *err_str = "%s - error: %x, status: %d\n"; \
+			u8 urb_value_tmp = 0;	\
+			status_tmp = f81534_get_normal_register(dev, \
+					register, &urb_value_tmp); \
+			if (status_tmp < 0) {	\
+				dev_err(&dev->dev, \
+						err_str, \
+						__func__,  \
+						register, \
+						status_tmp); \
+				break; \
+			}; \
+			\
+			if ((value) != 0) \
+				urb_value_tmp |= \
+						((1L << mask) & (value)) ; \
+			else \
+				urb_value_tmp &=  \
+						~(1L << mask); \
+			\
+			status_tmp = f81534_set_normal_register(dev, \
+					register, \
+					urb_value_tmp); \
+			\
+			if (status_tmp < 0) {	\
+				dev_err(&dev->dev, \
+						err_str, \
+						__func__, \
+						register, status_tmp); \
+				break; \
+			}; \
+		} while (0); \
+		status_tmp; \
+	})
+
+static int f81534_switch_gpio_mode(struct usb_serial_port
+				   *serial_port, int mode)
+{
+	int x = f81534_port_to_phy_index(serial_port);
+	int y = 0;
+	int status;
+	struct usb_device *dev = serial_port->serial->dev;
+	struct io_map_value *request_mode =
+	    f81534_mode_control[(mode >= eModeInvalid) ? eModeRS232 : mode];
+
+	struct pin_data *m1 = &request_mode->port[x].m1;
+	struct pin_data *m2 = &request_mode->port[x].m2;
+	struct pin_data *sd = &request_mode->port[x].sd;
+
+	struct pin_data *pins[3] = { m1, m2, sd };
+
+	if (mode >= 8)
+		return -EINVAL;
+
+	for (y = 0; y < 3; ++y) {
+		status = READ_AND_SET_NORMAL(dev,
+					pins[y]->port_io.reg_address,
+					pins[y]->port_io.reg_offset,
+					pins[y]->port_io.reg_bit ? 0xff : 0x00);
+		if (status) {
+			dev_err(&serial_port->dev,
+					"%s: failed, %d\n",
+					__func__, __LINE__);
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+#define READ_AND_SET(dev, port_num, register, mask, value) \
+	({	\
+		int status_tmp = 0 ; \
+		do {	\
+			char *err_str = "%s:reg:%x, status:%d failed\n"; \
+			u8 urb_value_tmp = 0;	\
+			status_tmp = f81534_getregister(dev, port_num \
+					, register, &urb_value_tmp); \
+			if (status_tmp < 0) {	\
+				dev_err(&dev->dev, \
+						err_str, \
+						__func__, \
+						register, status_tmp); \
+				break; \
+			}; \
+			\
+			if (value != 0) \
+				urb_value_tmp |= (u8) (mask & value) ; \
+			else \
+				urb_value_tmp &= (u8) ~(mask); \
+			\
+			status_tmp = f81534_setregister(dev, port_num, \
+					register, urb_value_tmp); \
+			\
+			if (status_tmp < 0) {	\
+				dev_err(&dev->dev, \
+						err_str, \
+						__func__, \
+						register, status_tmp); \
+				break; \
+			}; \
+		} while (0); \
+		status_tmp; \
+	})
+
+
+static int f81534_set_port_mode(struct usb_serial_port *port,
+		enum eUartMode eMode)
+{
+	int status = 0;
+	u8 urb_value = 0;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+	if (eMode > eModeInvalid)
+		return -1;
+
+	if (eMode != eModeInvalid) {
+		int data = 1;
+
+		status = f81534_getregister(port->serial->dev,
+					f81534_port_to_phy_index(port),
+					CLK_SEL_REGISTER,
+					&urb_value);
+		if (status) {
+			dev_err(&port->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+		urb_value &= ~(data << 4);
+		urb_value &= ~(data << 5);
+
+		switch (port_priv->port_pin_data.eForceUartMode) {
+		case eModeRS232:
+		case eModeShutdown:
+		case eModeRS232_coexist:
+			break;
+
+		case eModeRS485:
+			urb_value |= (data << 4);
+			urb_value |= (data << 5);
+			dev_dbg(&port->dev, "%s: eModeRS485 urb:%x\n", __func__,
+				urb_value);
+			break;
+
+		default:
+			urb_value |= (data << 4);
+			dev_dbg(&port->dev, "%s others urb:%x\n", __func__,
+				urb_value);
+			break;
+
+		}
+
+		status = f81534_setregister(port->serial->dev,
+					f81534_port_to_phy_index(port),
+					CLK_SEL_REGISTER,
+					urb_value);
+		if (status) {
+			dev_err(&port->dev,
+					"%s: failed, %d\n", __func__, __LINE__);
+			return status;
+		}
+
+	}
+
+	port_priv->port_pin_data.eForceUartMode = eMode;
+	return 0;
+}
+
+static int f81534_ioctl_set_rs485(struct usb_serial_port *port,
+			   struct serial_rs485 __user *arg)
+{
+
+	struct serial_rs485 data;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+	struct usb_device *usb_dev = port->serial->dev;
+	u16 device_port = f81534_port_to_phy_index(port);
+	int status;
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&port->dev, "%s: interrupted!\n", __func__);
+		return status;
+	}
+
+	status = copy_from_user(&data, (struct serial_rs485 __user *)arg,
+				sizeof(data));
+	if (status) {
+		status = -EFAULT;
+		goto finish;
+	}
+
+	if (data.flags & SER_RS485_ENABLED) {
+		if (data.flags & SER_RS485_RTS_ON_SEND) {
+			dev_dbg(&port->dev, "%s: eModeRS485_1\n", __func__);
+			port_priv->port_pin_data.eForceUartMode = eModeRS485_1;
+		} else {
+			dev_dbg(&port->dev, "%s: eModeRS485\n", __func__);
+			port_priv->port_pin_data.eForceUartMode = eModeRS485;
+		}
+	} else {
+		dev_dbg(&port->dev, "%s: eModeRS232\n", __func__);
+		port_priv->port_pin_data.eForceUartMode = eModeRS232;
+	}
+
+	status = f81534_set_port_config(usb_dev, device_port, port,
+					port_priv->currentBaudRate,
+					port_priv->shadowLCR);
+	if (status) {
+		dev_err(&usb_dev->dev, "%s: set port error!!\n", __func__);
+		goto finish;
+	}
+
+	status = f81534_save_configure_data(port);
+
+finish:
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(port->serial);
+
+	return status;
+}
+
+static int f81534_ioctl_get_rs485(struct usb_serial_port *port,
+			   struct serial_rs485 __user *arg)
+{
+	int status = 0;
+	struct serial_rs485 data;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&port->dev, "%s: interrupted!\n", __func__);
+		return status;
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	switch (port_priv->port_pin_data.eForceUartMode) {
+	case eModeRS485:
+		dev_dbg(&port->dev, "%s: eModeRS485\n", __func__);
+		data.flags = SER_RS485_ENABLED;
+		break;
+	case eModeRS485_1:
+		dev_dbg(&port->dev, "%s: eModeRS485_1\n", __func__);
+		data.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;
+		break;
+	default:
+		dev_dbg(&port->dev, "%s: eModeRS232\n", __func__);
+		break;
+	}
+
+	if (copy_to_user((struct serial_rs485 *)arg,
+			 &data, sizeof(struct serial_rs485)))
+		status = -EFAULT;
+
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(port->serial);
+
+	return status;
+}
+
+static int f81534_ioctl(struct tty_struct *tty, unsigned int cmd,
+			unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGRS485:
+		return f81534_ioctl_get_rs485(port,
+					      (struct serial_rs485 __user *)
+					      arg);
+
+	case TIOCSRS485:
+		return f81534_ioctl_set_rs485(port,
+					      (struct serial_rs485 __user *)
+					      arg);
+
+	case TIOCGSERIAL:
+		return f81534_get_serial_info(port,
+					      (struct serial_struct __user *)
+					      arg);
+
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static void f81534_process_read_urb(struct urb *urb)
+{
+	struct usb_serial *serial;
+	struct f81534_serial_private *serial_priv;
+	struct usb_serial_port *port = NULL;
+	struct f81534_port_private *port_priv = NULL;
+	unsigned char *ch;
+	u8 lsr = 0;
+	int i, j;
+	int len = urb->actual_length;
+	int datalen = 0;
+	int tty_port_num = 0;
+	int phy_port_num = 0;
+	int status;
+	bool msr_avail;
+	u8 *msr;
+	struct tty_struct *tty;
+
+	if (!len)
+		return;
+
+	ch = urb->transfer_buffer;
+	serial = urb->context;
+	serial_priv = usb_get_serial_data(serial);
+
+	for (i = 0; i < urb->actual_length; i++) {
+		ch = (unsigned char *)urb->transfer_buffer + i;
+		j = i / 128;
+		if (i == (1 + j * 128)) {
+			msr_avail = true;
+			msr = (unsigned char *)(ch + 2);
+
+			if (*ch == 0x03) {	/* tx empty */
+				spin_lock(&serial_priv->write_urb_lock);
+				serial_priv->phy_port_in_use[phy_port_num] =
+				    false;
+				spin_unlock(&serial_priv->write_urb_lock);
+
+				usb_serial_port_softint(port);
+				i = i + 126;
+
+			} else if (*ch == 0x01) {	/* 0x01 read */
+				tty = tty_port_tty_get(&port->port);
+				if (tty)
+					tty_kref_put(tty);
+				else
+					i += 126;	/*skip packet */
+
+			} else if (*ch == 0x04) {	/*  msr changed */
+				i += 126;	/* direct drop this packet */
+
+				dev_dbg(&port->dev,
+					"%s- MSR Change (token: 0x04), current:%02x, changed to: %02x\n",
+					__func__,
+					serial_priv->shadowMSR[phy_port_num],
+					*msr);
+			} else {
+				i += 126;	/* direct drop this packet */
+				dev_err(&port->dev, "%s %d known cmd\n",
+					__func__, __LINE__);
+				msr_avail = false;
+			}
+
+			if (msr_avail) {
+				u8 oldmsr = 0;
+
+				spin_lock(&serial_priv->msr_lock);
+				oldmsr = serial_priv->shadowMSR[phy_port_num];
+				serial_priv->shadowMSR[phy_port_num] = *msr;
+				serial_priv->msr_time[phy_port_num] = jiffies;
+				spin_unlock(&serial_priv->msr_lock);
+
+				if (((oldmsr & 0xf0) ^ (*msr & 0xf0)) &
+					UART_MSR_DCD) {
+
+					tty = tty_port_tty_get(&port->port);
+					if (tty) {
+						usb_serial_handle_dcd_change
+						    (port, port->port.tty,
+						     (*msr) & UART_MSR_DCD);
+						tty_kref_put(tty);
+					}
+
+					dev_dbg(&port->dev,
+						"%s: DCD Changed: port %d from %x to %x, tty state: %d\n",
+						__func__, phy_port_num, oldmsr,
+						*msr, port->port.tty ? 1 : 0);
+				}
+
+				f81534_update_msr(port, msr);
+			}
+
+			continue;
+		}
+
+		if (i == (j * 128)) {
+			phy_port_num = *ch;
+			tty_port_num =
+			    f81534_phy_to_logic_port(serial, phy_port_num);
+			port = serial->port[tty_port_num];
+			port_priv = usb_get_serial_port_data(port);
+			lsr = 0;
+			continue;
+		}
+
+		if (i == (2 + j * 128)) {
+			datalen = *ch / 2;
+			continue;
+		}
+
+		if (i < (4 + j * 128))
+			continue;
+
+		if (((i % 2) == 1) && (i > (4 + j * 128))) {
+			lsr |= *ch;
+			continue;
+		}
+
+		if (datalen == 0) {
+			i = (j + 1) * 128 - 1;
+
+			tty = tty_port_tty_get(&port->port);
+
+			if (!tty)
+				continue;
+
+			if (UART_LSR_OE & lsr)
+				tty_insert_flip_char(&port->port, 0,
+						     TTY_OVERRUN);
+
+			tty_flip_buffer_push(&port->port);
+
+			if ((i + 1) == urb->actual_length)
+				i++;
+
+			if (port_priv && (lsr & UART_LSR_BRK_ERROR_BITS)) {
+				dev_info(&port->dev, "phy_port_num: %d, lsr: %x\n",
+				       phy_port_num, lsr);
+				f81534_update_lsr(port, &lsr);
+			}
+
+			tty_kref_put(tty);
+
+			continue;
+		}
+
+		tty_buffer_request_room(&port->port, 1);
+		tty_insert_flip_string(&port->port, ch, 1);
+		datalen--;
+	}
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status != 0) {
+		dev_err(&serial->dev->dev,
+			"%s - resubmit read urb failed: %i\n",
+			__func__, status);
+	}
+
+}
+
+static void f81534_write_bulk_callback(struct urb *urb)
+{
+}
+
+static void f81534_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial *serial = urb->context;
+	struct f81534_serial_private *serial_priv;
+
+	serial_priv = usb_get_serial_data(serial);
+
+	if (urb->status)
+		return;
+
+	f81534_process_read_urb(urb);
+}
+
+static int f81534_free_urbs(struct usb_serial *serial)
+{
+	struct f81534_serial_private *serial_priv = usb_get_serial_data(serial);
+	int i;
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		if (serial_priv->read_urb[i]) {
+			usb_kill_urb(serial_priv->read_urb[i]);
+			usb_free_urb(serial_priv->read_urb[i]);
+			serial_priv->read_urb[i] = NULL;
+		}
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		if (serial_priv->write_urb[i]) {
+			usb_kill_urb(serial_priv->write_urb[i]);
+			usb_free_urb(serial_priv->write_urb[i]);
+			serial_priv->write_urb[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int f81534_setup_urbs(struct usb_serial *serial)
+{
+	struct usb_serial_port *port0;
+	struct f81534_serial_private *serial_priv;
+	int status;
+	int i = 0;
+	u8 ep0_in_address;
+	void *buf = NULL;
+
+	port0 = serial->port[0];
+	serial_priv = usb_get_serial_data(serial);
+	ep0_in_address = port0->bulk_in_endpointAddress;
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		serial_priv->read_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!serial_priv->read_urb[i]) {
+			dev_err(&serial->dev->dev, "No free urbs available\n");
+			status = -ENOMEM;
+			goto failed;
+		}
+
+		buf = serial_priv->read_buffer[i];
+
+		usb_fill_bulk_urb(serial_priv->read_urb[i], serial->dev,
+				  usb_rcvbulkpipe(serial->dev,
+						  ep0_in_address),
+				  buf, sizeof(serial_priv->read_buffer[i]),
+				  f81534_read_bulk_callback, serial);
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		status = usb_submit_urb(serial_priv->read_urb[i], GFP_KERNEL);
+		if (status != 0)
+			goto failed;
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		serial_priv->write_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!serial_priv->write_urb[i]) {
+			status = -ENOMEM;
+			goto failed;
+		}
+
+		buf = serial_priv->write_buffer[i];
+
+		usb_fill_bulk_urb(serial_priv->write_urb[i], serial->dev,
+				  usb_sndbulkpipe(serial->dev,
+						  port0->
+						  bulk_out_endpointAddress),
+				  buf, sizeof(serial_priv->write_buffer[i]),
+				  f81534_write_bulk_callback, serial);
+	}
+
+	return 0;
+
+failed:
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		if (serial_priv->read_urb[i]) {
+			usb_kill_urb(serial_priv->read_urb[i]);
+			usb_free_urb(serial_priv->read_urb[i]);
+		}
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i)
+		usb_free_urb(serial_priv->write_urb[i]);
+
+	return status;
+}
+
+static int f81534_save_configure_data(struct usb_serial_port *port)
+{
+	int status;
+	int count;
+	int phy;
+	int gpio_address, uart_address;
+	int offset;
+	bool reConfigure = false;
+	u8 uart_mode, gpio_mode;
+	u8 data[F81534_RESERVE_SIZE + 1];
+	u8 tmp[F81534_RESERVE_SIZE];
+
+	struct usb_serial *serial = port->serial;
+	struct f81534_port_private *port_priv;
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	/* compare mem with ic data */
+	for (count = 0; count < serial->num_ports; ++count) {
+		port_priv = usb_get_serial_port_data(serial->port[count]);
+		phy = f81534_logic_to_phy_port(serial, count);
+
+		if (!port_priv) {
+			dev_info(&port->dev,
+				 "%s: port_priv:0 something problem here phy:%d!!\n",
+				 __func__, phy);
+			continue;
+		}
+
+		if (serial_priv->custom_idx == F81534_CUSTOM_NO_CUSTOM_DATA) {
+			uart_address = F81534_RESERVE_ADDRESS_START + phy;
+			gpio_address = F81534_RESERVE_ADDRESS_START + phy + 4;
+		} else {
+			/* if had custom setting, override
+			 * 1st byte is a indicater, 0xff is empty,
+			 * 0x0f is had data. Skip with 1st data
+			 */
+
+			uart_address = F81534_CUSTOM_ADDRESS_START +
+			    serial_priv->custom_idx * F81534_CUSTOM_DATA_SIZE+
+			    phy + 1;
+
+			gpio_address = F81534_CUSTOM_ADDRESS_START +
+			    serial_priv->custom_idx * F81534_CUSTOM_DATA_SIZE +
+			    phy + 4 + 1;
+		}
+
+		status = f81534_read_data(port->serial, uart_address,
+					  1, &uart_mode);
+		if (status) {
+			dev_err(&port->dev,
+				"%s: read uart configure data failed: index:%x, status:%d\n",
+				__func__, uart_address, status);
+			return status;
+		}
+
+		status = f81534_read_data(port->serial, gpio_address,
+					  1, &gpio_mode);
+		if (status) {
+			dev_err(&port->dev,
+				"%s: read gpio configure data failed: index:%x, status:%d\n",
+				__func__, gpio_address, status);
+			return status;
+		}
+
+		if (port_priv->port_pin_data.eGPIOMode != gpio_mode)
+			reConfigure = true;
+
+		/* check uart flag */
+		if (port_priv->port_pin_data.eForceUartMode == eModeRS232) {
+			if ((uart_mode & 0x03) != F81534_RS232_FLAG)
+				reConfigure = true;
+		} else if (port_priv->port_pin_data.eForceUartMode ==
+			   eModeRS485_1) {
+			if ((uart_mode & 0x03) != F81534_RS485_1_FLAG)
+				reConfigure = true;
+		} else if (port_priv->port_pin_data.eForceUartMode ==
+			   eModeRS485) {
+			if ((uart_mode & 0x03) != F81534_RS485_FLAG)
+				reConfigure = true;
+		} else
+			reConfigure = true;
+
+		if (reConfigure)
+			break;
+	}
+
+	if (!reConfigure) {
+		dev_info(&serial->dev->dev, "%s: update-to-date\n", __func__);
+		return 0;
+	}
+
+	dev_info(&serial->dev->dev, "%s: updating\n", __func__);
+
+	/* next setting block */
+	serial_priv->custom_idx =
+	    (serial_priv->custom_idx + 1) % F81534_CUSTOM_MAX_IDX;
+	dev_info(&serial->dev->dev, "%s: saving to block index:%d\n", __func__,
+		 serial_priv->custom_idx);
+
+	/* erase when start block is 0 */
+	if (!serial_priv->custom_idx) {
+		dev_dbg(&serial->dev->dev, "%s: need erase\n", __func__);
+
+		/* erase */
+		status = f81534_erase_sector(serial,
+				F81534_CUSTOM_ADDRESS_START);
+		if (status) {
+			dev_err(&port->dev,
+				"%s: f81534_erase_sector failed!! status:%d\n",
+				__func__, status);
+			return status;
+		}
+	} else {
+		dev_dbg(&serial->dev->dev, "%s: dont need erase\n", __func__);
+	}
+
+	/* reprogram */
+
+	for (count = 0; count < serial->num_ports; ++count) {
+		port_priv = usb_get_serial_port_data(serial->port[count]);
+		phy = f81534_logic_to_phy_port(serial, count);
+
+		gpio_mode = port_priv->port_pin_data.eGPIOMode;
+		serial_priv->reserve_data[phy + 4] = gpio_mode;
+		serial_priv->reserve_data[phy + 0] &= ~(0x03);
+
+		/* check uart flag */
+		if (port_priv->port_pin_data.eForceUartMode == eModeRS232) {
+			serial_priv->reserve_data[phy + 0] |=
+					F81534_RS232_FLAG;
+		} else if (port_priv->port_pin_data.eForceUartMode ==
+			   eModeRS485_1) {
+			serial_priv->reserve_data[phy + 0] |=
+					F81534_RS485_1_FLAG;
+		} else if (port_priv->port_pin_data.eForceUartMode ==
+			   eModeRS485) {
+			serial_priv->reserve_data[phy + 0] |=
+					F81534_RS485_FLAG;
+		} else {
+			dev_err(&serial->dev->dev,
+				"%s: write configure error!! eForceUartMode:%d\n",
+				__func__,
+				port_priv->port_pin_data.eForceUartMode);
+		}
+
+		dev_info(&serial->dev->dev,
+			 "%s: port:%d uart_mode:%x, gpio_mode:%x\n", __func__,
+			 count, serial_priv->reserve_data[phy + 0], gpio_mode);
+	}
+
+	/* 1st byte is a indicater, 0xff is empty, 0x0f is had data
+	 * only write 8 bytes of total 4 port uart & gpio mode
+	 * so we need write 1+8 data
+	 */
+
+	data[0] = 0x0f;
+	memcpy(&data[1], serial_priv->reserve_data,
+			F81534_RESERVE_SIZE);
+
+	offset = F81534_CUSTOM_ADDRESS_START +
+			F81534_CUSTOM_DATA_SIZE * serial_priv->custom_idx;
+
+	status = f81534_write_data(serial, offset, sizeof(data), data);
+	if (status) {
+		dev_err(&port->dev,
+			"%s: f81534_write_data failed!! status:%d\n", __func__,
+			status);
+		return status;
+	}
+
+	/* check save & memory data */
+	do {
+		memset(tmp, 0, sizeof(tmp));
+
+		status = f81534_read_data(serial,
+					  F81534_CUSTOM_ADDRESS_START +
+					  F81534_CUSTOM_DATA_SIZE *
+					  serial_priv->custom_idx + 1,
+					  sizeof(tmp), tmp);
+		if (status) {
+			dev_err(&port->dev,
+				"%s: f81534_read_data failed!! status:%d\n",
+				__func__, status);
+			return status;
+		}
+
+		for (count = 0; count < 8; ++count) {
+			if (tmp[count] != serial_priv->reserve_data[count]) {
+				dev_err(&port->dev,
+					"%s:check data error, count:%d, data:%x %x\n",
+					__func__, count, tmp[count],
+					serial_priv->reserve_data[count]);
+			}
+		}
+
+	} while (0);
+
+	dev_dbg(&serial->dev->dev, "%s: complete\n", __func__);
+
+	return 0;
+}
+
+static int f81534_load_configure_data(struct usb_serial_port *port)
+{
+	int status;
+	unsigned char uart_flag, gpio_mode;
+	int device_port = f81534_port_to_phy_index(port);
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	uart_flag = serial_priv->reserve_data[device_port];
+	gpio_mode = serial_priv->reserve_data[device_port + 4];
+
+	switch (uart_flag) {
+	case 0x37:
+	case 0x38:
+	case 0x39:
+		serial_priv->reserve_data[device_port] = F81534_RS232_FLAG;
+		gpio_mode = serial_priv->reserve_data[device_port + 4] =
+		    eModeRS232;
+		port_priv->port_pin_data.eForceUartMode = eModeRS232;
+		port_priv->port_pin_data.eGPIOMode = eModeRS232;
+		dev_info(&port->dev,
+			 "transceiver field with old style, upgrading\n");
+		break;
+
+	default:
+		if (uart_flag & BIT(0)) {	/* rs485 */
+			if (uart_flag & BIT(1))	/* Inv */
+				port_priv->port_pin_data.eForceUartMode =
+				    eModeRS485;
+			else
+				port_priv->port_pin_data.eForceUartMode =
+				    eModeRS485_1;
+		} else
+			port_priv->port_pin_data.eForceUartMode = eModeRS232;
+
+		break;
+	}
+
+	if ((gpio_mode >= 0) && (gpio_mode < 8)) {
+		port_priv->port_pin_data.eGPIOMode = gpio_mode;
+		dev_dbg(&port->dev, "gpio set to %d\n", gpio_mode);
+	} else {
+		port_priv->port_pin_data.eGPIOMode = eModeRS232;
+		dev_info(&port->dev, "unknown gpio %d, setting to %d\n",
+			 gpio_mode, eModeRS232);
+	}
+
+	status =
+	    f81534_switch_gpio_mode(port, port_priv->port_pin_data.eGPIOMode);
+	if (status) {
+		dev_err(&port->dev,
+			"%s: f81534_switch_gpio_mode failed!! status:%d\n",
+			__func__, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static void dump_configure(struct usb_serial *serial)
+{
+	unsigned char transceiver, mode;
+	int count;
+	int index;
+	int gpio_address, uart_address;
+	struct f81534_serial_private *serial_priv = usb_get_serial_data(serial);
+
+	index = serial_priv->custom_idx;
+
+	for (count = 0; count < 4; ++count) {
+		if (index == F81534_CUSTOM_NO_CUSTOM_DATA) {
+			uart_address = F81534_RESERVE_ADDRESS_START + count;
+			gpio_address = F81534_RESERVE_ADDRESS_START + count + 4;
+		} else {
+
+			/* if had custom setting, override
+			 * 1st byte is a indicater.
+			 * 0xff is empty, 0x0f is had data.
+			 * read and skip with 1st data.
+			 */
+
+			uart_address = F81534_CUSTOM_ADDRESS_START +
+			    F81534_CUSTOM_DATA_SIZE * index + count + 1;
+
+			gpio_address = F81534_CUSTOM_ADDRESS_START +
+			    F81534_CUSTOM_DATA_SIZE * index + count + 4 + 1;
+		}
+
+		f81534_read_data(serial, uart_address, 1, &transceiver);
+		f81534_read_data(serial, gpio_address, 1, &mode);
+
+		dev_info(&serial->dev->dev,
+			 "%s: port:%d uart_flag:%x gpio:%x\n", __func__,
+			 count, transceiver, mode);
+	}
+}
+
+static int f81534_attach(struct usb_serial *serial)
+{
+	struct f81534_serial_private *serial_priv = usb_get_serial_data(serial);
+	int status = 0;
+	int i;
+	int offset;
+	int num_port = serial->num_ports;
+
+	serial_priv->serial = serial;
+	memset(serial_priv->port_mapping, F81534_UNUSED_PORT,
+	       sizeof(serial_priv->port_mapping));
+
+	switch (num_port) {
+	case eIC_F81532:
+	case eIC_F81534:
+		serial_priv->ic_type = num_port;
+		dev_info(&serial->dev->dev, "%s detected\n",
+			 m_ic_name[num_port]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i)
+		serial_priv->port_mapping[i] = i;
+
+	switch (num_port) {
+	case 4:
+		break;
+	case 2:
+		serial_priv->port_mapping[1] = 3;
+		serial_priv->port_mapping[2] = F81534_UNUSED_PORT;
+		serial_priv->port_mapping[3] = F81534_UNUSED_PORT;
+		break;
+	case 1:
+		serial_priv->port_mapping[1] = F81534_UNUSED_PORT;
+		serial_priv->port_mapping[2] = F81534_UNUSED_PORT;
+		serial_priv->port_mapping[3] = F81534_UNUSED_PORT;
+		break;
+	default:
+		dev_err(&serial->dev->dev,
+			"%s: Cant determine ports: %d, error!!!\n", __func__,
+			num_port);
+		status = -EINVAL;
+		goto failed;
+	}
+
+	for (i = 0; i < F81534_NUM_PORT; ++i) {
+		/* clear fifo when plug in */
+		f81534_setregister(serial->dev, i, FIFO_CONTROL_REGISTER, 0xc7);
+
+		status = f81534_get_normal_register(serial->dev, 0x5a00 + i,
+						    &serial_priv->shadowMSR[i]);
+		if (status) {
+			dev_err(&serial->dev->dev,
+				"%s f81534_get_normal_register:%x failed\n",
+				__func__, 0x5a00 + i);
+			goto failed;
+		}
+
+		serial_priv->msr_time[i] = jiffies;
+	}
+
+	spin_lock_init(&serial_priv->write_urb_lock);
+	spin_lock_init(&serial_priv->msr_lock);
+	mutex_init(&serial_priv->msr_mutex);
+	mutex_init(&serial_priv->updating_mutex);
+
+	status = f81534_setup_urbs(serial);
+	if (status != 0)
+		goto failed;
+
+	status = f81534_read_data(serial,
+				F81534_RESERVE_ADDRESS_START,
+				F81534_RESERVE_SIZE,
+				serial_priv->reserve_data);
+	if (status) {
+		dev_err(&serial->dev->dev, "%s read reserve data failed\n",
+			__func__);
+		goto failed;
+	}
+
+	/* if had custom setting, override
+	 * 1st byte is a indicater, 0xff is empty, 0x0f is had data
+	 * skip with 1st data
+	 */
+
+	if (serial_priv->custom_idx == F81534_CUSTOM_NO_CUSTOM_DATA)
+		return 0;
+
+	offset = F81534_CUSTOM_ADDRESS_START +
+			F81534_CUSTOM_DATA_SIZE * serial_priv->custom_idx + 1;
+	/* only read 8 bytes for mode & GPIO */
+	status = f81534_read_data(serial,
+			offset,
+			sizeof(serial_priv->reserve_data),
+			serial_priv->reserve_data);
+	if (status) {
+		dev_err(&serial->dev->dev,
+			"%s: get custom data failed, idx:%d, status:%d!!\n",
+			__func__, serial_priv->custom_idx, status);
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	kfree(serial_priv);
+	return status;
+}
+
+static ssize_t uart_mode_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	int status = 0;
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&port->dev, "%s: interrupted!\n", __func__);
+		return status;
+	}
+
+	switch (port_priv->port_pin_data.eForceUartMode) {
+	case eModeRS232:
+		status = sprintf(buf, "eModeRS232\n");
+		break;
+	case eModeRS485:
+		status = sprintf(buf, "eModeRS485\n");
+		break;
+	case eModeRS485_1:
+		status = sprintf(buf, "eModeRS485_1\n");
+		break;
+	default:
+		status = -EINVAL;
+		dev_err(dev, "%s error!!\n", __func__);
+		break;
+	}
+
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(port->serial);
+
+	return status;
+}
+
+static ssize_t uart_mode_store(struct device *dev,
+			       struct device_attribute *attr, const char *buf,
+			       size_t count)
+{
+	struct usb_serial_port *serial_port = to_usb_serial_port(dev);
+	struct f81534_port_private *port_priv =
+	    usb_get_serial_port_data(serial_port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(serial_port->serial);
+	struct usb_device *usb_dev = serial_port->serial->dev;
+	u16 device_port = f81534_port_to_phy_index(serial_port);
+	int index = 0;
+	int status;
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&serial_port->dev, "%s: interrupted!\n", __func__);
+		return status;
+	}
+
+	if (!count) {
+		dev_err(dev, "%s: count error\n", __func__);
+		status = -EINVAL;
+		goto finish;
+	}
+
+	index = buf[0] - '0';
+
+	if ((index < eModeRS232) || (index > eModeRS485_1)) {
+		status = -EINVAL;
+		goto finish;
+	}
+
+	status = f81534_set_port_config(usb_dev, device_port, serial_port,
+					port_priv->currentBaudRate,
+					port_priv->shadowLCR);
+	if (status) {
+		dev_err(dev, "%s: set port error!!\n", __func__);
+		goto finish;
+	}
+
+	status = f81534_save_configure_data(serial_port);
+	if (status) {
+		dev_err(dev, "%s:save configure error!!\n", __func__);
+		goto finish;
+	}
+
+	port_priv->port_pin_data.eForceUartMode = index;
+	status = count;
+
+finish:
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(serial_port->serial);
+	return status;
+}
+
+static ssize_t gpio_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	int status = 0;
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&port->dev, "%s: interrupted!\n", __func__);
+		return status;
+	}
+
+	switch (port_priv->port_pin_data.eGPIOMode) {
+	case eModeRS232:
+		status = sprintf(buf, "001\n");
+		break;
+	case eModeRS485:
+		status = sprintf(buf, "010\n");
+		break;
+	case eModeRS485_1:
+		status = sprintf(buf, "011\n");
+		break;
+	case eModeRS422:
+		status = sprintf(buf, "000\n");
+		break;
+	case eModeRS422_term:
+		status = sprintf(buf, "100\n");
+		break;
+	case eModeRS232_coexist:
+		status = sprintf(buf, "101\n");
+		break;
+	case eModeRS485_1_term:
+		status = sprintf(buf, "110\n");
+		break;
+	case eModeShutdown:
+		status = sprintf(buf, "111\n");
+		break;
+	default:
+		status = -EINVAL;
+		dev_err(dev, "%s error!!\n", __func__);
+		break;
+	}
+
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(port->serial);
+
+	return status;
+}
+
+static ssize_t gpio_store(struct device *dev,
+			  struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	struct usb_serial_port *serial_port = to_usb_serial_port(dev);
+	struct f81534_port_private *port_priv =
+	    usb_get_serial_port_data(serial_port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(serial_port->serial);
+	int index = 0;
+	int status = 0;
+
+	status = mutex_lock_interruptible(&serial_priv->updating_mutex);
+	if (status) {
+		dev_info(&serial_port->dev, "%s: interrupted!\n", __func__);
+		goto finish;
+	}
+
+	if (!count) {
+		dev_err(dev, "%s: count error\n", __func__);
+		status = -EINVAL;
+		goto finish;
+	}
+
+	index = buf[0] - '0';
+
+	if (index > eModeShutdown) {
+		status = -EINVAL;
+		goto finish;
+	}
+
+	status = f81534_switch_gpio_mode(serial_port, index);
+	if (status) {
+		dev_err(dev, "%s: set gpio error!!\n", __func__);
+		goto finish;
+	}
+
+	port_priv->port_pin_data.eGPIOMode = index;
+
+	status = f81534_save_configure_data(serial_port);
+	if (status)
+		goto finish;
+
+	status = count;
+
+finish:
+	mutex_unlock(&serial_priv->updating_mutex);
+	f81534_wakeup_all_port(serial_port->serial);
+
+	return status;
+}
+
+static DEVICE_ATTR_RW(uart_mode);
+static DEVICE_ATTR_RW(gpio);
+
+static int f81534_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct f81534_port_private *port_priv = NULL;
+	int status = 0;
+
+	status |= device_create_file(&port->dev, &dev_attr_uart_mode);
+	status |= device_create_file(&port->dev, &dev_attr_gpio);
+
+	dev_dbg(&port->dev, "%s f81534_port_to_phy_index(port): %d",
+		__func__, f81534_port_to_phy_index(port));
+
+	if (status)
+		return -EPERM;
+
+	port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+
+	if (!port_priv)
+		return -ENOMEM;
+
+	usb_set_serial_port_data(port, port_priv);
+	spin_lock_init(&port_priv->lock);
+
+	port_priv->port = port;
+
+	status = f81534_load_configure_data(port);
+	if (status)
+		return status;
+
+	if ((serial->num_ports - 1) == f81534_port_index(port)) {
+		f81534_save_configure_data(port);
+		dump_configure(serial);
+	}
+
+	status = f81534_set_port_mode(port,
+				      port_priv->port_pin_data.eForceUartMode);
+	if (status < 0) {
+		dev_err(&port->dev, "%s - initial setup failed (%i)\n",
+			__func__, f81534_port_to_phy_index(port));
+		goto port_fail;
+	}
+	return 0;
+
+port_fail:
+
+	kfree(port_priv);
+	return status;
+}
+
+static int f81534_port_remove(struct usb_serial_port *port)
+{
+	struct f81534_port_private *port_priv;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	device_remove_file(&port->dev, &dev_attr_uart_mode);
+	device_remove_file(&port->dev, &dev_attr_gpio);
+
+	port_priv = usb_get_serial_port_data(port);
+	kfree(port_priv);
+
+	return 0;
+}
+
+static int f81534_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+
+	int r = 0;
+	int index = f81534_port_to_phy_index(port);
+	int count = 5, result;
+	unsigned long current_jiffies = jiffies + 1;
+	unsigned long flags = 0;
+	u8 msr = 0;
+
+	/* to try: read MSR again here? */
+
+	while (count--) {
+		mutex_lock(&serial_priv->msr_mutex);
+		spin_lock_irqsave(&serial_priv->msr_lock, flags);
+		result =
+		    time_after(current_jiffies, serial_priv->msr_time[index]);
+		msr = serial_priv->shadowMSR[index];
+		spin_unlock_irqrestore(&serial_priv->msr_lock, flags);
+		mutex_unlock(&serial_priv->msr_mutex);
+
+		if (!result)
+			break;
+
+		/* wait for delayed MSR change from bulk-in */
+		if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
+			dev_info(&port->dev, "%s: breaked !!\n", __func__);
+			break;
+		}
+	}
+
+	r = (port_priv->shadowMCR & UART_MCR_DTR ? TIOCM_DTR : 0) |
+	    (port_priv->shadowMCR & UART_MCR_RTS ? TIOCM_RTS : 0) |
+	    (msr & UART_MSR_CTS ? TIOCM_CTS : 0) |
+	    (msr & UART_MSR_DCD ? TIOCM_CAR : 0) |
+	    (msr & UART_MSR_RI ? TIOCM_RI : 0) |
+	    (msr & UART_MSR_DSR ? TIOCM_DSR : 0);
+
+	return r;
+}
+
+static int f81534_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear)
+{
+	struct f81534_port_private *port_priv;
+
+	port_priv = usb_get_serial_port_data(tty->driver_data);
+
+	dev_dbg(&port_priv->port->dev, "%s set:%x, clear:%x\n",
+		__func__, set, clear);
+
+	return f81534_update_mctrl(port_priv->port, set, clear);
+}
+
+static void f81534_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct f81534_port_private *port_priv;
+	u16 val;
+
+	port_priv = usb_get_serial_port_data(port);
+	val = (break_state == -1) ? 1 : 0;
+}
+
+static void f81534_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_device *dev = port->serial->dev;
+
+	if (!on) {
+		if (f81534_setregister(dev, f81534_port_to_phy_index(port),
+				       MODEM_CONTROL_REGISTER, 8) < 0) {
+			dev_err(&port->dev, "%s-error from flowcontrol urb\n",
+				__func__);
+			return;
+		}
+	}
+
+	/* drop RTS and DTR */
+	if (on)
+		f81534_update_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+	else
+		f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+static void f81534_update_msr(struct usb_serial_port *port, unsigned char *ch)
+{
+	u8 newMSR = (u8) *ch;
+
+	if (newMSR & UART_MSR_ANY_DELTA) {
+		/* update input line counters */
+		if (newMSR & UART_MSR_DCTS)
+			port->icount.cts++;
+		if (newMSR & UART_MSR_DDSR)
+			port->icount.dsr++;
+		if (newMSR & UART_MSR_DDCD)
+			port->icount.dcd++;
+		if (newMSR & UART_MSR_TERI)
+			port->icount.rng++;
+
+		wake_up_interruptible(&port->port.delta_msr_wait);
+	}
+}
+
+static void f81534_update_lsr(struct usb_serial_port *port, unsigned char *ch)
+{
+	struct f81534_port_private *port_priv;
+	struct async_icount *icount = &port->icount;
+	unsigned long flags;
+	u8 newLSR = (u8) *ch;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	if (newLSR & UART_LSR_BI)
+		newLSR &= (u8) (UART_LSR_OE | UART_LSR_BI);
+
+	spin_lock_irqsave(&port_priv->lock, flags);
+	port_priv->shadowLSR = newLSR;
+	spin_unlock_irqrestore(&port_priv->lock, flags);
+
+	if (newLSR & UART_LSR_BRK_ERROR_BITS) {
+
+		if (newLSR & UART_LSR_BI)
+			icount->brk++;
+
+		if (newLSR & UART_LSR_OE)
+			icount->overrun++;
+
+		if (newLSR & UART_LSR_PE)
+			icount->parity++;
+
+		if (newLSR & UART_LSR_FE)
+			icount->frame++;
+	}
+}
+
+static int f81534_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct f81534_port_private *port_priv;
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+	int port_num = f81534_port_to_phy_index(port);
+	unsigned long flags = 0;
+	int r;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&serial_priv->write_urb_lock, flags);
+
+	if (serial_priv->phy_port_in_use[port_num])
+		r = 0;
+	else
+		r = m_F81534_MAX_TX_SIZE;
+
+	spin_unlock_irqrestore(&serial_priv->write_urb_lock, flags);
+
+	return r;
+}
+
+static int f81534_write(struct tty_struct *tty,
+			struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	struct f81534_serial_private *serial_priv =
+	    usb_get_serial_data(port->serial);
+	struct urb *write_urb;
+	unsigned char *data;
+	unsigned long flags;
+	int status;
+	int bytes_out = 0;
+	const unsigned char *current_position = buf;
+	int port_num = f81534_port_to_phy_index(port);
+	int updating_data = mutex_is_locked(&serial_priv->updating_mutex);
+
+	if (serial_priv->write_urb[port_num] == NULL) {
+		dev_err(&port->dev, "%s - no output urb\n", __func__);
+		return 0;
+	}
+
+	if (updating_data)
+		return 0;
+
+	if (serial_priv->phy_port_in_use[port_num])
+		return 0;
+
+	write_urb = serial_priv->write_urb[port_num];
+	count = min(count, m_F81534_MAX_TX_SIZE);
+	data = write_urb->transfer_buffer;
+
+	spin_lock_irqsave(&serial_priv->write_urb_lock, flags);
+
+	if (serial_priv->phy_port_in_use[port_num])
+		goto write_out;
+
+	data[0] = 0;
+	data[128] = 1;
+	data[256] = 2;
+	data[384] = 3;
+	data[128 * port_num + 0] = port_num;
+	data[128 * port_num + 1] = 2;
+	data[128 * port_num + 2] = count;
+	data[128 * port_num + 3] = 0;
+	memcpy(&data[128 * port_num + 4], current_position, count);
+
+	write_urb->transfer_buffer_length = F81534_WRITE_BUFFER_SIZE;
+
+	status = usb_submit_urb(write_urb, GFP_ATOMIC);
+	if (status == 0) {
+		serial_priv->phy_port_in_use[port_num] = true;
+		bytes_out += count;
+	}
+
+write_out:
+	spin_unlock_irqrestore(&serial_priv->write_urb_lock, flags);
+
+	return bytes_out;
+}
+
+#ifdef CONFIG_PM
+static int f81534_usb_serial_suspend(struct usb_serial *serial,
+			pm_message_t message)
+{
+	/* STD => PM_EVENT_FREEZE 1 */
+	/* STR => PM_EVENT_SUSPEND 2 */
+
+	dev_dbg(&serial->dev->dev, "%s message:%d\n", __func__, message.event);
+
+	if (message.event == PM_EVENT_SUSPEND)
+		f81534_free_urbs(serial);
+
+	return 0;
+}
+
+static int f81534_usb_serial_resume(struct usb_serial *serial)
+{
+	int status;
+	int i;
+	int phy;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		struct f81534_port_private *port_priv =
+		    usb_get_serial_port_data(serial->port[i]);
+
+		phy = f81534_logic_to_phy_port(serial, i);
+
+		BUG_ON(phy == F81534_UNUSED_PORT);
+
+		f81534_set_port_config(serial->dev, phy, serial->port[i],
+				       port_priv->currentBaudRate,
+				       port_priv->shadowLCR);
+	}
+
+	status = f81534_setup_urbs(serial);
+
+	return status;
+}
+#endif
+
+static struct usb_serial_driver f81534_device = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "F81534",
+		   },
+	.description = DRIVER_DESC,
+	.id_table = id_table,
+	.open = f81534_open,
+	.close = f81534_close,
+	.write = f81534_write,
+	.write_room = f81534_write_room,
+	.calc_num_ports = f81534_calc_num_ports,
+	.attach = f81534_attach,
+	.release = f81534_release,
+	.disconnect = f81534_disconnect,
+	.port_probe = f81534_port_probe,
+	.port_remove = f81534_port_remove,
+	.dtr_rts = f81534_dtr_rts,
+	.break_ctl = f81534_break_ctl,
+	.tiocmiwait = usb_serial_generic_tiocmiwait,
+	.get_icount = usb_serial_generic_get_icount,
+	.ioctl = f81534_ioctl,
+	.tiocmget = f81534_tiocmget,
+	.tiocmset = f81534_tiocmset,
+	.set_termios = f81534_set_termios,
+
+#ifdef CONFIG_PM
+	.resume = f81534_usb_serial_resume,
+	.suspend = f81534_usb_serial_suspend,
+#endif
+
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+	&f81534_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Peter Hong <Peter_Hong@...tek.com.tw>");
+MODULE_AUTHOR("Tom Tsai <Tom_Tsai@...tek.com.tw>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ