Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752701AbbFLDnZ (ORCPT ); Thu, 11 Jun 2015 23:43:25 -0400 Received: from mail-pa0-f51.google.com ([209.85.220.51]:35225 "EHLO mail-pa0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751286AbbFLDnT (ORCPT ); Thu, 11 Jun 2015 23:43:19 -0400 From: Peter Hung X-Google-Original-From: Peter Hung To: gregkh@linuxfoundation.org Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, peter_hong@fintek.com.tw, tom_tsai@fintek.com.tw, Peter Hung Subject: [PATCH 1/1] staging:f81534 Add F81532/534 Driver Date: Fri, 12 Jun 2015 11:43:12 +0800 Message-Id: <1434080592-13089-1-git-send-email-hpeter+linux_kernel@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 87831 Lines: 3458 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@gmail.com peter_hong@fintek.com.tw Patches Welcome :D Signed-off-by: Peter Hung --- 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 + Peter Hong 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@fintek.com.tw) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, + ¤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_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 "); +MODULE_AUTHOR("Tom Tsai "); +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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/