diff options
Diffstat (limited to 'drivers/usb/serial')
28 files changed, 2160 insertions, 184 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..95aa5233726c 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) }, @@ -82,6 +95,7 @@ struct ch341_private { unsigned baud_rate; /* set baud rate */ u8 line_control; /* set line control value RTS/DTR */ u8 line_status; /* active status of modem control inputs */ + u8 lcr; }; static void ch341_set_termios(struct tty_struct *tty, @@ -99,6 +113,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request, r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, value, index, NULL, 0, DEFAULT_TIMEOUT); + if (r < 0) + dev_err(&dev->dev, "failed to send control message: %d\n", r); return r; } @@ -116,13 +132,26 @@ static int ch341_control_in(struct usb_device *dev, r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, value, index, buf, bufsize, DEFAULT_TIMEOUT); - return r; + if (r < bufsize) { + if (r >= 0) { + dev_err(&dev->dev, + "short control message received (%d < %u)\n", + r, bufsize); + r = -EIO; + } + + dev_err(&dev->dev, "failed to receive control message: %d\n", + r); + return r; + } + + return 0; } -static int ch341_set_baudrate(struct usb_device *dev, - struct ch341_private *priv) +static int ch341_set_baudrate_lcr(struct usb_device *dev, + struct ch341_private *priv, u8 lcr) { - short a, b; + short a; int r; unsigned long factor; short divisor; @@ -142,43 +171,47 @@ 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); + /* + * CH341A buffers data until a full endpoint-size packet (32 bytes) + * has been received unless bit 7 is set. + */ + a |= BIT(7); + + r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a); + if (r) + return r; + + r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr); + if (r) + return r; 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) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; unsigned long flags; buffer = kmalloc(size, GFP_KERNEL); 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; - /* setup the private status if available */ - if (r == 2) { - r = 0; - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; - spin_unlock_irqrestore(&priv->lock, flags); - } else - r = -EPROTO; + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; + spin_unlock_irqrestore(&priv->lock, flags); out: kfree(buffer); return r; @@ -188,33 +221,30 @@ out: kfree(buffer); static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; buffer = kmalloc(size, GFP_KERNEL); if (!buffer) 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 +253,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_set_baudrate_lcr(dev, priv, priv->lcr); if (r < 0) goto out; @@ -253,7 +279,6 @@ static int ch341_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(port->serial->dev, priv); if (r < 0) @@ -315,7 +340,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) r = ch341_configure(serial->dev, priv); if (r) - goto out; + return r; if (tty) ch341_set_termios(tty, port, NULL); @@ -325,12 +350,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) if (r) { dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", __func__, r); - goto out; + return r; } r = usb_serial_generic_open(tty, port); + if (r) + goto err_kill_interrupt_urb; + + return 0; -out: return r; +err_kill_interrupt_urb: + usb_kill_urb(port->interrupt_in_urb); + + return r; } /* Old_termios contains the original termios settings and @@ -342,35 +374,69 @@ 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); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); + priv->baud_rate = baud_rate; + + r = ch341_set_baudrate_lcr(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 if (r == 0) { + priv->lcr = ctrl; + } } - ch341_set_handshake(port->serial->dev, priv->line_control); + spin_lock_irqsave(&priv->lock, flags); + if (C_BAUD(tty) == B0) + priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) + priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); + spin_unlock_irqrestore(&priv->lock, flags); - /* Unimplemented: - * (cflag & CSIZE) : data bits [5, 8] - * (cflag & PARENB) : parity {NONE, EVEN, ODD} - * (cflag & CSTOPB) : stop bits [1, 2] - */ + ch341_set_handshake(port->serial->dev, priv->line_control); } 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 +457,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]); @@ -539,14 +605,23 @@ static int ch341_tiocmget(struct tty_struct *tty) static int ch341_reset_resume(struct usb_serial *serial) { - struct ch341_private *priv; - - priv = usb_get_serial_port_data(serial->port[0]); + struct usb_serial_port *port = serial->port[0]; + struct ch341_private *priv = usb_get_serial_port_data(port); + int ret; /* reconfigure ch341 serial port after bus-reset */ ch341_configure(serial->dev, priv); - return 0; + if (tty_port_initialized(&port->port)) { + ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); + if (ret) { + dev_err(&port->dev, "failed to submit interrupt urb: %d\n", + ret); + return ret; + } + } + + return usb_serial_generic_resume(serial); } static struct usb_serial_driver ch341_device = { 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/cyberjack.c b/drivers/usb/serial/cyberjack.c index 5f17a3b9916d..80260b08398b 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -50,6 +50,7 @@ #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ +static int cyberjack_attach(struct usb_serial *serial); static int cyberjack_port_probe(struct usb_serial_port *port); static int cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, @@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = { .description = "Reiner SCT Cyberjack USB card reader", .id_table = id_table, .num_ports = 1, + .attach = cyberjack_attach, .port_probe = cyberjack_port_probe, .port_remove = cyberjack_port_remove, .open = cyberjack_open, @@ -100,6 +102,14 @@ struct cyberjack_private { short wrsent; /* Data already sent */ }; +static int cyberjack_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_out < serial->num_ports) + return -ENODEV; + + return 0; +} + static int cyberjack_port_probe(struct usb_serial_port *port) { struct cyberjack_private *priv; diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c new file mode 100644 index 000000000000..22f23a429a95 --- /dev/null +++ b/drivers/usb/serial/f81534.c @@ -0,0 +1,1411 @@ +/* + * 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; + int ret; + + 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 */ + ret = f81534_logic_to_phy_port(port->serial, port); + if (ret < 0) + return ret; + + port_priv->phy_num = ret; + 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/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 97cabf803c2f..b2f2e87aed94 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1043,6 +1043,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; + kfree(buffer); } /* we are done with this urb, so let the host driver diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 11c05ce2f35f..d50e5773483f 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; @@ -2754,6 +2751,11 @@ static int edge_startup(struct usb_serial *serial) EDGE_COMPATIBILITY_MASK1, EDGE_COMPATIBILITY_MASK2 }; + if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + dev = serial->dev; /* create our private serial structure */ diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index fce82fd79f77..9a0db2965fbb 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial, dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); - /* return an error on purpose */ - return -ENODEV; + return 1; } stayinbootmode: @@ -1508,7 +1507,7 @@ stayinbootmode: dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); serial->product_info.TiMode = TI_MODE_BOOT; - return 0; + return 1; } static int ti_do_config(struct edgeport_port *port, int feature, int on) @@ -2459,9 +2458,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; @@ -2549,6 +2545,13 @@ static int edge_startup(struct usb_serial *serial) int status; u16 product_id; + /* Make sure we have the required endpoints when in download mode. */ + if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) { + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) + return -ENODEV; + } + /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); if (!edge_serial) @@ -2556,14 +2559,18 @@ static int edge_startup(struct usb_serial *serial) mutex_init(&edge_serial->es_lock); edge_serial->serial = serial; + INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); usb_set_serial_data(serial, edge_serial); status = download_fw(edge_serial); - if (status) { + if (status < 0) { kfree(edge_serial); return status; } + if (status > 0) + return 1; /* bind but do not register any ports */ + product_id = le16_to_cpu( edge_serial->serial->dev->descriptor.idProduct); @@ -2575,7 +2582,6 @@ static int edge_startup(struct usb_serial *serial) } } - INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); edge_heartbeat_schedule(edge_serial); return 0; @@ -2583,6 +2589,9 @@ static int edge_startup(struct usb_serial *serial) static void edge_disconnect(struct usb_serial *serial) { + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); + + cancel_delayed_work_sync(&edge_serial->heartbeat_work); } static void edge_release(struct usb_serial *serial) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 344b4eea4bd5..d57fb5199218 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -68,6 +68,16 @@ struct iuu_private { u32 clk; }; +static int iuu_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports) + return -ENODEV; + + return 0; +} + static int iuu_port_probe(struct usb_serial_port *port) { struct iuu_private *priv; @@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = { .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, .init_termios = iuu_init_termios, + .attach = iuu_attach, .port_probe = iuu_port_probe, .port_remove = iuu_port_remove, }; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e49ad0c63ad8..83523fcf6fb9 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); #endif +static int keyspan_pda_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int keyspan_pda_port_probe(struct usb_serial_port *port) { @@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = { .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, .tiocmset = keyspan_pda_tiocmset, + .attach = keyspan_pda_attach, .port_probe = keyspan_pda_port_probe, .port_remove = keyspan_pda_port_remove, }; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index fc5d3a791e08..6cb45757818f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, status_buf, KLSI_STATUSBUF_LEN, 10000 ); - if (rc < 0) - dev_err(&port->dev, "Reading line status failed (error = %d)\n", - rc); - else { + if (rc != KLSI_STATUSBUF_LEN) { + dev_err(&port->dev, "reading line status failed: %d\n", rc); + if (rc >= 0) + rc = -EIO; + } else { status = get_unaligned_le16(status_buf); dev_info(&port->serial->dev->dev, "read status %x %x\n", @@ -296,7 +297,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 +312,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/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2363654cafc9..813035f51fe7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -51,6 +51,7 @@ /* Function prototypes */ +static int kobil_attach(struct usb_serial *serial); static int kobil_port_probe(struct usb_serial_port *probe); static int kobil_port_remove(struct usb_serial_port *probe); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); @@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = { .description = "KOBIL USB smart card terminal", .id_table = id_table, .num_ports = 1, + .attach = kobil_attach, .port_probe = kobil_port_probe, .port_remove = kobil_port_remove, .ioctl = kobil_ioctl, @@ -113,6 +115,16 @@ struct kobil_private { }; +static int kobil_attach(struct usb_serial *serial) +{ + if (serial->num_interrupt_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n"); + return -ENODEV; + } + + return 0; +} + static int kobil_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index de9992b492b0..91bc170b408a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -65,8 +65,6 @@ struct moschip_port { struct urb *write_urb_pool[NUM_URBS]; }; -static struct usb_serial_driver moschip7720_2port_driver; - #define USB_VENDOR_ID_MOSCHIP 0x9710 #define MOSCHIP_DEVICE_ID_7720 0x7720 #define MOSCHIP_DEVICE_ID_7715 0x7715 @@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) tty_port_tty_wakeup(&mos7720_port->port->port); } -/* - * mos77xx_probe - * this function installs the appropriate read interrupt endpoint callback - * depending on whether the device is a 7720 or 7715, thus avoiding costly - * run-time checks in the high-frequency callback routine itself. - */ -static int mos77xx_probe(struct usb_serial *serial, - const struct usb_device_id *id) -{ - if (id->idProduct == MOSCHIP_DEVICE_ID_7715) - moschip7720_2port_driver.read_int_callback = - mos7715_interrupt_callback; - else - moschip7720_2port_driver.read_int_callback = - mos7720_interrupt_callback; - - return 0; -} - static int mos77xx_calc_num_ports(struct usb_serial *serial) { u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); @@ -1861,9 +1840,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; @@ -1920,6 +1896,11 @@ static int mos7720_startup(struct usb_serial *serial) u16 product; int ret_val; + if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing bulk endpoints\n"); + return -ENODEV; + } + product = le16_to_cpu(serial->dev->descriptor.idProduct); dev = serial->dev; @@ -1944,19 +1925,18 @@ static int mos7720_startup(struct usb_serial *serial) tmp->interrupt_in_endpointAddress; serial->port[1]->interrupt_in_urb = NULL; serial->port[1]->interrupt_in_buffer = NULL; + + if (serial->port[0]->interrupt_in_urb) { + struct urb *urb = serial->port[0]->interrupt_in_urb; + + urb->complete = mos7715_interrupt_callback; + } } /* setting configuration feature to one */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); - /* start the interrupt urb */ - ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); - if (ret_val) - dev_err(&dev->dev, - "%s - Error %d submitting control urb\n", - __func__, ret_val); - #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT if (product == MOSCHIP_DEVICE_ID_7715) { ret_val = mos7715_parport_init(serial); @@ -1964,6 +1944,13 @@ static int mos7720_startup(struct usb_serial *serial) return ret_val; } #endif + /* start the interrupt urb */ + ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); + if (ret_val) { + dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", + ret_val); + } + /* LSR For Port 1 */ read_mos_reg(serial, 0, MOS7720_LSR, &data); dev_dbg(&dev->dev, "LSR:%x\n", data); @@ -1973,6 +1960,8 @@ static int mos7720_startup(struct usb_serial *serial) static void mos7720_release(struct usb_serial *serial) { + usb_kill_urb(serial->port[0]->interrupt_in_urb); + #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT /* close the parallel port */ @@ -2022,11 +2011,6 @@ static int mos7720_port_probe(struct usb_serial_port *port) if (!mos7720_port) return -ENOMEM; - /* Initialize all port interrupt end point to port 0 int endpoint. - * Our device has only one interrupt endpoint common to all ports. - */ - port->interrupt_in_endpointAddress = - port->serial->port[0]->interrupt_in_endpointAddress; mos7720_port->port = port; usb_set_serial_port_data(port, mos7720_port); @@ -2056,7 +2040,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .close = mos7720_close, .throttle = mos7720_throttle, .unthrottle = mos7720_unthrottle, - .probe = mos77xx_probe, .attach = mos7720_startup, .release = mos7720_release, .port_probe = mos7720_port_probe, @@ -2070,7 +2053,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { .chars_in_buffer = mos7720_chars_in_buffer, .break_ctl = mos7720_break, .read_bulk_callback = mos7720_bulk_in_callback, - .read_int_callback = NULL /* dynamically assigned in probe() */ + .read_int_callback = mos7720_interrupt_callback, }; static struct usb_serial_driver * const serial_drivers[] = { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 57426d703a09..ea27fb23967a 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -214,7 +214,6 @@ MODULE_DEVICE_TABLE(usb, id_table); struct moschip_port { int port_num; /*Actual port number in the device(1,2,etc) */ - struct urb *write_urb; /* write URB for this port */ struct urb *read_urb; /* read URB for this port */ __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ @@ -1037,9 +1036,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) serial, serial->port[0]->interrupt_in_urb->interval); - /* start interrupt read for mos7840 * - * will continue as long as mos7840 is connected */ - + /* start interrupt read for mos7840 */ response = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); @@ -1186,7 +1183,6 @@ static void mos7840_close(struct usb_serial_port *port) } } - usb_kill_urb(mos7840_port->write_urb); usb_kill_urb(mos7840_port->read_urb); mos7840_port->read_urb_busy = false; @@ -1199,12 +1195,6 @@ static void mos7840_close(struct usb_serial_port *port) } } - if (mos7840_port->write_urb) { - /* if this urb had a transfer buffer already (old tx) free it */ - kfree(mos7840_port->write_urb->transfer_buffer); - usb_free_urb(mos7840_port->write_urb); - } - Data = 0x0; mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); @@ -1956,9 +1946,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; @@ -2116,6 +2103,17 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) return mos7840_num_ports; } +static int mos7840_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int mos7840_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -2391,6 +2389,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .tiocmset = mos7840_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, + .attach = mos7840_attach, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index f6c6900bccf0..a180b17d2432 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room(struct tty_struct *tty); static void omninet_disconnect(struct usb_serial *serial); +static int omninet_attach(struct usb_serial *serial); static int omninet_port_probe(struct usb_serial_port *port); static int omninet_port_remove(struct usb_serial_port *port); @@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net lcd plus usb", .id_table = id_table, .num_ports = 1, + .attach = omninet_attach, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, .open = omninet_open, @@ -104,6 +106,17 @@ struct omninet_data { __u8 od_outseq; /* Sequence number for bulk_out URBs */ }; +static int omninet_attach(struct usb_serial *serial) +{ + /* The second bulk-out endpoint is used for writing. */ + if (serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; 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/option.c b/drivers/usb/serial/option.c index 9894e341c6ac..42cc72e54c05 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -268,6 +268,8 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_CC864_SINGLE 0x1006 #define TELIT_PRODUCT_DE910_DUAL 0x1010 #define TELIT_PRODUCT_UE910_V2 0x1012 +#define TELIT_PRODUCT_LE922_USBCFG1 0x1040 +#define TELIT_PRODUCT_LE922_USBCFG2 0x1041 #define TELIT_PRODUCT_LE922_USBCFG0 0x1042 #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 @@ -1210,6 +1212,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1), + .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2), + .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), @@ -1989,6 +1995,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ @@ -2000,6 +2007,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a4b88bc038b6..b8bf52bf7a94 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int oti6858_attach(struct usb_serial *serial); static int oti6858_port_probe(struct usb_serial_port *port); static int oti6858_port_remove(struct usb_serial_port *port); @@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = { .write_bulk_callback = oti6858_write_bulk_callback, .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, + .attach = oti6858_attach, .port_probe = oti6858_port_probe, .port_remove = oti6858_port_remove, }; @@ -324,6 +326,20 @@ static void send_data(struct work_struct *work) usb_serial_port_softint(port); } +static int oti6858_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int oti6858_port_probe(struct usb_serial_port *port) { struct oti6858_private *priv; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ae682e4eeaef..1db4b61bdf7b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, @@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial, static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; + unsigned char num_ports = serial->num_ports; enum pl2303_type type = TYPE_01; unsigned char *buf; + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index e3b7af8adfb7..09d9be88209e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -27,6 +27,7 @@ #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 +#define ATEN_PRODUCT_ID2 0x2118 #define IODATA_VENDOR_ID 0x04bb #define IODATA_PRODUCT_ID 0x0a03 diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1bc6089b9008..696458db7e3c 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ + {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */ {USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */ {USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ {USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */ diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 85acb50a7ee2..5709cc93b083 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port) { struct usb_serial *serial; struct qt2_port_private *port_priv; - unsigned long flags; int i; serial = port->serial; port_priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&port_priv->urb_lock, flags); usb_kill_urb(port_priv->write_urb); - port_priv->urb_in_use = false; - spin_unlock_irqrestore(&port_priv->urb_lock, flags); /* flush the port transmit buffer */ i = usb_control_msg(serial->dev, @@ -463,9 +459,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/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index ef0dbf0703c5..475e6c31b266 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial, return 0; } +static int spcp8x5_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int spcp8x5_port_probe(struct usb_serial_port *port) { const struct usb_device_id *id = usb_get_serial_data(port->serial); @@ -477,6 +490,7 @@ static struct usb_serial_driver spcp8x5_device = { .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, .probe = spcp8x5_probe, + .attach = spcp8x5_attach, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; 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..64b85b8dedf3 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -579,6 +579,13 @@ static int ti_startup(struct usb_serial *serial) goto free_tdev; } + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + status = -ENODEV; + goto free_tdev; + } + return 0; free_tdev: @@ -1426,9 +1433,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; |