summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-12-01 16:12:12 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-12-01 16:12:12 +0100
commit66c79319f838df744b062a73c3dcdfd2156b7da7 (patch)
tree55e977f35309c7120b94cdd1bcac4aea87685ebf /drivers/usb/serial
parent7faf44bf925531478f5862002d6291893a3b9ba3 (diff)
parent3c3dd1e058cb01e835dcade4b54a6f13ffaeaf7c (diff)
downloadlinux-66c79319f838df744b062a73c3dcdfd2156b7da7.tar.bz2
Merge tag 'usb-serial-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes: USB-serial updates for v4.10-rc1 These updates include a new driver for Fintek F8153x devices, support for the GPIO functionality on CP2105 devices, and improved support for CH34X devices. Included are also some clean ups and fixes for various minor issues. Signed-off-by: Johan Hovold <johan@kernel.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/Kconfig10
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/ch341.c113
-rw-r--r--drivers/usb/serial/cp210x.c411
-rw-r--r--drivers/usb/serial/f81534.c1409
-rw-r--r--drivers/usb/serial/ftdi_sio.c5
-rw-r--r--drivers/usb/serial/io_edgeport.c3
-rw-r--r--drivers/usb/serial/io_ti.c3
-rw-r--r--drivers/usb/serial/kl5kusb105.c35
-rw-r--r--drivers/usb/serial/mos7720.c3
-rw-r--r--drivers/usb/serial/mos7840.c3
-rw-r--r--drivers/usb/serial/opticon.c3
-rw-r--r--drivers/usb/serial/quatech2.c3
-rw-r--r--drivers/usb/serial/ssu100.c3
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c3
-rw-r--r--drivers/usb/serial/usb_wwan.c3
16 files changed, 1914 insertions, 97 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 56ecb8b5115d..d9bc8dafe000 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 349d9df0895f..9e43b7b002eb 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/ch341.c b/drivers/usb/serial/ch341.c
index f139488d0816..2597b83a8ae2 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -61,13 +61,26 @@
* the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
*/
+#define CH341_REQ_READ_VERSION 0x5F
#define CH341_REQ_WRITE_REG 0x9A
#define CH341_REQ_READ_REG 0x95
-#define CH341_REG_BREAK1 0x05
-#define CH341_REG_BREAK2 0x18
-#define CH341_NBREAK_BITS_REG1 0x01
-#define CH341_NBREAK_BITS_REG2 0x40
-
+#define CH341_REQ_SERIAL_INIT 0xA1
+#define CH341_REQ_MODEM_CTRL 0xA4
+
+#define CH341_REG_BREAK 0x05
+#define CH341_REG_LCR 0x18
+#define CH341_NBREAK_BITS 0x01
+
+#define CH341_LCR_ENABLE_RX 0x80
+#define CH341_LCR_ENABLE_TX 0x40
+#define CH341_LCR_MARK_SPACE 0x20
+#define CH341_LCR_PAR_EVEN 0x10
+#define CH341_LCR_ENABLE_PAR 0x08
+#define CH341_LCR_STOP_BITS_2 0x04
+#define CH341_LCR_CS8 0x03
+#define CH341_LCR_CS7 0x02
+#define CH341_LCR_CS6 0x01
+#define CH341_LCR_CS5 0x00
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
@@ -119,10 +132,10 @@ static int ch341_control_in(struct usb_device *dev,
return r;
}
-static int ch341_set_baudrate(struct usb_device *dev,
- struct ch341_private *priv)
+static int ch341_init_set_baudrate(struct usb_device *dev,
+ struct ch341_private *priv, unsigned ctrl)
{
- short a, b;
+ short a;
int r;
unsigned long factor;
short divisor;
@@ -142,18 +155,17 @@ static int ch341_set_baudrate(struct usb_device *dev,
factor = 0x10000 - factor;
a = (factor & 0xff00) | divisor;
- b = factor & 0xff;
- r = ch341_control_out(dev, 0x9a, 0x1312, a);
- if (!r)
- r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
+ /* 0x9c is "enable SFR_UART Control register and timer" */
+ r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
+ 0x9c | (ctrl << 8), a | 0x80);
return r;
}
static int ch341_set_handshake(struct usb_device *dev, u8 control)
{
- return ch341_control_out(dev, 0xa4, ~control, 0);
+ return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
}
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
@@ -167,7 +179,7 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
if (!buffer)
return -ENOMEM;
- r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
+ r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
if (r < 0)
goto out;
@@ -197,24 +209,21 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
return -ENOMEM;
/* expect two bytes 0x27 0x00 */
- r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
+ r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
if (r < 0)
goto out;
+ dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
- r = ch341_control_out(dev, 0xa1, 0, 0);
- if (r < 0)
- goto out;
-
- r = ch341_set_baudrate(dev, priv);
+ r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
if (r < 0)
goto out;
/* expect two bytes 0x56 0x00 */
- r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
+ r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
if (r < 0)
goto out;
- r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050);
if (r < 0)
goto out;
@@ -223,11 +232,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
if (r < 0)
goto out;
- r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
- if (r < 0)
- goto out;
-
- r = ch341_set_baudrate(dev, priv);
+ r = ch341_init_set_baudrate(dev, priv, 0);
if (r < 0)
goto out;
@@ -342,16 +347,53 @@ static void ch341_set_termios(struct tty_struct *tty,
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned baud_rate;
unsigned long flags;
+ unsigned char ctrl;
+ int r;
+
+ /* redundant changes may cause the chip to lose bytes */
+ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+ return;
baud_rate = tty_get_baud_rate(tty);
priv->baud_rate = baud_rate;
+ ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ ctrl |= CH341_LCR_CS5;
+ break;
+ case CS6:
+ ctrl |= CH341_LCR_CS6;
+ break;
+ case CS7:
+ ctrl |= CH341_LCR_CS7;
+ break;
+ case CS8:
+ ctrl |= CH341_LCR_CS8;
+ break;
+ }
+
+ if (C_PARENB(tty)) {
+ ctrl |= CH341_LCR_ENABLE_PAR;
+ if (C_PARODD(tty) == 0)
+ ctrl |= CH341_LCR_PAR_EVEN;
+ if (C_CMSPAR(tty))
+ ctrl |= CH341_LCR_MARK_SPACE;
+ }
+
+ if (C_CSTOPB(tty))
+ ctrl |= CH341_LCR_STOP_BITS_2;
if (baud_rate) {
spin_lock_irqsave(&priv->lock, flags);
priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
spin_unlock_irqrestore(&priv->lock, flags);
- ch341_set_baudrate(port->serial->dev, priv);
+ r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
+ if (r < 0 && old_termios) {
+ priv->baud_rate = tty_termios_baud_rate(old_termios);
+ tty_termios_copy_hw(&tty->termios, old_termios);
+ }
} else {
spin_lock_irqsave(&priv->lock, flags);
priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
@@ -360,17 +402,12 @@ static void ch341_set_termios(struct tty_struct *tty,
ch341_set_handshake(port->serial->dev, priv->line_control);
- /* Unimplemented:
- * (cflag & CSIZE) : data bits [5, 8]
- * (cflag & PARENB) : parity {NONE, EVEN, ODD}
- * (cflag & CSTOPB) : stop bits [1, 2]
- */
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
- ((uint16_t) CH341_REG_BREAK2 << 8) | CH341_REG_BREAK1;
+ ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
int r;
uint16_t reg_contents;
@@ -391,12 +428,12 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
__func__, break_reg[0], break_reg[1]);
if (break_state != 0) {
dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
- break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
- break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
+ break_reg[0] &= ~CH341_NBREAK_BITS;
+ break_reg[1] &= ~CH341_LCR_ENABLE_TX;
} else {
dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
- break_reg[0] |= CH341_NBREAK_BITS_REG1;
- break_reg[1] |= CH341_NBREAK_BITS_REG2;
+ break_reg[0] |= CH341_NBREAK_BITS;
+ break_reg[1] |= CH341_LCR_ENABLE_TX;
}
dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 243ac5ebe46a..fff718352e0c 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -23,6 +23,9 @@
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
+#include <linux/gpio/driver.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
@@ -33,7 +36,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
static void cp210x_close(struct usb_serial_port *);
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp);
+ tcflag_t *cflagp, unsigned int *baudp);
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
@@ -44,6 +47,9 @@ static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
+static int cp210x_attach(struct usb_serial *);
+static void cp210x_disconnect(struct usb_serial *);
+static void cp210x_release(struct usb_serial *);
static int cp210x_port_probe(struct usb_serial_port *);
static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
@@ -209,6 +215,16 @@ static const struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
+struct cp210x_serial_private {
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gc;
+ u8 config;
+ u8 gpio_mode;
+ bool gpio_registered;
+#endif
+ u8 partnum;
+};
+
struct cp210x_port_private {
__u8 bInterfaceNumber;
bool has_swapped_line_ctl;
@@ -230,6 +246,9 @@ static struct usb_serial_driver cp210x_device = {
.tx_empty = cp210x_tx_empty,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
+ .attach = cp210x_attach,
+ .disconnect = cp210x_disconnect,
+ .release = cp210x_release,
.port_probe = cp210x_port_probe,
.port_remove = cp210x_port_remove,
.dtr_rts = cp210x_dtr_rts
@@ -272,6 +291,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E
+#define CP210X_VENDOR_SPECIFIC 0xFF
/* CP210X_IFC_ENABLE */
#define UART_ENABLE 0x0001
@@ -314,6 +334,21 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_RTS 0x0200
+/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_READ_LATCH 0x00C2
+#define CP210X_GET_PARTNUM 0x370B
+#define CP210X_GET_PORTCONFIG 0x370C
+#define CP210X_GET_DEVICEMODE 0x3711
+#define CP210X_WRITE_LATCH 0x37E1
+
+/* Part number definitions */
+#define CP210X_PARTNUM_CP2101 0x01
+#define CP210X_PARTNUM_CP2102 0x02
+#define CP210X_PARTNUM_CP2103 0x03
+#define CP210X_PARTNUM_CP2104 0x04
+#define CP210X_PARTNUM_CP2105 0x05
+#define CP210X_PARTNUM_CP2108 0x08
+
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct cp210x_comm_status {
__le32 ulErrors;
@@ -369,6 +404,60 @@ struct cp210x_flow_ctl {
#define CP210X_SERIAL_RTS_ACTIVE 1
#define CP210X_SERIAL_RTS_FLOW_CTL 2
+/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
+struct cp210x_pin_mode {
+ u8 eci;
+ u8 sci;
+} __packed;
+
+#define CP210X_PIN_MODE_MODEM 0
+#define CP210X_PIN_MODE_GPIO BIT(0)
+
+/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes.
+ * Structure needs padding due to unused/unspecified bytes.
+ */
+struct cp210x_config {
+ __le16 gpio_mode;
+ u8 __pad0[2];
+ __le16 reset_state;
+ u8 __pad1[4];
+ __le16 suspend_state;
+ u8 sci_cfg;
+ u8 eci_cfg;
+ u8 device_cfg;
+} __packed;
+
+/* GPIO modes */
+#define CP210X_SCI_GPIO_MODE_OFFSET 9
+#define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9)
+
+#define CP210X_ECI_GPIO_MODE_OFFSET 2
+#define CP210X_ECI_GPIO_MODE_MASK GENMASK(3, 2)
+
+/* CP2105 port configuration values */
+#define CP2105_GPIO0_TXLED_MODE BIT(0)
+#define CP2105_GPIO1_RXLED_MODE BIT(1)
+#define CP2105_GPIO1_RS485_MODE BIT(2)
+
+/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
+struct cp210x_gpio_write {
+ u8 mask;
+ u8 state;
+} __packed;
+
+/*
+ * Helper to get interface number when we only have struct usb_serial.
+ */
+static u8 cp210x_interface_num(struct usb_serial *serial)
+{
+ struct usb_host_interface *cur_altsetting;
+
+ cur_altsetting = serial->interface->cur_altsetting;
+
+ return cur_altsetting->desc.bInterfaceNumber;
+}
+
/*
* Reads a variable-sized block of CP210X_ registers, identified by req.
* Returns data into buf in native USB byte order.
@@ -402,7 +491,7 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n",
req, bufsize, result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
/*
* FIXME Some callers don't bother to check for error,
@@ -465,6 +554,40 @@ static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
}
/*
+ * Reads a variable-sized vendor block of CP210X_ registers, identified by val.
+ * Returns data into buf in native USB byte order.
+ */
+static int cp210x_read_vendor_block(struct usb_serial *serial, u8 type, u16 val,
+ void *buf, int bufsize)
+{
+ void *dmabuf;
+ int result;
+
+ dmabuf = kmalloc(bufsize, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, type, val,
+ cp210x_interface_num(serial), dmabuf, bufsize,
+ USB_CTRL_GET_TIMEOUT);
+ if (result == bufsize) {
+ memcpy(buf, dmabuf, bufsize);
+ result = 0;
+ } else {
+ dev_err(&serial->interface->dev,
+ "failed to get vendor val 0x%04x size %d: %d\n", val,
+ bufsize, result);
+ if (result >= 0)
+ result = -EIO;
+ }
+
+ kfree(dmabuf);
+
+ return result;
+}
+
+/*
* Writes any 16-bit CP210X_ register (req) whose value is passed
* entirely in the wValue field of the USB request.
*/
@@ -515,7 +638,7 @@ static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n",
req, bufsize, result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
}
return result;
@@ -533,6 +656,42 @@ static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
return cp210x_write_reg_block(port, req, &le32_val, sizeof(le32_val));
}
+#ifdef CONFIG_GPIOLIB
+/*
+ * Writes a variable-sized vendor block of CP210X_ registers, identified by val.
+ * Data in buf must be in native USB byte order.
+ */
+static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type,
+ u16 val, void *buf, int bufsize)
+{
+ void *dmabuf;
+ int result;
+
+ dmabuf = kmemdup(buf, bufsize, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, type, val,
+ cp210x_interface_num(serial), dmabuf, bufsize,
+ USB_CTRL_SET_TIMEOUT);
+
+ kfree(dmabuf);
+
+ if (result == bufsize) {
+ result = 0;
+ } else {
+ dev_err(&serial->interface->dev,
+ "failed to set vendor val 0x%04x size %d: %d\n", val,
+ bufsize, result);
+ if (result >= 0)
+ result = -EIO;
+ }
+
+ return result;
+}
+#endif
+
/*
* Detect CP2108 GET_LINE_CTL bug and activate workaround.
* Write a known good value 0x800, read it back.
@@ -683,7 +842,7 @@ static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
} else {
dev_err(&port->dev, "failed to get comm status: %d\n", result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
}
kfree(sts);
@@ -719,7 +878,7 @@ static void cp210x_get_termios(struct tty_struct *tty,
&tty->termios.c_cflag, &baud);
tty_encode_baud_rate(tty, baud, baud);
} else {
- unsigned int cflag;
+ tcflag_t cflag;
cflag = 0;
cp210x_get_termios_port(port, &cflag, &baud);
}
@@ -730,10 +889,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
*/
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp)
+ tcflag_t *cflagp, unsigned int *baudp)
{
struct device *dev = &port->dev;
- unsigned int cflag;
+ tcflag_t cflag;
struct cp210x_flow_ctl flow_ctl;
u32 baud;
u16 bits;
@@ -930,16 +1089,9 @@ static void cp210x_set_termios(struct tty_struct *tty,
dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
case CS8:
- bits |= BITS_DATA_8;
- dev_dbg(dev, "%s - data bits = 8\n", __func__);
- break;
- /*case CS9:
- bits |= BITS_DATA_9;
- dev_dbg(dev, "%s - data bits = 9\n", __func__);
- break;*/
default:
- dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
bits |= BITS_DATA_8;
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
}
if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
@@ -1108,10 +1260,188 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
}
+#ifdef CONFIG_GPIOLIB
+static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ switch (offset) {
+ case 0:
+ if (priv->config & CP2105_GPIO0_TXLED_MODE)
+ return -ENODEV;
+ break;
+ case 1:
+ if (priv->config & (CP2105_GPIO1_RXLED_MODE |
+ CP2105_GPIO1_RS485_MODE))
+ return -ENODEV;
+ break;
+ }
+
+ return 0;
+}
+
+static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ int result;
+ u8 buf;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST,
+ CP210X_READ_LATCH, &buf, sizeof(buf));
+ if (result < 0)
+ return result;
+
+ return !!(buf & BIT(gpio));
+}
+
+static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_gpio_write buf;
+
+ if (value == 1)
+ buf.state = BIT(gpio);
+ else
+ buf.state = 0;
+
+ buf.mask = BIT(gpio);
+
+ cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE,
+ CP210X_WRITE_LATCH, &buf, sizeof(buf));
+}
+
+static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ /* Hardware does not support an input mode */
+ return 0;
+}
+
+static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ /* Hardware does not support an input mode */
+ return -ENOTSUPP;
+}
+
+static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
+ int value)
+{
+ return 0;
+}
+
+static int cp210x_gpio_set_single_ended(struct gpio_chip *gc, unsigned int gpio,
+ enum single_ended_mode mode)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ /* Succeed only if in correct mode (this can't be set at runtime) */
+ if ((mode == LINE_MODE_PUSH_PULL) && (priv->gpio_mode & BIT(gpio)))
+ return 0;
+
+ if ((mode == LINE_MODE_OPEN_DRAIN) && !(priv->gpio_mode & BIT(gpio)))
+ return 0;
+
+ return -ENOTSUPP;
+}
+
+/*
+ * This function is for configuring GPIO using shared pins, where other signals
+ * are made unavailable by configuring the use of GPIO. This is believed to be
+ * only applicable to the cp2105 at this point, the other devices supported by
+ * this driver that provide GPIO do so in a way that does not impact other
+ * signals and are thus expected to have very different initialisation.
+ */
+static int cp2105_shared_gpio_init(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ struct cp210x_pin_mode mode;
+ struct cp210x_config config;
+ u8 intf_num = cp210x_interface_num(serial);
+ int result;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_DEVICEMODE, &mode,
+ sizeof(mode));
+ if (result < 0)
+ return result;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PORTCONFIG, &config,
+ sizeof(config));
+ if (result < 0)
+ return result;
+
+ /* 2 banks of GPIO - One for the pins taken from each serial port */
+ if (intf_num == 0) {
+ if (mode.eci == CP210X_PIN_MODE_MODEM)
+ return 0;
+
+ priv->config = config.eci_cfg;
+ priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
+ CP210X_ECI_GPIO_MODE_MASK) >>
+ CP210X_ECI_GPIO_MODE_OFFSET);
+ priv->gc.ngpio = 2;
+ } else if (intf_num == 1) {
+ if (mode.sci == CP210X_PIN_MODE_MODEM)
+ return 0;
+
+ priv->config = config.sci_cfg;
+ priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
+ CP210X_SCI_GPIO_MODE_MASK) >>
+ CP210X_SCI_GPIO_MODE_OFFSET);
+ priv->gc.ngpio = 3;
+ } else {
+ return -ENODEV;
+ }
+
+ priv->gc.label = "cp210x";
+ priv->gc.request = cp210x_gpio_request;
+ priv->gc.get_direction = cp210x_gpio_direction_get;
+ priv->gc.direction_input = cp210x_gpio_direction_input;
+ priv->gc.direction_output = cp210x_gpio_direction_output;
+ priv->gc.get = cp210x_gpio_get;
+ priv->gc.set = cp210x_gpio_set;
+ priv->gc.set_single_ended = cp210x_gpio_set_single_ended;
+ priv->gc.owner = THIS_MODULE;
+ priv->gc.parent = &serial->interface->dev;
+ priv->gc.base = -1;
+ priv->gc.can_sleep = true;
+
+ result = gpiochip_add_data(&priv->gc, serial);
+ if (!result)
+ priv->gpio_registered = true;
+
+ return result;
+}
+
+static void cp210x_gpio_remove(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ if (priv->gpio_registered) {
+ gpiochip_remove(&priv->gc);
+ priv->gpio_registered = false;
+ }
+}
+
+#else
+
+static int cp2105_shared_gpio_init(struct usb_serial *serial)
+{
+ return 0;
+}
+
+static void cp210x_gpio_remove(struct usb_serial *serial)
+{
+ /* Nothing to do */
+}
+
+#endif
+
static int cp210x_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
- struct usb_host_interface *cur_altsetting;
struct cp210x_port_private *port_priv;
int ret;
@@ -1119,8 +1449,7 @@ static int cp210x_port_probe(struct usb_serial_port *port)
if (!port_priv)
return -ENOMEM;
- cur_altsetting = serial->interface->cur_altsetting;
- port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+ port_priv->bInterfaceNumber = cp210x_interface_num(serial);
usb_set_serial_port_data(port, port_priv);
@@ -1143,6 +1472,52 @@ static int cp210x_port_remove(struct usb_serial_port *port)
return 0;
}
+static int cp210x_attach(struct usb_serial *serial)
+{
+ int result;
+ struct cp210x_serial_private *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PARTNUM, &priv->partnum,
+ sizeof(priv->partnum));
+ if (result < 0)
+ goto err_free_priv;
+
+ usb_set_serial_data(serial, priv);
+
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
+ result = cp2105_shared_gpio_init(serial);
+ if (result < 0) {
+ dev_err(&serial->interface->dev,
+ "GPIO initialisation failed, continuing without GPIO support\n");
+ }
+ }
+
+ return 0;
+err_free_priv:
+ kfree(priv);
+
+ return result;
+}
+
+static void cp210x_disconnect(struct usb_serial *serial)
+{
+ cp210x_gpio_remove(serial);
+}
+
+static void cp210x_release(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ cp210x_gpio_remove(serial);
+
+ kfree(priv);
+}
+
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
new file mode 100644
index 000000000000..8282a6a18fee
--- /dev/null
+++ b/drivers/usb/serial/f81534.c
@@ -0,0 +1,1409 @@
+/*
+ * F81532/F81534 USB to Serial Ports Bridge
+ *
+ * F81532 => 2 Serial Ports
+ * F81534 => 4 Serial Ports
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Copyright (C) 2016 Feature Integration Technology Inc., (Fintek)
+ * Copyright (C) 2016 Tom Tsai (Tom_Tsai@fintek.com.tw)
+ * Copyright (C) 2016 Peter Hong (Peter_Hong@fintek.com.tw)
+ *
+ * The F81532/F81534 had 1 control endpoint for setting, 1 endpoint bulk-out
+ * for all serial port TX and 1 endpoint bulk-in for all serial port read in
+ * (Read Data/MSR/LSR).
+ *
+ * Write URB is fixed with 512bytes, per serial port used 128Bytes.
+ * It can be described by f81534_prepare_write_buffer()
+ *
+ * Read URB is 512Bytes max, per serial port used 128Bytes.
+ * It can be described by f81534_process_read_urb() and maybe received with
+ * 128x1,2,3,4 bytes.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+/* Serial Port register Address */
+#define F81534_UART_BASE_ADDRESS 0x1200
+#define F81534_UART_OFFSET 0x10
+#define F81534_DIVISOR_LSB_REG (0x00 + F81534_UART_BASE_ADDRESS)
+#define F81534_DIVISOR_MSB_REG (0x01 + F81534_UART_BASE_ADDRESS)
+#define F81534_FIFO_CONTROL_REG (0x02 + F81534_UART_BASE_ADDRESS)
+#define F81534_LINE_CONTROL_REG (0x03 + F81534_UART_BASE_ADDRESS)
+#define F81534_MODEM_CONTROL_REG (0x04 + F81534_UART_BASE_ADDRESS)
+#define F81534_MODEM_STATUS_REG (0x06 + F81534_UART_BASE_ADDRESS)
+#define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS)
+
+#define F81534_DEF_CONF_ADDRESS_START 0x3000
+#define F81534_DEF_CONF_SIZE 8
+
+#define F81534_CUSTOM_ADDRESS_START 0x2f00
+#define F81534_CUSTOM_DATA_SIZE 0x10
+#define F81534_CUSTOM_NO_CUSTOM_DATA 0xff
+#define F81534_CUSTOM_VALID_TOKEN 0xf0
+#define F81534_CONF_OFFSET 1
+
+#define F81534_MAX_DATA_BLOCK 64
+#define F81534_MAX_BUS_RETRY 20
+
+/* Default URB timeout for USB operations */
+#define F81534_USB_MAX_RETRY 10
+#define F81534_USB_TIMEOUT 1000
+#define F81534_SET_GET_REGISTER 0xA0
+
+#define F81534_NUM_PORT 4
+#define F81534_UNUSED_PORT 0xff
+#define F81534_WRITE_BUFFER_SIZE 512
+
+#define DRIVER_DESC "Fintek F81532/F81534"
+#define FINTEK_VENDOR_ID_1 0x1934
+#define FINTEK_VENDOR_ID_2 0x2C42
+#define FINTEK_DEVICE_ID 0x1202
+#define F81534_MAX_TX_SIZE 124
+#define F81534_MAX_RX_SIZE 124
+#define F81534_RECEIVE_BLOCK_SIZE 128
+#define F81534_MAX_RECEIVE_BLOCK_SIZE 512
+
+#define F81534_TOKEN_RECEIVE 0x01
+#define F81534_TOKEN_WRITE 0x02
+#define F81534_TOKEN_TX_EMPTY 0x03
+#define F81534_TOKEN_MSR_CHANGE 0x04
+
+/*
+ * We used interal SPI bus to access FLASH section. We must wait the SPI bus to
+ * idle if we performed any command.
+ *
+ * SPI Bus status register: F81534_BUS_REG_STATUS
+ * Bit 0/1 : BUSY
+ * Bit 2 : IDLE
+ */
+#define F81534_BUS_BUSY (BIT(0) | BIT(1))
+#define F81534_BUS_IDLE BIT(2)
+#define F81534_BUS_READ_DATA 0x1004
+#define F81534_BUS_REG_STATUS 0x1003
+#define F81534_BUS_REG_START 0x1002
+#define F81534_BUS_REG_END 0x1001
+
+#define F81534_CMD_READ 0x03
+
+#define F81534_DEFAULT_BAUD_RATE 9600
+#define F81534_MAX_BAUDRATE 115200
+
+#define F81534_PORT_CONF_DISABLE_PORT BIT(3)
+#define F81534_PORT_CONF_NOT_EXIST_PORT BIT(7)
+#define F81534_PORT_UNAVAILABLE \
+ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT)
+
+#define F81534_1X_RXTRIGGER 0xc3
+#define F81534_8X_RXTRIGGER 0xcf
+
+static const struct usb_device_id f81534_id_table[] = {
+ { USB_DEVICE(FINTEK_VENDOR_ID_1, FINTEK_DEVICE_ID) },
+ { USB_DEVICE(FINTEK_VENDOR_ID_2, FINTEK_DEVICE_ID) },
+ {} /* Terminating entry */
+};
+
+#define F81534_TX_EMPTY_BIT 0
+
+struct f81534_serial_private {
+ u8 conf_data[F81534_DEF_CONF_SIZE];
+ int tty_idx[F81534_NUM_PORT];
+ u8 setting_idx;
+ int opened_port;
+ struct mutex urb_mutex;
+};
+
+struct f81534_port_private {
+ struct mutex mcr_mutex;
+ unsigned long tx_empty;
+ spinlock_t msr_lock;
+ u8 shadow_mcr;
+ u8 shadow_msr;
+ u8 phy_num;
+};
+
+static int f81534_logic_to_phy_port(struct usb_serial *serial,
+ struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ int count = 0;
+ int i;
+
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ if (port->port_number == count)
+ return i;
+
+ ++count;
+ }
+
+ return -ENODEV;
+}
+
+static int f81534_set_register(struct usb_serial *serial, u16 reg, u8 data)
+{
+ struct usb_interface *interface = serial->interface;
+ struct usb_device *dev = serial->dev;
+ size_t count = F81534_USB_MAX_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ *tmp = data;
+
+ /*
+ * Our device maybe not reply when heavily loading, We'll retry for
+ * F81534_USB_MAX_RETRY times.
+ */
+ while (count--) {
+ status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ F81534_SET_GET_REGISTER,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ reg, 0, tmp, sizeof(u8),
+ F81534_USB_TIMEOUT);
+ if (status > 0) {
+ status = 0;
+ break;
+ } else if (status == 0) {
+ status = -EIO;
+ }
+ }
+
+ if (status < 0) {
+ dev_err(&interface->dev, "%s: reg: %x data: %x failed: %d\n",
+ __func__, reg, data, status);
+ }
+
+ kfree(tmp);
+ return status;
+}
+
+static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data)
+{
+ struct usb_interface *interface = serial->interface;
+ struct usb_device *dev = serial->dev;
+ size_t count = F81534_USB_MAX_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /*
+ * Our device maybe not reply when heavily loading, We'll retry for
+ * F81534_USB_MAX_RETRY times.
+ */
+ while (count--) {
+ status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ F81534_SET_GET_REGISTER,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ reg, 0, tmp, sizeof(u8),
+ F81534_USB_TIMEOUT);
+ if (status > 0) {
+ status = 0;
+ break;
+ } else if (status == 0) {
+ status = -EIO;
+ }
+ }
+
+ if (status < 0) {
+ dev_err(&interface->dev, "%s: reg: %x failed: %d\n", __func__,
+ reg, status);
+ goto end;
+ }
+
+ *data = *tmp;
+
+end:
+ kfree(tmp);
+ return status;
+}
+
+static int f81534_set_port_register(struct usb_serial_port *port, u16 reg,
+ u8 data)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return f81534_set_register(port->serial,
+ reg + port_priv->phy_num * F81534_UART_OFFSET, data);
+}
+
+static int f81534_get_port_register(struct usb_serial_port *port, u16 reg,
+ u8 *data)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return f81534_get_register(port->serial,
+ reg + port_priv->phy_num * F81534_UART_OFFSET, data);
+}
+
+/*
+ * If we try to access the internal flash via SPI bus, we should check the bus
+ * status for every command. e.g., F81534_BUS_REG_START/F81534_BUS_REG_END
+ */
+static int f81534_wait_for_spi_idle(struct usb_serial *serial)
+{
+ size_t count = F81534_MAX_BUS_RETRY;
+ u8 tmp;
+ int status;
+
+ do {
+ status = f81534_get_register(serial, F81534_BUS_REG_STATUS,
+ &tmp);
+ if (status)
+ return status;
+
+ if (tmp & F81534_BUS_BUSY)
+ continue;
+
+ if (tmp & F81534_BUS_IDLE)
+ break;
+
+ } while (--count);
+
+ if (!count) {
+ dev_err(&serial->interface->dev,
+ "%s: timed out waiting for idle SPI bus\n",
+ __func__);
+ return -EIO;
+ }
+
+ return f81534_set_register(serial, F81534_BUS_REG_STATUS,
+ tmp & ~F81534_BUS_IDLE);
+}
+
+static int f81534_get_spi_register(struct usb_serial *serial, u16 reg,
+ u8 *data)
+{
+ int status;
+
+ status = f81534_get_register(serial, reg, data);
+ if (status)
+ return status;
+
+ return f81534_wait_for_spi_idle(serial);
+}
+
+static int f81534_set_spi_register(struct usb_serial *serial, u16 reg, u8 data)
+{
+ int status;
+
+ status = f81534_set_register(serial, reg, data);
+ if (status)
+ return status;
+
+ return f81534_wait_for_spi_idle(serial);
+}
+
+static int f81534_read_flash(struct usb_serial *serial, u32 address,
+ size_t size, u8 *buf)
+{
+ u8 tmp_buf[F81534_MAX_DATA_BLOCK];
+ size_t block = 0;
+ size_t read_size;
+ size_t count;
+ int status;
+ int offset;
+ u16 reg_tmp;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ F81534_CMD_READ);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 16) & 0xff);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 8) & 0xff);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 0) & 0xff);
+ if (status)
+ return status;
+
+ /* Continuous read mode */
+ do {
+ read_size = min_t(size_t, F81534_MAX_DATA_BLOCK, size);
+
+ for (count = 0; count < read_size; ++count) {
+ /* To write F81534_BUS_REG_END when final byte */
+ if (size <= F81534_MAX_DATA_BLOCK &&
+ read_size == count + 1)
+ reg_tmp = F81534_BUS_REG_END;
+ else
+ reg_tmp = F81534_BUS_REG_START;
+
+ /*
+ * Dummy code, force IC to generate a read pulse, the
+ * set of value 0xf1 is dont care (any value is ok)
+ */
+ status = f81534_set_spi_register(serial, reg_tmp,
+ 0xf1);
+ if (status)
+ return status;
+
+ status = f81534_get_spi_register(serial,
+ F81534_BUS_READ_DATA,
+ &tmp_buf[count]);
+ if (status)
+ return status;
+
+ offset = count + block * F81534_MAX_DATA_BLOCK;
+ buf[offset] = tmp_buf[count];
+ }
+
+ size -= read_size;
+ ++block;
+ } while (size);
+
+ return 0;
+}
+
+static void f81534_prepare_write_buffer(struct usb_serial_port *port, u8 *buf)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int phy_num = port_priv->phy_num;
+ u8 tx_len;
+ int i;
+
+ /*
+ * The block layout is fixed with 4x128 Bytes, per 128 Bytes a port.
+ * index 0: port phy idx (e.g., 0,1,2,3)
+ * index 1: only F81534_TOKEN_WRITE
+ * index 2: serial TX out length
+ * index 3: fix to 0
+ * index 4~127: serial out data block
+ */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ buf[i * F81534_RECEIVE_BLOCK_SIZE] = i;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 1] = F81534_TOKEN_WRITE;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 2] = 0;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 3] = 0;
+ }
+
+ tx_len = kfifo_out_locked(&port->write_fifo,
+ &buf[phy_num * F81534_RECEIVE_BLOCK_SIZE + 4],
+ F81534_MAX_TX_SIZE, &port->lock);
+
+ buf[phy_num * F81534_RECEIVE_BLOCK_SIZE + 2] = tx_len;
+}
+
+static int f81534_submit_writer(struct usb_serial_port *port, gfp_t mem_flags)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ struct urb *urb;
+ unsigned long flags;
+ int result;
+
+ /* Check is any data in write_fifo */
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (kfifo_is_empty(&port->write_fifo)) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Check H/W is TXEMPTY */
+ if (!test_and_clear_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty))
+ return 0;
+
+ urb = port->write_urbs[0];
+ f81534_prepare_write_buffer(port, port->bulk_out_buffers[0]);
+ urb->transfer_buffer_length = F81534_WRITE_BUFFER_SIZE;
+
+ result = usb_submit_urb(urb, mem_flags);
+ if (result) {
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+ dev_err(&port->dev, "%s: submit failed: %d\n", __func__,
+ result);
+ return result;
+ }
+
+ usb_serial_port_softint(port);
+ return 0;
+}
+
+static u32 f81534_calc_baud_divisor(u32 baudrate, u32 clockrate)
+{
+ if (!baudrate)
+ return 0;
+
+ /* Round to nearest divisor */
+ return DIV_ROUND_CLOSEST(clockrate, baudrate);
+}
+
+static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate,
+ u8 lcr)
+{
+ u32 divisor;
+ int status;
+ u8 value;
+
+ if (baudrate <= 1200)
+ value = F81534_1X_RXTRIGGER; /* 128 FIFO & TL: 1x */
+ else
+ value = F81534_8X_RXTRIGGER; /* 128 FIFO & TL: 8x */
+
+ status = f81534_set_port_register(port, F81534_CONFIG1_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: CONFIG1 setting failed\n", __func__);
+ return status;
+ }
+
+ if (baudrate <= 1200)
+ value = UART_FCR_TRIGGER_1 | UART_FCR_ENABLE_FIFO; /* TL: 1 */
+ else
+ value = UART_FCR_R_TRIG_11 | UART_FCR_ENABLE_FIFO; /* TL: 14 */
+
+ status = f81534_set_port_register(port, F81534_FIFO_CONTROL_REG,
+ value);
+ if (status) {
+ dev_err(&port->dev, "%s: FCR setting failed\n", __func__);
+ return status;
+ }
+
+ divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE);
+ value = UART_LCR_DLAB;
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+ value);
+ if (status) {
+ dev_err(&port->dev, "%s: set LCR failed\n", __func__);
+ return status;
+ }
+
+ value = divisor & 0xff;
+ status = f81534_set_port_register(port, F81534_DIVISOR_LSB_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: set DLAB LSB failed\n", __func__);
+ return status;
+ }
+
+ value = (divisor >> 8) & 0xff;
+ status = f81534_set_port_register(port, F81534_DIVISOR_MSB_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: set DLAB MSB failed\n", __func__);
+ return status;
+ }
+
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG, lcr);
+ if (status) {
+ dev_err(&port->dev, "%s: set LCR failed\n", __func__);
+ return status;
+ }
+
+ return 0;
+}
+
+static int f81534_update_mctrl(struct usb_serial_port *port, unsigned int set,
+ unsigned int clear)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+ u8 tmp;
+
+ if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0)
+ return 0; /* no change */
+
+ mutex_lock(&port_priv->mcr_mutex);
+
+ /* 'Set' takes precedence over 'Clear' */
+ clear &= ~set;
+
+ /* Always enable UART_MCR_OUT2 */
+ tmp = UART_MCR_OUT2 | port_priv->shadow_mcr;
+
+ if (clear & TIOCM_DTR)
+ tmp &= ~UART_MCR_DTR;
+
+ if (clear & TIOCM_RTS)
+ tmp &= ~UART_MCR_RTS;
+
+ if (set & TIOCM_DTR)
+ tmp |= UART_MCR_DTR;
+
+ if (set & TIOCM_RTS)
+ tmp |= UART_MCR_RTS;
+
+ status = f81534_set_port_register(port, F81534_MODEM_CONTROL_REG, tmp);
+ if (status < 0) {
+ dev_err(&port->dev, "%s: MCR write failed\n", __func__);
+ mutex_unlock(&port_priv->mcr_mutex);
+ return status;
+ }
+
+ port_priv->shadow_mcr = tmp;
+ mutex_unlock(&port_priv->mcr_mutex);
+ return 0;
+}
+
+/*
+ * This function will search the data area with token F81534_CUSTOM_VALID_TOKEN
+ * for latest configuration index. If nothing found
+ * (*index = F81534_CUSTOM_NO_CUSTOM_DATA), We'll load default configure in
+ * F81534_DEF_CONF_ADDRESS_START section.
+ *
+ * Due to we only use block0 to save data, so *index should be 0 or
+ * F81534_CUSTOM_NO_CUSTOM_DATA.
+ */
+static int f81534_find_config_idx(struct usb_serial *serial, u8 *index)
+{
+ u8 tmp;
+ int status;
+
+ status = f81534_read_flash(serial, F81534_CUSTOM_ADDRESS_START, 1,
+ &tmp);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: read failed: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ /* We'll use the custom data when the data is valid. */
+ if (tmp == F81534_CUSTOM_VALID_TOKEN)
+ *index = 0;
+ else
+ *index = F81534_CUSTOM_NO_CUSTOM_DATA;
+
+ return 0;
+}
+
+/*
+ * We had 2 generation of F81532/534 IC. All has an internal storage.
+ *
+ * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any
+ * internal data will used. All mode and gpio control should manually set
+ * by AP or Driver and all storage space value are 0xff. The
+ * f81534_calc_num_ports() will run to final we marked as "oldest version"
+ * for this IC.
+ *
+ * 2rd is designed to more generic to use any transceiver and this is our
+ * mass production type. We'll save data in F81534_CUSTOM_ADDRESS_START
+ * (0x2f00) with 9bytes. The 1st byte is a indicater. If the token is
+ * F81534_CUSTOM_VALID_TOKEN(0xf0), the IC is 2nd gen type, the following
+ * 4bytes save port mode (0:RS232/1:RS485 Invert/2:RS485), and the last
+ * 4bytes save GPIO state(value from 0~7 to represent 3 GPIO output pin).
+ * The f81534_calc_num_ports() will run to "new style" with checking
+ * F81534_PORT_UNAVAILABLE section.
+ */
+static int f81534_calc_num_ports(struct usb_serial *serial)
+{
+ u8 setting[F81534_CUSTOM_DATA_SIZE];
+ u8 setting_idx;
+ u8 num_port = 0;
+ int status;
+ size_t i;
+
+ /* Check had custom setting */
+ status = f81534_find_config_idx(serial, &setting_idx);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: find idx failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ /*
+ * We'll read custom data only when data available, otherwise we'll
+ * read default value instead.
+ */
+ if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) {
+ status = f81534_read_flash(serial,
+ F81534_CUSTOM_ADDRESS_START +
+ F81534_CONF_OFFSET,
+ sizeof(setting), setting);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: get custom data failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ dev_dbg(&serial->interface->dev,
+ "%s: read config from block: %d\n", __func__,
+ setting_idx);
+ } else {
+ /* Read default board setting */
+ status = f81534_read_flash(serial,
+ F81534_DEF_CONF_ADDRESS_START, F81534_NUM_PORT,
+ setting);
+
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: read failed: %d\n", __func__,
+ status);
+ return 0;
+ }
+
+ dev_dbg(&serial->interface->dev, "%s: read default config\n",
+ __func__);
+ }
+
+ /* New style, find all possible ports */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (setting[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ ++num_port;
+ }
+
+ if (num_port)
+ return num_port;
+
+ dev_warn(&serial->interface->dev, "%s: Read Failed. default 4 ports\n",
+ __func__);
+ return 4; /* Nothing found, oldest version IC */
+}
+
+static void f81534_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ u8 new_lcr = 0;
+ int status;
+ u32 baud;
+
+ 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)
+ return;
+
+ if (baud > F81534_MAX_BAUDRATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = F81534_DEFAULT_BAUD_RATE;
+
+ tty_encode_baud_rate(tty, baud, baud);
+ }
+
+ dev_dbg(&port->dev, "%s: baud: %d\n", __func__, baud);
+
+ status = f81534_set_port_config(port, baud, new_lcr);
+ if (status < 0) {
+ dev_err(&port->dev, "%s: set port config failed: %d\n",
+ __func__, status);
+ }
+}
+
+static int f81534_submit_read_urb(struct usb_serial *serial, gfp_t flags)
+{
+ return usb_serial_generic_submit_read_urbs(serial->port[0], flags);
+}
+
+static void f81534_msr_changed(struct usb_serial_port *port, u8 msr)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned long flags;
+ u8 old_msr;
+
+ if (!(msr & UART_MSR_ANY_DELTA))
+ return;
+
+ spin_lock_irqsave(&port_priv->msr_lock, flags);
+ old_msr = port_priv->shadow_msr;
+ port_priv->shadow_msr = msr;
+ spin_unlock_irqrestore(&port_priv->msr_lock, flags);
+
+ dev_dbg(&port->dev, "%s: MSR from %02x to %02x\n", __func__, old_msr,
+ msr);
+
+ /* Update input line counters */
+ if (msr & UART_MSR_DCTS)
+ port->icount.cts++;
+ if (msr & UART_MSR_DDSR)
+ port->icount.dsr++;
+ if (msr & UART_MSR_DDCD)
+ port->icount.dcd++;
+ if (msr & UART_MSR_TERI)
+ port->icount.rng++;
+
+ wake_up_interruptible(&port->port.delta_msr_wait);
+
+ if (!(msr & UART_MSR_DDCD))
+ return;
+
+ dev_dbg(&port->dev, "%s: DCD Changed: phy_num: %d from %x to %x\n",
+ __func__, port_priv->phy_num, old_msr, msr);
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return;
+
+ usb_serial_handle_dcd_change(port, tty, msr & UART_MSR_DCD);
+ tty_kref_put(tty);
+}
+
+static int f81534_read_msr(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int status;
+ u8 msr;
+
+ /* Get MSR initial value */
+ status = f81534_get_port_register(port, F81534_MODEM_STATUS_REG, &msr);
+ if (status)
+ return status;
+
+ /* Force update current state */
+ spin_lock_irqsave(&port_priv->msr_lock, flags);
+ port_priv->shadow_msr = msr;
+ spin_unlock_irqrestore(&port_priv->msr_lock, flags);
+
+ return 0;
+}
+
+static int f81534_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+
+ status = f81534_set_port_register(port,
+ F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ if (status) {
+ dev_err(&port->dev, "%s: Clear FIFO failed: %d\n", __func__,
+ status);
+ return status;
+ }
+
+ if (tty)
+ f81534_set_termios(tty, port, NULL);
+
+ status = f81534_read_msr(port);
+ if (status)
+ return status;
+
+ mutex_lock(&serial_priv->urb_mutex);
+
+ /* Submit Read URBs for first port opened */
+ if (!serial_priv->opened_port) {
+ status = f81534_submit_read_urb(port->serial, GFP_KERNEL);
+ if (status)
+ goto exit;
+ }
+
+ serial_priv->opened_port++;
+
+exit:
+ mutex_unlock(&serial_priv->urb_mutex);
+
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+ return status;
+}
+
+static void f81534_close(struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ struct usb_serial_port *port0 = port->serial->port[0];
+ unsigned long flags;
+ size_t i;
+
+ usb_kill_urb(port->write_urbs[0]);
+
+ spin_lock_irqsave(&port->lock, flags);
+ kfifo_reset_out(&port->write_fifo);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Kill Read URBs when final port closed */
+ mutex_lock(&serial_priv->urb_mutex);
+ serial_priv->opened_port--;
+
+ if (!serial_priv->opened_port) {
+ for (i = 0; i < ARRAY_SIZE(port0->read_urbs); ++i)
+ usb_kill_urb(port0->read_urbs[i]);
+ }
+
+ mutex_unlock(&serial_priv->urb_mutex);
+}
+
+static int f81534_get_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *retinfo)
+{
+ struct f81534_port_private *port_priv;
+ struct serial_struct tmp;
+
+ port_priv = usb_get_serial_port_data(port);
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.type = PORT_16550A;
+ tmp.port = port->port_number;
+ tmp.line = port->minor;
+ tmp.baud_base = F81534_MAX_BAUDRATE;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int f81534_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct serial_struct __user *buf = (struct serial_struct __user *)arg;
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ return f81534_get_serial_info(port, buf);
+ default:
+ break;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static void f81534_process_per_serial_block(struct usb_serial_port *port,
+ u8 *data)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int phy_num = data[0];
+ size_t read_size = 0;
+ size_t i;
+ char tty_flag;
+ int status;
+ u8 lsr;
+
+ /*
+ * The block layout is 128 Bytes
+ * index 0: port phy idx (e.g., 0,1,2,3),
+ * index 1: It's could be
+ * F81534_TOKEN_RECEIVE
+ * F81534_TOKEN_TX_EMPTY
+ * F81534_TOKEN_MSR_CHANGE
+ * index 2: serial in size (data+lsr, must be even)
+ * meaningful for F81534_TOKEN_RECEIVE only
+ * index 3: current MSR with this device
+ * index 4~127: serial in data block (data+lsr, must be even)
+ */
+ switch (data[1]) {
+ case F81534_TOKEN_TX_EMPTY:
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+
+ /* Try to submit writer */
+ status = f81534_submit_writer(port, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ return;
+
+ case F81534_TOKEN_MSR_CHANGE:
+ f81534_msr_changed(port, data[3]);
+ return;
+
+ case F81534_TOKEN_RECEIVE:
+ read_size = data[2];
+ if (read_size > F81534_MAX_RX_SIZE) {
+ dev_err(&port->dev,
+ "%s: phy: %d read_size: %zu larger than: %d\n",
+ __func__, phy_num, read_size,
+ F81534_MAX_RX_SIZE);
+ return;
+ }
+
+ break;
+
+ default:
+ dev_warn(&port->dev, "%s: unknown token: %02x\n", __func__,
+ data[1]);
+ return;
+ }
+
+ for (i = 4; i < 4 + read_size; i += 2) {
+ tty_flag = TTY_NORMAL;
+ lsr = data[i + 1];
+
+ if (lsr & UART_LSR_BRK_ERROR_BITS) {
+ if (lsr & UART_LSR_BI) {
+ tty_flag = TTY_BREAK;
+ port->icount.brk++;
+ usb_serial_handle_break(port);
+ } else if (lsr & UART_LSR_PE) {
+ tty_flag = TTY_PARITY;
+ port->icount.parity++;
+ } else if (lsr & UART_LSR_FE) {
+ tty_flag = TTY_FRAME;
+ port->icount.frame++;
+ }
+
+ if (lsr & UART_LSR_OE) {
+ port->icount.overrun++;
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
+ }
+
+ if (port->port.console && port->sysrq) {
+ if (usb_serial_handle_sysrq_char(port, data[i]))
+ continue;
+ }
+
+ tty_insert_flip_char(&port->port, data[i], tty_flag);
+ }
+
+ tty_flip_buffer_push(&port->port);
+}
+
+static void f81534_process_read_urb(struct urb *urb)
+{
+ struct f81534_serial_private *serial_priv;
+ struct usb_serial_port *port;
+ struct usb_serial *serial;
+ u8 *buf;
+ int phy_port_num;
+ int tty_port_num;
+ size_t i;
+
+ if (!urb->actual_length ||
+ urb->actual_length % F81534_RECEIVE_BLOCK_SIZE) {
+ return;
+ }
+
+ port = urb->context;
+ serial = port->serial;
+ buf = urb->transfer_buffer;
+ serial_priv = usb_get_serial_data(serial);
+
+ for (i = 0; i < urb->actual_length; i += F81534_RECEIVE_BLOCK_SIZE) {
+ phy_port_num = buf[i];
+ if (phy_port_num >= F81534_NUM_PORT) {
+ dev_err(&port->dev,
+ "%s: phy_port_num: %d larger than: %d\n",
+ __func__, phy_port_num, F81534_NUM_PORT);
+ continue;
+ }
+
+ tty_port_num = serial_priv->tty_idx[phy_port_num];
+ port = serial->port[tty_port_num];
+
+ if (tty_port_initialized(&port->port))
+ f81534_process_per_serial_block(port, &buf[i]);
+ }
+}
+
+static void f81534_write_usb_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+ __func__, urb->status);
+ return;
+ case -EPIPE:
+ dev_err(&port->dev, "%s - urb stopped: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+ __func__, urb->status);
+ break;
+ }
+}
+
+static int f81534_setup_ports(struct usb_serial *serial)
+{
+ struct usb_serial_port *port;
+ u8 port0_out_address;
+ int buffer_size;
+ size_t i;
+
+ /*
+ * In our system architecture, we had 2 or 4 serial ports,
+ * but only get 1 set of bulk in/out endpoints.
+ *
+ * The usb-serial subsystem will generate port 0 data,
+ * but port 1/2/3 will not. It's will generate write URB and buffer
+ * by following code and use the port0 read URB for read operation.
+ */
+ for (i = 1; i < serial->num_ports; ++i) {
+ port0_out_address = serial->port[0]->bulk_out_endpointAddress;
+ buffer_size = serial->port[0]->bulk_out_size;
+ port = serial->port[i];
+
+ if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+ return -ENOMEM;
+
+ port->bulk_out_size = buffer_size;
+ port->bulk_out_endpointAddress = port0_out_address;
+
+ port->write_urbs[0] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!port->write_urbs[0])
+ return -ENOMEM;
+
+ port->bulk_out_buffers[0] = kzalloc(buffer_size, GFP_KERNEL);
+ if (!port->bulk_out_buffers[0])
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(port->write_urbs[0], serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port0_out_address),
+ port->bulk_out_buffers[0], buffer_size,
+ serial->type->write_bulk_callback, port);
+
+ port->write_urb = port->write_urbs[0];
+ port->bulk_out_buffer = port->bulk_out_buffers[0];
+ }
+
+ return 0;
+}
+
+static int f81534_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_host_interface *iface_desc;
+ struct device *dev;
+ int num_bulk_in = 0;
+ int num_bulk_out = 0;
+ int size_bulk_in = 0;
+ int size_bulk_out = 0;
+ int i;
+
+ dev = &serial->interface->dev;
+ iface_desc = serial->interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint)) {
+ ++num_bulk_in;
+ size_bulk_in = usb_endpoint_maxp(endpoint);
+ }
+
+ if (usb_endpoint_is_bulk_out(endpoint)) {
+ ++num_bulk_out;
+ size_bulk_out = usb_endpoint_maxp(endpoint);
+ }
+ }
+
+ if (num_bulk_in != 1 || num_bulk_out != 1) {
+ dev_err(dev, "expected endpoints not found\n");
+ return -ENODEV;
+ }
+
+ if (size_bulk_out != F81534_WRITE_BUFFER_SIZE ||
+ size_bulk_in != F81534_MAX_RECEIVE_BLOCK_SIZE) {
+ dev_err(dev, "unsupported endpoint max packet size\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int f81534_attach(struct usb_serial *serial)
+{
+ struct f81534_serial_private *serial_priv;
+ int index = 0;
+ int status;
+ int i;
+
+ serial_priv = devm_kzalloc(&serial->interface->dev,
+ sizeof(*serial_priv), GFP_KERNEL);
+ if (!serial_priv)
+ return -ENOMEM;
+
+ usb_set_serial_data(serial, serial_priv);
+
+ mutex_init(&serial_priv->urb_mutex);
+
+ status = f81534_setup_ports(serial);
+ if (status)
+ return status;
+
+ /* Check had custom setting */
+ status = f81534_find_config_idx(serial, &serial_priv->setting_idx);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: find idx failed: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ /*
+ * We'll read custom data only when data available, otherwise we'll
+ * read default value instead.
+ */
+ if (serial_priv->setting_idx == F81534_CUSTOM_NO_CUSTOM_DATA) {
+ /*
+ * The default configuration layout:
+ * byte 0/1/2/3: uart setting
+ */
+ status = f81534_read_flash(serial,
+ F81534_DEF_CONF_ADDRESS_START,
+ F81534_DEF_CONF_SIZE,
+ serial_priv->conf_data);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: read reserve data failed: %d\n",
+ __func__, status);
+ return status;
+ }
+ } else {
+ /* Only read 8 bytes for mode & GPIO */
+ status = f81534_read_flash(serial,
+ F81534_CUSTOM_ADDRESS_START +
+ F81534_CONF_OFFSET,
+ sizeof(serial_priv->conf_data),
+ serial_priv->conf_data);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: idx: %d get data failed: %d\n",
+ __func__, serial_priv->setting_idx,
+ status);
+ return status;
+ }
+ }
+
+ /* Assign phy-to-logic mapping */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ serial_priv->tty_idx[i] = index++;
+ dev_dbg(&serial->interface->dev,
+ "%s: phy_num: %d, tty_idx: %d\n", __func__, i,
+ serial_priv->tty_idx[i]);
+ }
+
+ return 0;
+}
+
+static int f81534_port_probe(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv;
+
+ port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL);
+ if (!port_priv)
+ return -ENOMEM;
+
+ spin_lock_init(&port_priv->msr_lock);
+ mutex_init(&port_priv->mcr_mutex);
+
+ /* Assign logic-to-phy mapping */
+ port_priv->phy_num = f81534_logic_to_phy_port(port->serial, port);
+ if (port_priv->phy_num < 0 || port_priv->phy_num >= F81534_NUM_PORT)
+ return -ENODEV;
+
+ usb_set_serial_port_data(port, port_priv);
+ dev_dbg(&port->dev, "%s: port_number: %d, phy_num: %d\n", __func__,
+ port->port_number, port_priv->phy_num);
+
+ 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);
+ int status;
+ int r;
+ u8 msr;
+ u8 mcr;
+
+ /* Read current MSR from device */
+ status = f81534_get_port_register(port, F81534_MODEM_STATUS_REG, &msr);
+ if (status)
+ return status;
+
+ mutex_lock(&port_priv->mcr_mutex);
+ mcr = port_priv->shadow_mcr;
+ mutex_unlock(&port_priv->mcr_mutex);
+
+ r = (mcr & UART_MCR_DTR ? TIOCM_DTR : 0) |
+ (mcr & 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 usb_serial_port *port = tty->driver_data;
+
+ return f81534_update_mctrl(port, set, clear);
+}
+
+static void f81534_dtr_rts(struct usb_serial_port *port, int on)
+{
+ if (on)
+ f81534_update_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+ else
+ f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+static int f81534_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const u8 *buf, int count)
+{
+ int bytes_out, status;
+
+ if (!count)
+ return 0;
+
+ bytes_out = kfifo_in_locked(&port->write_fifo, buf, count,
+ &port->lock);
+
+ status = f81534_submit_writer(port, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ return status;
+ }
+
+ return bytes_out;
+}
+
+static bool f81534_tx_empty(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return test_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+}
+
+static int f81534_resume(struct usb_serial *serial)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(serial);
+ struct usb_serial_port *port;
+ int error = 0;
+ int status;
+ size_t i;
+
+ /*
+ * We'll register port 0 bulkin when port had opened, It'll take all
+ * port received data, MSR register change and TX_EMPTY information.
+ */
+ mutex_lock(&serial_priv->urb_mutex);
+
+ if (serial_priv->opened_port) {
+ status = f81534_submit_read_urb(serial, GFP_NOIO);
+ if (status) {
+ mutex_unlock(&serial_priv->urb_mutex);
+ return status;
+ }
+ }
+
+ mutex_unlock(&serial_priv->urb_mutex);
+
+ for (i = 0; i < serial->num_ports; i++) {
+ port = serial->port[i];
+ if (!tty_port_initialized(&port->port))
+ continue;
+
+ status = f81534_submit_writer(port, GFP_NOIO);
+ if (status) {
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ ++error;
+ }
+ }
+
+ if (error)
+ return -EIO;
+
+ return 0;
+}
+
+static struct usb_serial_driver f81534_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "f81534",
+ },
+ .description = DRIVER_DESC,
+ .id_table = f81534_id_table,
+ .open = f81534_open,
+ .close = f81534_close,
+ .write = f81534_write,
+ .tx_empty = f81534_tx_empty,
+ .calc_num_ports = f81534_calc_num_ports,
+ .probe = f81534_probe,
+ .attach = f81534_attach,
+ .port_probe = f81534_port_probe,
+ .dtr_rts = f81534_dtr_rts,
+ .process_read_urb = f81534_process_read_urb,
+ .ioctl = f81534_ioctl,
+ .tiocmget = f81534_tiocmget,
+ .tiocmset = f81534_tiocmset,
+ .write_bulk_callback = f81534_write_usb_callback,
+ .set_termios = f81534_set_termios,
+ .resume = f81534_resume,
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+ &f81534_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, f81534_id_table);
+
+MODULE_DEVICE_TABLE(usb, f81534_id_table);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Peter Hong <Peter_Hong@fintek.com.tw>");
+MODULE_AUTHOR("Tom Tsai <Tom_Tsai@fintek.com.tw>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 6e9fc8bcc285..23d14b98ae2a 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1455,8 +1455,6 @@ static int get_serial_info(struct usb_serial_port *port,
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.flags = priv->flags;
tmp.baud_base = priv->baud_base;
@@ -1538,9 +1536,6 @@ static int get_lsr_info(struct usb_serial_port *port,
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned int result = 0;
- if (!retinfo)
- return -EFAULT;
-
if (priv->transmit_empty)
result = TIOCSER_TEMT;
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 11c05ce2f35f..dcc0c58aaad5 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1554,9 +1554,6 @@ static int get_serial_info(struct edgeport_port *edge_port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_16550A;
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index fce82fd79f77..c339163698eb 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2459,9 +2459,6 @@ static int get_serial_info(struct edgeport_port *edge_port,
struct serial_struct tmp;
unsigned cwait;
- if (!retinfo)
- return -EFAULT;
-
cwait = edge_port->port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index fc5d3a791e08..0ee190fc1bf8 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -296,7 +296,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -311,21 +311,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
+ goto err_generic_close;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
-exit:
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+err_generic_close:
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index de9992b492b0..d52caa03679c 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -1861,9 +1861,6 @@ static int get_serial_info(struct moschip_port *mos7720_port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_16550A;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 57426d703a09..9a220b8e810f 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1956,9 +1956,6 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
if (mos7840_port == NULL)
return -1;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_16550A;
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 4b7bfb394a32..5ded6f524d59 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -336,9 +336,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!serial)
- return -EFAULT;
-
memset(&tmp, 0x00, sizeof(tmp));
/* fake emulate a 16550 uart to make userspace code happy */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 85acb50a7ee2..659cb8606bd9 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -463,9 +463,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = 0;
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 70a098de429f..2a156144c76c 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -318,9 +318,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = 0;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index a8b9bdba314f..8db9d071d940 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -1426,9 +1426,6 @@ static int ti_get_serial_info(struct ti_port *tport,
struct serial_struct ret_serial;
unsigned cwait;
- if (!ret_arg)
- return -EFAULT;
-
cwait = port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 3dfdfc81254b..59bfcb3da116 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -140,9 +140,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = port->port_number;