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
[email protected]
[email protected]
Signed-off-by: Peter Hung <[email protected]>
---
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 ([email protected])
+ *
+ */
+#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,
+ ¤t_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 <[email protected]>");
+MODULE_AUTHOR("Tom Tsai <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1
Hello Johan,
Peter Hung ?? 2015/6/15 ?W?? 09:54 ?g?D:
> 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)
>
Do you receive my patch?
Are there anything should I do to improve it ?
--
With Best Regards,
Peter Hung
Hi Peter,
On Thu, Jun 25, 2015 at 01:16:56PM +0800, Peter Hung wrote:
> Hello Johan,
>
> Peter Hung 於 2015/6/15 上午 09:54 寫道:
> > 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)
> >
>
> Do you receive my patch?
Yes, I did. I just haven't had time to review it yet.
> Are there anything should I do to improve it ?
There are, including
- your custom read and write implementations look odd, you should be
able to reuse a lot more of the generic framework
- you'll also need to implement open/and close in some way
(enable/disable in hardware or flag in software) as you should not
push data to a closed tty
- stack allocated buffers used for DMA (register accessors)
- DMA buffers allocated as part of larger struct (read/write buffers)
- lots of magic constants (e.g. use defines for the registers)
- As Greg already mentioned, you need to implement gpio support using
gpiolib, not a custom sysfs interface
I don't have time to look closer at the architectural bits until next
week I'm afraid, but perhaps you could start with the above.
Thanks,
Johan
Hi Johan,
Johan Hovold 於 2015/6/25 下午 04:06 寫道:
> - As Greg already mentioned, you need to implement gpio support using
> gpiolib, not a custom sysfs interface
>
> I don't have time to look closer at the architectural bits until next
> week I'm afraid, but perhaps you could start with the above.
Thanks for your advices, I'll try to improve it again.
But I had something need to clarify.
1. The sysfs interface of the driver provide "limited output pin"
control. only support output 0/1, not support input mode.
Should I implement it as gpiolib?
2. If it still recommends to implement with gpiolib, Could I implement
it and preserve the sysfs interface for legacy?
--
With Best Regards,
Peter Hung
On Thu, Jun 25, 2015 at 05:21:01PM +0800, Peter Hung wrote:
> Hi Johan,
>
> Johan Hovold 於 2015/6/25 下午 04:06 寫道:
> > - As Greg already mentioned, you need to implement gpio support using
> > gpiolib, not a custom sysfs interface
> >
> > I don't have time to look closer at the architectural bits until next
> > week I'm afraid, but perhaps you could start with the above.
>
> Thanks for your advices, I'll try to improve it again.
> But I had something need to clarify.
>
> 1. The sysfs interface of the driver provide "limited output pin"
> control. only support output 0/1, not support input mode.
> Should I implement it as gpiolib?
Yes.
> 2. If it still recommends to implement with gpiolib, Could I implement
> it and preserve the sysfs interface for legacy?
No, I'm afraid not. We don't want to have to maintain out-of-tree legacy
interfaces.
Johan
On Thu, Jun 25, 2015 at 10:06:07AM +0200, Johan Hovold wrote:
> Hi Peter,
>
> On Thu, Jun 25, 2015 at 01:16:56PM +0800, Peter Hung wrote:
> > Hello Johan,
> >
> > Peter Hung 於 2015/6/15 上午 09:54 寫道:
> > > 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)
> > >
> >
> > Do you receive my patch?
>
> Yes, I did. I just haven't had time to review it yet.
>
> > Are there anything should I do to improve it ?
>
> There are, including
>
> - your custom read and write implementations look odd, you should be
> able to reuse a lot more of the generic framework
> - you'll also need to implement open/and close in some way
> (enable/disable in hardware or flag in software) as you should not
> push data to a closed tty
> - stack allocated buffers used for DMA (register accessors)
> - DMA buffers allocated as part of larger struct (read/write buffers)
> - lots of magic constants (e.g. use defines for the registers)
> - As Greg already mentioned, you need to implement gpio support using
> gpiolib, not a custom sysfs interface
>
> I don't have time to look closer at the architectural bits until next
> week I'm afraid, but perhaps you could start with the above.
I took a closer look at the protocol, and you should definitely be able
to reuse more of the generic implementation, at least for the read path.
You should also make sure to document the bulk-message layout somewhere
in the driver. Do you always have to send full 512-byte messages?
Use the read-urbs already allocated by usb-serial core, and take a look
at how this is handled in mxuport (e.g. submit port0 read urbs at
attach, and demultiplex in the process_read_urb callback). Your
message-parsing code needs to be cleaned up.
For the write-path, you at least need to use a fifo per port (usb-serial
core will have allocated one for the first port, again see mxuport).
Depending on the firmware implementation you may be able to reuse the
generic implementation as mxuport does, or you need to keep a custom
implementation. Could you elaborate on what buffers you have in the
firmware? What happens if you submit data for a port before receiving a
"tx empty" notification over the bulk-in endpoints?
Also make sure to remove all unused code (e.g. the READ_AND_SET macro)
before resubmitting.
Thanks,
Johan