[PATCH 1/1] staging:f81534 Add F81532/534 Driver

From: Peter Hung
Date: Thu Jun 11 2015 - 23:43:27 EST


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)
4. There are 4x3 GPIOs to control transceiver. It's can be controlled
with sysfs

If had any question, Please send email to
hpeter+linux_kernel@xxxxxxxxx
peter_hong@xxxxxxxxxxxxx

Patches Welcome :D

Signed-off-by: Peter Hung <hpeter+linux_kernel@xxxxxxxxx>
---
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/f81534/Kconfig | 10 +
drivers/staging/f81534/Makefile | 1 +
drivers/staging/f81534/Readme | 9 +
drivers/staging/f81534/TODO | 12 +
drivers/staging/f81534/f81534.c | 3335 +++++++++++++++++++++++++++++++++++++++
7 files changed, 3370 insertions(+)
create mode 100644 drivers/staging/f81534/Kconfig
create mode 100644 drivers/staging/f81534/Makefile
create mode 100644 drivers/staging/f81534/Readme
create mode 100644 drivers/staging/f81534/TODO
create mode 100644 drivers/staging/f81534/f81534.c

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 7f6cae5..b7c0bd0 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -112,4 +112,6 @@ source "drivers/staging/fsl-mc/Kconfig"

source "drivers/staging/wilc1000/Kconfig"

+source "drivers/staging/f81534/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 347f647..9d17cb8 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
obj-$(CONFIG_WILC1000) += wilc1000/
+obj-$(CONFIG_USB_SERIAL_F8153X) += f81534/
diff --git a/drivers/staging/f81534/Kconfig b/drivers/staging/f81534/Kconfig
new file mode 100644
index 0000000..41bf55c
--- /dev/null
+++ b/drivers/staging/f81534/Kconfig
@@ -0,0 +1,10 @@
+#
+# Xilinx Clocking Wizard Driver
+#
+
+config USB_SERIAL_F8153X
+ tristate "F81532/534 USB to Serial Ports Driver"
+ depends on USB_SERIAL
+ default m
+ ---help---
+ Support for Fintek F81532/534 USB to Serial Ports board
diff --git a/drivers/staging/f81534/Makefile b/drivers/staging/f81534/Makefile
new file mode 100644
index 0000000..d73178d
--- /dev/null
+++ b/drivers/staging/f81534/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_SERIAL_F8153X) += f81534.o
diff --git a/drivers/staging/f81534/Readme b/drivers/staging/f81534/Readme
new file mode 100644
index 0000000..00b72b0
--- /dev/null
+++ b/drivers/staging/f81534/Readme
@@ -0,0 +1,9 @@
+This driver is for Fintek F81534/F81532
+
+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)
+ 4. There are 4x3 GPIOs to control transceiver. It's can be controlled with
+ sysfs
\ No newline at end of file
diff --git a/drivers/staging/f81534/TODO b/drivers/staging/f81534/TODO
new file mode 100644
index 0000000..422bdf2
--- /dev/null
+++ b/drivers/staging/f81534/TODO
@@ -0,0 +1,12 @@
+Current Progress
+ - Functional Test ok (BurninTest 921600bps 4Port / S4 / S5)
+
+TODO:
+ - Code review
+ - checkpatch.pl
+
+Welcome to send patch for F81532/534 If you found a problem.
+
+Patches to:
+ Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
+ Peter Hong <hpeter+linux_kernel@xxxxxxxxx>
diff --git a/drivers/staging/f81534/f81534.c b/drivers/staging/f81534/f81534.c
new file mode 100644
index 0000000..f6f5c2b
--- /dev/null
+++ b/drivers/staging/f81534/f81534.c
@@ -0,0 +1,3335 @@
+/*
+ * F81532/F81534 USB to Serial Ports Bridge
+ *
+ * F81532 => 2 Serial Ports
+ * F81534 => 4 Serial Ports
+ *
+ * Copyright (C) 2014 Tom Tsai (Tom_Tsai@xxxxxxxxxxxxx)
+ *
+ */
+#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 FINTEK_MAGIC 'F'
+
+
+#define FINTEK_SET_GPIO_MODE _IOW(FINTEK_MAGIC, 4, int)
+#define FINTEK_GET_GPIO_MODE _IOR(FINTEK_MAGIC, 5, int)
+#define FINTEK_GET_DATA _IOR(FINTEK_MAGIC, 8, struct internal_data)
+#define FINTEK_SET_DATA _IOW(FINTEK_MAGIC, 9, struct internal_data)
+#define FINTEK_ERASE_DATA_PAGE _IOW(FINTEK_MAGIC, 10, struct internal_data)
+
+#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_get_configure_data(struct usb_serial_port *port,
+ struct internal_data __user *arg)
+{
+ struct usb_serial *serial = port->serial;
+ struct internal_data data;
+ int nRet = 0;
+ unsigned int max_block = F81534_MAX_DATA_BLOCK;
+
+ memset(&data, 0, sizeof(data));
+
+ if (copy_from_user
+ (&data, (struct internal_data __user *)arg, sizeof(data)))
+ return -EFAULT;
+
+ data.size = min(data.size, max_block);
+
+ nRet = f81534_read_data(serial, data.address, data.size, data.buf);
+ if (nRet)
+ return nRet;
+
+ if (copy_to_user
+ ((struct internal_data __user *)arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int f81534_set_configure_data(struct usb_serial_port *port,
+ struct internal_data __user *arg)
+{
+ struct usb_serial *serial = port->serial;
+ struct internal_data data;
+ int nRet = 0;
+ unsigned int max_block = F81534_MAX_DATA_BLOCK;
+
+ memset(&data, 0, sizeof(data));
+
+ if (copy_from_user
+ (&data, (struct internal_data __user *)arg, sizeof(data)))
+ return -EFAULT;
+
+ data.size = min(data.size, max_block);
+
+ nRet = f81534_write_data(serial, data.address, data.size, data.buf);
+ if (nRet)
+ return nRet;
+
+ if (copy_to_user
+ ((struct internal_data __user *)arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int f81534_erase_configure_data(struct usb_serial_port *port,
+ struct internal_data __user *arg)
+{
+ struct usb_serial *serial = port->serial;
+ struct internal_data data;
+ int nRet = 0;
+
+ memset(&data, 0, sizeof(data));
+
+ if (copy_from_user
+ (&data, (struct internal_data __user *)arg, sizeof(data)))
+ return -EFAULT;
+
+ nRet = f81534_erase_sector(serial, data.address);
+ if (nRet)
+ return nRet;
+
+ return 0;
+}
+
+static int f81534_ioctl_set_gpio(struct usb_serial_port *port,
+ unsigned long __user arg)
+{
+ int status = 0;
+ int mode;
+ 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;
+ }
+
+ status = copy_from_user(&mode, (int __user *)arg, sizeof(mode));
+ if (status)
+ goto finish;
+
+ status = f81534_switch_gpio_mode(port, mode);
+ if (status)
+ goto finish;
+
+ port_priv->port_pin_data.eGPIOMode = mode;
+
+ status = f81534_save_configure_data(port);
+ if (status)
+ goto finish;
+
+finish:
+ mutex_unlock(&serial_priv->updating_mutex);
+ f81534_wakeup_all_port(port->serial);
+
+ return status;
+}
+
+static int f81534_ioctl_get_gpio(struct usb_serial_port *port,
+ unsigned long __user arg)
+{
+ int status = 0;
+ 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;
+ }
+
+ if (copy_to_user((int *)arg,
+ &port_priv->port_pin_data.eGPIOMode,
+ sizeof(enum eUartMode)))
+ status = -EFAULT;
+
+ mutex_unlock(&serial_priv->updating_mutex);
+ f81534_wakeup_all_port(port->serial);
+
+ return status;
+}
+
+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);
+
+ case FINTEK_SET_GPIO_MODE:
+ return f81534_ioctl_set_gpio(port, arg);
+
+ case FINTEK_GET_GPIO_MODE:
+ return f81534_ioctl_get_gpio(port, arg);
+
+
+ case FINTEK_ERASE_DATA_PAGE:
+ return f81534_erase_configure_data(port,
+ (struct internal_data __user
+ *)
+ arg);
+
+ case FINTEK_GET_DATA:
+ return f81534_get_configure_data(port,
+ (struct internal_data __user *)
+ arg);
+
+ case FINTEK_SET_DATA:
+ return f81534_set_configure_data(port,
+ (struct internal_data __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@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Tom Tsai <Tom_Tsai@xxxxxxxxxxxxx>");
+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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/