Merge tag 'usb-serial-5.11-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 11 Dec 2020 15:16:52 +0000 (16:16 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 11 Dec 2020 15:16:52 +0000 (16:16 +0100)
Johan writes:

USB-serial updates for 5.11-rc1

Here are the USB-serial updates for 5.11-rc1, including:

 - keyspan_pda write-implementation fixes
 - digi_acceleport write-wakeup fix
 - mos7720 parport-restore fix
 - mos7720 parport-tasklet removal
 - cp210x termios-handling cleanups
 - option device-flag fix
 - ftdi_sio GPIO CBUS-configuration improvements
 - removal of in_interrupt() uses

Included are also various clean ups.

All have been in linux-next with no reported issues.

* tag 'usb-serial-5.11-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial: (30 commits)
  USB: serial: ftdi_sio: log the CBUS GPIO validity
  USB: serial: ftdi_sio: drop GPIO line checking dead code
  USB: serial: ftdi_sio: report the valid GPIO lines to gpiolib
  USB: serial: option: add interface-number sanity check to flag handling
  USB: serial: cp210x: clean up dtr_rts()
  USB: serial: cp210x: refactor flow-control handling
  USB: serial: cp210x: drop flow-control debugging
  USB: serial: cp210x: set terminal settings on open
  USB: serial: cp210x: clean up line-control handling
  USB: serial: cp210x: return early on unchanged termios
  USB: serial: mos7720: defer state restore to a workqueue
  USB: serial: mos7720: fix parallel-port state restore
  USB: serial: remove write wait queue
  USB: serial: digi_acceleport: fix write-wakeup deadlocks
  USB: serial: keyspan_pda: drop redundant usb-serial pointer
  USB: serial: keyspan_pda: use BIT() macro
  USB: serial: keyspan_pda: clean up comments and whitespace
  USB: serial: keyspan_pda: clean up xircom/entrega support
  USB: serial: keyspan_pda: add write-fifo support
  USB: serial: keyspan_pda: increase transmitter threshold
  ...

18 files changed:
arch/arm/configs/badge4_defconfig
arch/arm/configs/corgi_defconfig
arch/arm/configs/pxa_defconfig
arch/arm/configs/spitz_defconfig
arch/mips/configs/mtx1_defconfig
arch/mips/configs/rm200_defconfig
arch/powerpc/configs/g5_defconfig
arch/powerpc/configs/ppc6xx_defconfig
drivers/usb/serial/Kconfig
drivers/usb/serial/Makefile
drivers/usb/serial/cp210x.c
drivers/usb/serial/digi_acceleport.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/iuu_phoenix.c
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/mos7720.c
drivers/usb/serial/option.c
include/linux/usb/serial.h

index ef484c4..d9119da 100644 (file)
@@ -89,7 +89,6 @@ CONFIG_USB_SERIAL_KEYSPAN=m
 CONFIG_USB_SERIAL_MCT_U232=m
 CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_EXT2_FS=m
 CONFIG_EXT3_FS=m
index 4fec2ec..911e880 100644 (file)
@@ -191,7 +191,6 @@ CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_EMI62=m
 CONFIG_USB_EMI26=m
index d7b9eaf..8654ece 100644 (file)
@@ -574,7 +574,6 @@ CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_EMI62=m
 CONFIG_USB_EMI26=m
index a1cdbfa..8b2c144 100644 (file)
@@ -185,7 +185,6 @@ CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_EMI62=m
 CONFIG_USB_EMI26=m
index 914af12..9750bcc 100644 (file)
@@ -565,7 +565,6 @@ CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_SIERRAWIRELESS=m
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OPTION=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_EMI62=m
index 30d7c3d..3dc2da2 100644 (file)
@@ -311,7 +311,6 @@ CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_SAFE_PADDED=y
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_LEGOTOWER=m
 CONFIG_USB_LCD=m
index 1c674c4..1de0dbf 100644 (file)
@@ -194,7 +194,6 @@ CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_SAFE_PADDED=y
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_APPLEDISPLAY=m
 CONFIG_EXT2_FS=y
index 66e9a0f..ac92cbe 100644 (file)
@@ -911,7 +911,6 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y
 CONFIG_USB_SERIAL_SIERRAWIRELESS=m
 CONFIG_USB_SERIAL_TI=m
 CONFIG_USB_SERIAL_CYBERJACK=m
-CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OPTION=m
 CONFIG_USB_SERIAL_OMNINET=m
 CONFIG_USB_SERIAL_DEBUG=m
index 4007fa2..a21ff5a 100644 (file)
@@ -298,12 +298,12 @@ config USB_SERIAL_IUU
          module will be called iuu_phoenix.o
 
 config USB_SERIAL_KEYSPAN_PDA
-       tristate "USB Keyspan PDA Single Port Serial Driver"
+       tristate "USB Keyspan PDA / Xircom Single Port Serial Driver"
        select USB_EZUSB_FX2
        help
-         Say Y here if you want to use a Keyspan PDA single port USB to
-         serial converter device.  This driver makes use of firmware
-         developed from scratch by Brian Warner.
+         Say Y here if you want to use a Keyspan PDA, Xircom or Entrega single
+         port USB to serial converter device. This driver makes use of
+         firmware developed from scratch by Brian Warner.
 
          To compile this driver as a module, choose M here: the
          module will be called keyspan_pda.
@@ -538,17 +538,6 @@ config USB_SERIAL_CYBERJACK
 
          If unsure, say N.
 
-config USB_SERIAL_XIRCOM
-       tristate "USB Xircom / Entrega Single Port Serial Driver"
-       select USB_EZUSB_FX2
-       help
-         Say Y here if you want to use a Xircom or Entrega single port USB to
-         serial converter device.  This driver makes use of firmware
-         developed from scratch by Brian Warner.
-
-         To compile this driver as a module, choose M here: the
-         module will be called keyspan_pda.
-
 config USB_SERIAL_WWAN
        tristate
 
index 2d491e4..50c53ae 100644 (file)
@@ -61,5 +61,4 @@ obj-$(CONFIG_USB_SERIAL_UPD78F0730)           += upd78f0730.o
 obj-$(CONFIG_USB_SERIAL_VISOR)                 += visor.o
 obj-$(CONFIG_USB_SERIAL_WISHBONE)              += wishbone-serial.o
 obj-$(CONFIG_USB_SERIAL_WHITEHEAT)             += whiteheat.o
-obj-$(CONFIG_USB_SERIAL_XIRCOM)                        += keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_XSENS_MT)              += xsens_mt.o
index d0c05aa..fbb10df 100644 (file)
@@ -31,9 +31,6 @@
  */
 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,
-       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 *,
@@ -49,7 +46,7 @@ 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);
+static void cp210x_dtr_rts(struct usb_serial_port *port, int on);
 static void cp210x_process_read_urb(struct urb *urb);
 static void cp210x_enable_event_mode(struct usb_serial_port *port);
 static void cp210x_disable_event_mode(struct usb_serial_port *port);
@@ -267,7 +264,6 @@ enum cp210x_event_state {
 
 struct cp210x_port_private {
        u8                      bInterfaceNumber;
-       bool                    has_swapped_line_ctl;
        bool                    event_mode;
        enum cp210x_event_state event_state;
        u8 lsr;
@@ -559,14 +555,8 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
        int result;
 
        dmabuf = kmalloc(bufsize, GFP_KERNEL);
-       if (!dmabuf) {
-               /*
-                * FIXME Some callers don't bother to check for error,
-                * at least give them consistent junk until they are fixed
-                */
-               memset(buf, 0, bufsize);
+       if (!dmabuf)
                return -ENOMEM;
-       }
 
        result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
                        req, REQTYPE_INTERFACE_TO_HOST, 0,
@@ -580,12 +570,6 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
                                req, bufsize, result);
                if (result >= 0)
                        result = -EIO;
-
-               /*
-                * FIXME Some callers don't bother to check for error,
-                * at least give them consistent junk until they are fixed
-                */
-               memset(buf, 0, bufsize);
        }
 
        kfree(dmabuf);
@@ -593,46 +577,6 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
        return result;
 }
 
-/*
- * Reads any 32-bit CP210X_ register identified by req.
- */
-static int cp210x_read_u32_reg(struct usb_serial_port *port, u8 req, u32 *val)
-{
-       __le32 le32_val;
-       int err;
-
-       err = cp210x_read_reg_block(port, req, &le32_val, sizeof(le32_val));
-       if (err) {
-               /*
-                * FIXME Some callers don't bother to check for error,
-                * at least give them consistent junk until they are fixed
-                */
-               *val = 0;
-               return err;
-       }
-
-       *val = le32_to_cpu(le32_val);
-
-       return 0;
-}
-
-/*
- * Reads any 16-bit CP210X_ register identified by req.
- */
-static int cp210x_read_u16_reg(struct usb_serial_port *port, u8 req, u16 *val)
-{
-       __le16 le16_val;
-       int err;
-
-       err = cp210x_read_reg_block(port, req, &le16_val, sizeof(le16_val));
-       if (err)
-               return err;
-
-       *val = le16_to_cpu(le16_val);
-
-       return 0;
-}
-
 /*
  * Reads any 8-bit CP210X_ register identified by req.
  */
@@ -780,59 +724,6 @@ static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type,
 }
 #endif
 
-/*
- * Detect CP2108 GET_LINE_CTL bug and activate workaround.
- * Write a known good value 0x800, read it back.
- * If it comes back swapped the bug is detected.
- * Preserve the original register value.
- */
-static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port)
-{
-       struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
-       u16 line_ctl_save;
-       u16 line_ctl_test;
-       int err;
-
-       err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_save);
-       if (err)
-               return err;
-
-       err = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, 0x800);
-       if (err)
-               return err;
-
-       err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_test);
-       if (err)
-               return err;
-
-       if (line_ctl_test == 8) {
-               port_priv->has_swapped_line_ctl = true;
-               line_ctl_save = swab16(line_ctl_save);
-       }
-
-       return cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, line_ctl_save);
-}
-
-/*
- * Must always be called instead of cp210x_read_u16_reg(CP210X_GET_LINE_CTL)
- * to workaround cp2108 bug and get correct value.
- */
-static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)
-{
-       struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
-       int err;
-
-       err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, ctl);
-       if (err)
-               return err;
-
-       /* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
-       if (port_priv->has_swapped_line_ctl)
-               *ctl = swab16(*ctl);
-
-       return 0;
-}
-
 static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
 {
        struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
@@ -844,16 +735,8 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
                return result;
        }
 
-       /* Configure the termios structure */
-       cp210x_get_termios(tty, port);
-
-       if (tty) {
-               /* The baud rate must be initialised on cp2104 */
-               cp210x_change_speed(tty, port, NULL);
-
-               if (I_INPCK(tty))
-                       cp210x_enable_event_mode(port);
-       }
+       if (tty)
+               cp210x_set_termios(tty, port, NULL);
 
        result = usb_serial_generic_open(tty, port);
        if (result)
@@ -1032,167 +915,6 @@ static bool cp210x_tx_empty(struct usb_serial_port *port)
        return !count;
 }
 
-/*
- * cp210x_get_termios
- * Reads the baud rate, data bits, parity, stop bits and flow control mode
- * from the device, corrects any unsupported values, and configures the
- * termios structure to reflect the state of the device
- */
-static void cp210x_get_termios(struct tty_struct *tty,
-       struct usb_serial_port *port)
-{
-       unsigned int baud;
-
-       if (tty) {
-               cp210x_get_termios_port(tty->driver_data,
-                       &tty->termios.c_cflag, &baud);
-               tty_encode_baud_rate(tty, baud, baud);
-       } else {
-               tcflag_t cflag;
-               cflag = 0;
-               cp210x_get_termios_port(port, &cflag, &baud);
-       }
-}
-
-/*
- * cp210x_get_termios_port
- * 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,
-       tcflag_t *cflagp, unsigned int *baudp)
-{
-       struct device *dev = &port->dev;
-       tcflag_t cflag;
-       struct cp210x_flow_ctl flow_ctl;
-       u32 baud;
-       u16 bits;
-       u32 ctl_hs;
-       u32 flow_repl;
-
-       cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud);
-
-       dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
-       *baudp = baud;
-
-       cflag = *cflagp;
-
-       cp210x_get_line_ctl(port, &bits);
-       cflag &= ~CSIZE;
-       switch (bits & BITS_DATA_MASK) {
-       case BITS_DATA_5:
-               dev_dbg(dev, "%s - data bits = 5\n", __func__);
-               cflag |= CS5;
-               break;
-       case BITS_DATA_6:
-               dev_dbg(dev, "%s - data bits = 6\n", __func__);
-               cflag |= CS6;
-               break;
-       case BITS_DATA_7:
-               dev_dbg(dev, "%s - data bits = 7\n", __func__);
-               cflag |= CS7;
-               break;
-       case BITS_DATA_8:
-               dev_dbg(dev, "%s - data bits = 8\n", __func__);
-               cflag |= CS8;
-               break;
-       case BITS_DATA_9:
-               dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__);
-               cflag |= CS8;
-               bits &= ~BITS_DATA_MASK;
-               bits |= BITS_DATA_8;
-               cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
-               break;
-       default:
-               dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__);
-               cflag |= CS8;
-               bits &= ~BITS_DATA_MASK;
-               bits |= BITS_DATA_8;
-               cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
-               break;
-       }
-
-       switch (bits & BITS_PARITY_MASK) {
-       case BITS_PARITY_NONE:
-               dev_dbg(dev, "%s - parity = NONE\n", __func__);
-               cflag &= ~PARENB;
-               break;
-       case BITS_PARITY_ODD:
-               dev_dbg(dev, "%s - parity = ODD\n", __func__);
-               cflag |= (PARENB|PARODD);
-               break;
-       case BITS_PARITY_EVEN:
-               dev_dbg(dev, "%s - parity = EVEN\n", __func__);
-               cflag &= ~PARODD;
-               cflag |= PARENB;
-               break;
-       case BITS_PARITY_MARK:
-               dev_dbg(dev, "%s - parity = MARK\n", __func__);
-               cflag |= (PARENB|PARODD|CMSPAR);
-               break;
-       case BITS_PARITY_SPACE:
-               dev_dbg(dev, "%s - parity = SPACE\n", __func__);
-               cflag &= ~PARODD;
-               cflag |= (PARENB|CMSPAR);
-               break;
-       default:
-               dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__);
-               cflag &= ~PARENB;
-               bits &= ~BITS_PARITY_MASK;
-               cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
-               break;
-       }
-
-       cflag &= ~CSTOPB;
-       switch (bits & BITS_STOP_MASK) {
-       case BITS_STOP_1:
-               dev_dbg(dev, "%s - stop bits = 1\n", __func__);
-               break;
-       case BITS_STOP_1_5:
-               dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__);
-               bits &= ~BITS_STOP_MASK;
-               cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
-               break;
-       case BITS_STOP_2:
-               dev_dbg(dev, "%s - stop bits = 2\n", __func__);
-               cflag |= CSTOPB;
-               break;
-       default:
-               dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__);
-               bits &= ~BITS_STOP_MASK;
-               cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
-               break;
-       }
-
-       cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
-                       sizeof(flow_ctl));
-       ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
-       if (ctl_hs & CP210X_SERIAL_CTS_HANDSHAKE) {
-               dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
-               /*
-                * When the port is closed, the CP210x hardware disables
-                * auto-RTS and RTS is deasserted but it leaves auto-CTS when
-                * in hardware flow control mode. When re-opening the port, if
-                * auto-CTS is enabled on the cp210x, then auto-RTS must be
-                * re-enabled in the driver.
-                */
-               flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
-               flow_repl &= ~CP210X_SERIAL_RTS_MASK;
-               flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL);
-               flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
-               cp210x_write_reg_block(port,
-                               CP210X_SET_FLOW,
-                               &flow_ctl,
-                               sizeof(flow_ctl));
-
-               cflag |= CRTSCTS;
-       } else {
-               dev_dbg(dev, "%s - flow control = NONE\n", __func__);
-               cflag &= ~CRTSCTS;
-       }
-
-       *cflagp = cflag;
-}
-
 struct cp210x_rate {
        speed_t rate;
        speed_t high;
@@ -1352,127 +1074,121 @@ static void cp210x_disable_event_mode(struct usb_serial_port *port)
        port_priv->event_mode = false;
 }
 
+static bool cp210x_termios_change(const struct ktermios *a, const struct ktermios *b)
+{
+       bool iflag_change;
+
+       iflag_change = ((a->c_iflag ^ b->c_iflag) & INPCK);
+
+       return tty_termios_hw_change(a, b) || iflag_change;
+}
+
+static void cp210x_set_flow_control(struct tty_struct *tty,
+               struct usb_serial_port *port, struct ktermios *old_termios)
+{
+       struct cp210x_flow_ctl flow_ctl;
+       u32 flow_repl;
+       u32 ctl_hs;
+       int ret;
+
+       if (old_termios && C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS))
+               return;
+
+       ret = cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
+                       sizeof(flow_ctl));
+       if (ret)
+               return;
+
+       ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
+       flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
+
+       ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
+       ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
+       ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
+       ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
+       ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE);
+
+       if (C_CRTSCTS(tty)) {
+               ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
+               flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+               flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL);
+       } else {
+               ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
+               flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+               flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_ACTIVE);
+       }
+
+       dev_dbg(&port->dev, "%s - ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
+                       __func__, ctl_hs, flow_repl);
+
+       flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs);
+       flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
+
+       cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
+                       sizeof(flow_ctl));
+}
+
 static void cp210x_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {
-       struct device *dev = &port->dev;
-       unsigned int cflag, old_cflag;
+       struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
        u16 bits;
+       int ret;
 
-       cflag = tty->termios.c_cflag;
-       old_cflag = old_termios->c_cflag;
+       if (old_termios && !cp210x_termios_change(&tty->termios, old_termios))
+               return;
 
-       if (tty->termios.c_ospeed != old_termios->c_ospeed)
+       if (!old_termios || tty->termios.c_ospeed != old_termios->c_ospeed)
                cp210x_change_speed(tty, port, old_termios);
 
-       /* If the number of data bits is to be updated */
-       if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
-               cp210x_get_line_ctl(port, &bits);
-               bits &= ~BITS_DATA_MASK;
-               switch (cflag & CSIZE) {
-               case CS5:
-                       bits |= BITS_DATA_5;
-                       dev_dbg(dev, "%s - data bits = 5\n", __func__);
-                       break;
-               case CS6:
-                       bits |= BITS_DATA_6;
-                       dev_dbg(dev, "%s - data bits = 6\n", __func__);
-                       break;
-               case CS7:
-                       bits |= BITS_DATA_7;
-                       dev_dbg(dev, "%s - data bits = 7\n", __func__);
-                       break;
-               case CS8:
-               default:
-                       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))
-                       dev_dbg(dev, "Number of data bits requested not supported by device\n");
+       /* CP2101 only supports CS8, 1 stop bit and non-stick parity. */
+       if (priv->partnum == CP210X_PARTNUM_CP2101) {
+               tty->termios.c_cflag &= ~(CSIZE | CSTOPB | CMSPAR);
+               tty->termios.c_cflag |= CS8;
        }
 
-       if ((cflag     & (PARENB|PARODD|CMSPAR)) !=
-           (old_cflag & (PARENB|PARODD|CMSPAR))) {
-               cp210x_get_line_ctl(port, &bits);
-               bits &= ~BITS_PARITY_MASK;
-               if (cflag & PARENB) {
-                       if (cflag & CMSPAR) {
-                               if (cflag & PARODD) {
-                                       bits |= BITS_PARITY_MARK;
-                                       dev_dbg(dev, "%s - parity = MARK\n", __func__);
-                               } else {
-                                       bits |= BITS_PARITY_SPACE;
-                                       dev_dbg(dev, "%s - parity = SPACE\n", __func__);
-                               }
-                       } else {
-                               if (cflag & PARODD) {
-                                       bits |= BITS_PARITY_ODD;
-                                       dev_dbg(dev, "%s - parity = ODD\n", __func__);
-                               } else {
-                                       bits |= BITS_PARITY_EVEN;
-                                       dev_dbg(dev, "%s - parity = EVEN\n", __func__);
-                               }
-                       }
-               }
-               if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
-                       dev_dbg(dev, "Parity mode not supported by device\n");
+       bits = 0;
+
+       switch (C_CSIZE(tty)) {
+       case CS5:
+               bits |= BITS_DATA_5;
+               break;
+       case CS6:
+               bits |= BITS_DATA_6;
+               break;
+       case CS7:
+               bits |= BITS_DATA_7;
+               break;
+       case CS8:
+       default:
+               bits |= BITS_DATA_8;
+               break;
        }
 
-       if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
-               cp210x_get_line_ctl(port, &bits);
-               bits &= ~BITS_STOP_MASK;
-               if (cflag & CSTOPB) {
-                       bits |= BITS_STOP_2;
-                       dev_dbg(dev, "%s - stop bits = 2\n", __func__);
+       if (C_PARENB(tty)) {
+               if (C_CMSPAR(tty)) {
+                       if (C_PARODD(tty))
+                               bits |= BITS_PARITY_MARK;
+                       else
+                               bits |= BITS_PARITY_SPACE;
                } else {
-                       bits |= BITS_STOP_1;
-                       dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+                       if (C_PARODD(tty))
+                               bits |= BITS_PARITY_ODD;
+                       else
+                               bits |= BITS_PARITY_EVEN;
                }
-               if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
-                       dev_dbg(dev, "Number of stop bits requested not supported by device\n");
        }
 
-       if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
-               struct cp210x_flow_ctl flow_ctl;
-               u32 ctl_hs;
-               u32 flow_repl;
-
-               cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
-                               sizeof(flow_ctl));
-               ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
-               flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
-               dev_dbg(dev, "%s - read ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
-                               __func__, ctl_hs, flow_repl);
-
-               ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
-               ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
-               ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
-               ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
-               ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE);
-               if (cflag & CRTSCTS) {
-                       ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
-
-                       flow_repl &= ~CP210X_SERIAL_RTS_MASK;
-                       flow_repl |= CP210X_SERIAL_RTS_SHIFT(
-                                       CP210X_SERIAL_RTS_FLOW_CTL);
-                       dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
-               } else {
-                       ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
+       if (C_CSTOPB(tty))
+               bits |= BITS_STOP_2;
+       else
+               bits |= BITS_STOP_1;
 
-                       flow_repl &= ~CP210X_SERIAL_RTS_MASK;
-                       flow_repl |= CP210X_SERIAL_RTS_SHIFT(
-                                       CP210X_SERIAL_RTS_ACTIVE);
-                       dev_dbg(dev, "%s - flow control = NONE\n", __func__);
-               }
+       ret = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+       if (ret)
+               dev_err(&port->dev, "failed to set line control: %d\n", ret);
 
-               dev_dbg(dev, "%s - write ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
-                               __func__, ctl_hs, flow_repl);
-               flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs);
-               flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
-               cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
-                               sizeof(flow_ctl));
-       }
+       cp210x_set_flow_control(tty, port, old_termios);
 
        /*
         * Enable event-insertion mode only if input parity checking is
@@ -1518,12 +1234,12 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port,
        return cp210x_write_u16_reg(port, CP210X_SET_MHS, control);
 }
 
-static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
+static void cp210x_dtr_rts(struct usb_serial_port *port, int on)
 {
        if (on)
-               cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0);
+               cp210x_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0);
        else
-               cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS);
+               cp210x_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS);
 }
 
 static int cp210x_tiocmget(struct tty_struct *tty)
@@ -1986,7 +1702,6 @@ static int cp210x_port_probe(struct usb_serial_port *port)
 {
        struct usb_serial *serial = port->serial;
        struct cp210x_port_private *port_priv;
-       int ret;
 
        port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
        if (!port_priv)
@@ -1996,12 +1711,6 @@ static int cp210x_port_probe(struct usb_serial_port *port)
 
        usb_set_serial_port_data(port, port_priv);
 
-       ret = cp210x_detect_swapped_line_ctl(port);
-       if (ret) {
-               kfree(port_priv);
-               return ret;
-       }
-
        return 0;
 }
 
index 91055a1..0ecd531 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
-#include <linux/workqueue.h>
 #include <linux/uaccess.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
@@ -198,14 +197,13 @@ struct digi_port {
        int dp_throttle_restart;
        wait_queue_head_t dp_flush_wait;
        wait_queue_head_t dp_close_wait;        /* wait queue for close */
-       struct work_struct dp_wakeup_work;
+       wait_queue_head_t write_wait;
        struct usb_serial_port *dp_port;
 };
 
 
 /* Local Function Declarations */
 
-static void digi_wakeup_write_lock(struct work_struct *work);
 static int digi_write_oob_command(struct usb_serial_port *port,
        unsigned char *buf, int count, int interruptible);
 static int digi_write_inb_command(struct usb_serial_port *port,
@@ -356,26 +354,6 @@ __releases(lock)
        return timeout;
 }
 
-
-/*
- *  Digi Wakeup Write
- *
- *  Wake up port, line discipline, and tty processes sleeping
- *  on writes.
- */
-
-static void digi_wakeup_write_lock(struct work_struct *work)
-{
-       struct digi_port *priv =
-                       container_of(work, struct digi_port, dp_wakeup_work);
-       struct usb_serial_port *port = priv->dp_port;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->dp_port_lock, flags);
-       tty_port_tty_wakeup(&port->port);
-       spin_unlock_irqrestore(&priv->dp_port_lock, flags);
-}
-
 /*
  *  Digi Write OOB Command
  *
@@ -404,7 +382,7 @@ static int digi_write_oob_command(struct usb_serial_port *port,
        while (count > 0) {
                while (oob_priv->dp_write_urb_in_use) {
                        cond_wait_interruptible_timeout_irqrestore(
-                               &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+                               &oob_priv->write_wait, DIGI_RETRY_TIMEOUT,
                                &oob_priv->dp_port_lock, flags);
                        if (interruptible && signal_pending(current))
                                return -EINTR;
@@ -467,7 +445,7 @@ static int digi_write_inb_command(struct usb_serial_port *port,
                while (priv->dp_write_urb_in_use &&
                       time_before(jiffies, timeout)) {
                        cond_wait_interruptible_timeout_irqrestore(
-                               &port->write_wait, DIGI_RETRY_TIMEOUT,
+                               &priv->write_wait, DIGI_RETRY_TIMEOUT,
                                &priv->dp_port_lock, flags);
                        if (signal_pending(current))
                                return -EINTR;
@@ -546,7 +524,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port,
        while (oob_priv->dp_write_urb_in_use) {
                spin_unlock(&port_priv->dp_port_lock);
                cond_wait_interruptible_timeout_irqrestore(
-                       &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+                       &oob_priv->write_wait, DIGI_RETRY_TIMEOUT,
                        &oob_priv->dp_port_lock, flags);
                if (interruptible && signal_pending(current))
                        return -EINTR;
@@ -911,9 +889,8 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
        unsigned char *data = port->write_urb->transfer_buffer;
        unsigned long flags = 0;
 
-       dev_dbg(&port->dev,
-               "digi_write: TOP: port=%d, count=%d, in_interrupt=%ld\n",
-               priv->dp_port_num, count, in_interrupt());
+       dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n",
+               priv->dp_port_num, count);
 
        /* copy user data (which can sleep) before getting spin lock */
        count = min(count, port->bulk_out_size-2);
@@ -986,6 +963,7 @@ static void digi_write_bulk_callback(struct urb *urb)
        unsigned long flags;
        int ret = 0;
        int status = urb->status;
+       bool wakeup;
 
        /* port and serial sanity check */
        if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
@@ -1006,12 +984,13 @@ static void digi_write_bulk_callback(struct urb *urb)
                dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n");
                spin_lock_irqsave(&priv->dp_port_lock, flags);
                priv->dp_write_urb_in_use = 0;
-               wake_up_interruptible(&port->write_wait);
+               wake_up_interruptible(&priv->write_wait);
                spin_unlock_irqrestore(&priv->dp_port_lock, flags);
                return;
        }
 
        /* try to send any buffered data on this port */
+       wakeup = true;
        spin_lock_irqsave(&priv->dp_port_lock, flags);
        priv->dp_write_urb_in_use = 0;
        if (priv->dp_out_buf_len > 0) {
@@ -1027,19 +1006,18 @@ static void digi_write_bulk_callback(struct urb *urb)
                if (ret == 0) {
                        priv->dp_write_urb_in_use = 1;
                        priv->dp_out_buf_len = 0;
+                       wakeup = false;
                }
        }
-       /* wake up processes sleeping on writes immediately */
-       tty_port_tty_wakeup(&port->port);
-       /* also queue up a wakeup at scheduler time, in case we */
-       /* lost the race in write_chan(). */
-       schedule_work(&priv->dp_wakeup_work);
-
        spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
        if (ret && ret != -EPERM)
                dev_err_console(port,
                        "%s: usb_submit_urb failed, ret=%d, port=%d\n",
                        __func__, ret, priv->dp_port_num);
+
+       if (wakeup)
+               tty_port_tty_wakeup(&port->port);
 }
 
 static int digi_write_room(struct tty_struct *tty)
@@ -1239,11 +1217,9 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
        init_waitqueue_head(&priv->dp_transmit_idle_wait);
        init_waitqueue_head(&priv->dp_flush_wait);
        init_waitqueue_head(&priv->dp_close_wait);
-       INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
+       init_waitqueue_head(&priv->write_wait);
        priv->dp_port = port;
 
-       init_waitqueue_head(&port->write_wait);
-
        usb_set_serial_port_data(port, priv);
 
        return 0;
@@ -1508,13 +1484,14 @@ static int digi_read_oob_callback(struct urb *urb)
                        rts = C_CRTSCTS(tty);
 
                if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+                       bool wakeup = false;
+
                        spin_lock_irqsave(&priv->dp_port_lock, flags);
                        /* convert from digi flags to termiox flags */
                        if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
                                priv->dp_modem_signals |= TIOCM_CTS;
-                               /* port must be open to use tty struct */
                                if (rts)
-                                       tty_port_tty_wakeup(&port->port);
+                                       wakeup = true;
                        } else {
                                priv->dp_modem_signals &= ~TIOCM_CTS;
                                /* port must be open to use tty struct */
@@ -1533,6 +1510,9 @@ static int digi_read_oob_callback(struct urb *urb)
                                priv->dp_modem_signals &= ~TIOCM_CD;
 
                        spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
+                       if (wakeup)
+                               tty_port_tty_wakeup(&port->port);
                } else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
                        spin_lock_irqsave(&priv->dp_port_lock, flags);
                        priv->dp_transmit_idle = 1;
index e0f4c3d..94398f8 100644 (file)
@@ -1841,9 +1841,6 @@ static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset)
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        int result;
 
-       if (priv->gpio_altfunc & BIT(offset))
-               return -ENODEV;
-
        mutex_lock(&priv->gpio_lock);
        if (!priv->gpio_used) {
                /* Set default pin states, as we cannot get them from device */
@@ -2002,6 +1999,25 @@ static int ftdi_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
        return result;
 }
 
+static int ftdi_gpio_init_valid_mask(struct gpio_chip *gc,
+                                    unsigned long *valid_mask,
+                                    unsigned int ngpios)
+{
+       struct usb_serial_port *port = gpiochip_get_data(gc);
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       unsigned long map = priv->gpio_altfunc;
+
+       bitmap_complement(valid_mask, &map, ngpios);
+
+       if (bitmap_empty(valid_mask, ngpios))
+               dev_dbg(&port->dev, "no CBUS pin configured for GPIO\n");
+       else
+               dev_dbg(&port->dev, "CBUS%*pbl configured for GPIO\n", ngpios,
+                       valid_mask);
+
+       return 0;
+}
+
 static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
                                u16 nbytes)
 {
@@ -2173,6 +2189,7 @@ static int ftdi_gpio_init(struct usb_serial_port *port)
        priv->gc.get_direction = ftdi_gpio_direction_get;
        priv->gc.direction_input = ftdi_gpio_direction_input;
        priv->gc.direction_output = ftdi_gpio_direction_output;
+       priv->gc.init_valid_mask = ftdi_gpio_init_valid_mask;
        priv->gc.get = ftdi_gpio_get;
        priv->gc.set = ftdi_gpio_set;
        priv->gc.get_multiple = ftdi_gpio_get_multiple;
index b4ba791..f1201d4 100644 (file)
@@ -850,7 +850,6 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
        default:
                kfree(dataout);
                return IUU_INVALID_PARAMETER;
-               break;
        }
 
        switch (parity & 0xF0) {
@@ -864,7 +863,6 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
        default:
                kfree(dataout);
                return IUU_INVALID_PARAMETER;
-               break;
        }
 
        status = bulk_immediate(port, dataout, DataCount);
index c133391..e6f933e 100644 (file)
@@ -5,12 +5,12 @@
  * Copyright (C) 1999 - 2001 Greg Kroah-Hartman        <greg@kroah.com>
  * Copyright (C) 1999, 2000 Brian Warner       <warner@lothar.com>
  * Copyright (C) 2000 Al Borchers              <borchers@steinerpoint.com>
+ * Copyright (C) 2020 Johan Hovold <johan@kernel.org>
  *
  * See Documentation/usb/usb-serial.rst for more information on using this
  * driver
  */
 
-
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/usb/serial.h>
 #include <linux/usb/ezusb.h>
 
-/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
-#if IS_ENABLED(CONFIG_USB_SERIAL_KEYSPAN_PDA)
-       #define KEYSPAN
-#else
-       #undef KEYSPAN
-#endif
-#if IS_ENABLED(CONFIG_USB_SERIAL_XIRCOM)
-       #define XIRCOM
-#else
-       #undef XIRCOM
-#endif
-
-#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>"
+#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>, Johan Hovold <johan@kernel.org>"
 #define DRIVER_DESC "USB Keyspan PDA Converter driver"
 
+#define KEYSPAN_TX_THRESHOLD   128
+
 struct keyspan_pda_private {
        int                     tx_room;
-       int                     tx_throttled;
-       struct work_struct                      wakeup_work;
-       struct work_struct                      unthrottle_work;
+       struct work_struct      unthrottle_work;
        struct usb_serial       *serial;
        struct usb_serial_port  *port;
 };
 
+static int keyspan_pda_write_start(struct usb_serial_port *port);
 
 #define KEYSPAN_VENDOR_ID              0x06cd
 #define KEYSPAN_PDA_FAKE_ID            0x0103
@@ -62,18 +51,13 @@ struct keyspan_pda_private {
 #define ENTREGA_FAKE_ID                        0x8093
 
 static const struct usb_device_id id_table_combined[] = {
-#ifdef KEYSPAN
        { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
-#endif
-#ifdef XIRCOM
        { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
        { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
        { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
-#endif
        { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
        { }                                             /* Terminating entry */
 };
-
 MODULE_DEVICE_TABLE(usb, id_table_combined);
 
 static const struct usb_device_id id_table_std[] = {
@@ -81,46 +65,71 @@ static const struct usb_device_id id_table_std[] = {
        { }                                             /* Terminating entry */
 };
 
-#ifdef KEYSPAN
 static const struct usb_device_id id_table_fake[] = {
        { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
-       { }                                             /* Terminating entry */
-};
-#endif
-
-#ifdef XIRCOM
-static const struct usb_device_id id_table_fake_xircom[] = {
        { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
        { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
        { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
-       { }
+       { }                                             /* Terminating entry */
 };
-#endif
 
-static void keyspan_pda_wakeup_write(struct work_struct *work)
+static int keyspan_pda_get_write_room(struct keyspan_pda_private *priv)
 {
-       struct keyspan_pda_private *priv =
-               container_of(work, struct keyspan_pda_private, wakeup_work);
        struct usb_serial_port *port = priv->port;
+       struct usb_serial *serial = port->serial;
+       u8 *room;
+       int rc;
+
+       room = kmalloc(1, GFP_KERNEL);
+       if (!room)
+               return -ENOMEM;
 
-       tty_port_tty_wakeup(&port->port);
+       rc = usb_control_msg(serial->dev,
+                            usb_rcvctrlpipe(serial->dev, 0),
+                            6, /* write_room */
+                            USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+                            | USB_DIR_IN,
+                            0, /* value: 0 means "remaining room" */
+                            0, /* index */
+                            room,
+                            1,
+                            2000);
+       if (rc != 1) {
+               if (rc >= 0)
+                       rc = -EIO;
+               dev_dbg(&port->dev, "roomquery failed: %d\n", rc);
+               goto out_free;
+       }
+
+       dev_dbg(&port->dev, "roomquery says %d\n", *room);
+       rc = *room;
+out_free:
+       kfree(room);
+
+       return rc;
 }
 
 static void keyspan_pda_request_unthrottle(struct work_struct *work)
 {
        struct keyspan_pda_private *priv =
                container_of(work, struct keyspan_pda_private, unthrottle_work);
-       struct usb_serial *serial = priv->serial;
+       struct usb_serial_port *port = priv->port;
+       struct usb_serial *serial = port->serial;
+       unsigned long flags;
        int result;
 
-       /* ask the device to tell us when the tx buffer becomes
-          sufficiently empty */
+       dev_dbg(&port->dev, "%s\n", __func__);
+
+       /*
+        * Ask the device to tell us when the tx buffer becomes
+        * sufficiently empty.
+        */
        result = usb_control_msg(serial->dev,
                                 usb_sndctrlpipe(serial->dev, 0),
                                 7, /* request_unthrottle */
                                 USB_TYPE_VENDOR | USB_RECIP_INTERFACE
                                 | USB_DIR_OUT,
-                                16, /* value: threshold */
+                                KEYSPAN_TX_THRESHOLD,
                                 0, /* index */
                                 NULL,
                                 0,
@@ -128,9 +137,20 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work)
        if (result < 0)
                dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n",
                        __func__, result);
+       /*
+        * Need to check available space after requesting notification in case
+        * buffer is already empty so that no notification is sent.
+        */
+       result = keyspan_pda_get_write_room(priv);
+       if (result > KEYSPAN_TX_THRESHOLD) {
+               spin_lock_irqsave(&port->lock, flags);
+               priv->tx_room = max(priv->tx_room, result);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               usb_serial_port_softint(port);
+       }
 }
 
-
 static void keyspan_pda_rx_interrupt(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
@@ -139,6 +159,8 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
        int retval;
        int status = urb->status;
        struct keyspan_pda_private *priv;
+       unsigned long flags;
+
        priv = usb_get_serial_port_data(port);
 
        switch (status) {
@@ -172,18 +194,22 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
                break;
        case 1:
                /* status interrupt */
-               if (len < 3) {
+               if (len < 2) {
                        dev_warn(&port->dev, "short interrupt message received\n");
                        break;
                }
-               dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]);
+               dev_dbg(&port->dev, "rx int, d1=%d\n", data[1]);
                switch (data[1]) {
                case 1: /* modemline change */
                        break;
                case 2: /* tx unthrottle interrupt */
-                       priv->tx_throttled = 0;
-                       /* queue up a wakeup at scheduler time */
-                       schedule_work(&priv->wakeup_work);
+                       spin_lock_irqsave(&port->lock, flags);
+                       priv->tx_room = max(priv->tx_room, KEYSPAN_TX_THRESHOLD);
+                       spin_unlock_irqrestore(&port->lock, flags);
+
+                       keyspan_pda_write_start(port);
+
+                       usb_serial_port_softint(port);
                        break;
                default:
                        break;
@@ -201,31 +227,30 @@ exit:
                        __func__, retval);
 }
 
-
 static void keyspan_pda_rx_throttle(struct tty_struct *tty)
 {
-       /* stop receiving characters. We just turn off the URB request, and
-          let chars pile up in the device. If we're doing hardware
-          flowcontrol, the device will signal the other end when its buffer
-          fills up. If we're doing XON/XOFF, this would be a good time to
-          send an XOFF, although it might make sense to foist that off
-          upon the device too. */
        struct usb_serial_port *port = tty->driver_data;
 
+       /*
+        * Stop receiving characters. We just turn off the URB request, and
+        * let chars pile up in the device. If we're doing hardware
+        * flowcontrol, the device will signal the other end when its buffer
+        * fills up. If we're doing XON/XOFF, this would be a good time to
+        * send an XOFF, although it might make sense to foist that off upon
+        * the device too.
+        */
        usb_kill_urb(port->interrupt_in_urb);
 }
 
-
 static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
-       /* just restart the receive interrupt URB */
 
+       /* just restart the receive interrupt URB */
        if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
                dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
 }
 
-
 static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
 {
        int rc;
@@ -267,8 +292,6 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
                baud = 9600;
        }
 
-       /* rather than figure out how to sleep while waiting for this
-          to complete, I just use the "legacy" API. */
        rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                             0, /* set baud */
                             USB_TYPE_VENDOR
@@ -281,10 +304,10 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
                             2000); /* timeout */
        if (rc < 0)
                return 0;
+
        return baud;
 }
 
-
 static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -296,6 +319,7 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
                value = 1; /* start break */
        else
                value = 0; /* clear break */
+
        result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                        4, /* set break */
                        USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
@@ -303,39 +327,35 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
        if (result < 0)
                dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n",
                        __func__, result);
-       /* there is something funky about this.. the TCSBRK that 'cu' performs
-          ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
-          seconds apart, but it feels like the break sent isn't as long as it
-          is on /dev/ttyS0 */
 }
 
-
 static void keyspan_pda_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {
        struct usb_serial *serial = port->serial;
        speed_t speed;
 
-       /* cflag specifies lots of stuff: number of stop bits, parity, number
-          of data bits, baud. What can the device actually handle?:
-          CSTOPB (1 stop bit or 2)
-          PARENB (parity)
-          CSIZE (5bit .. 8bit)
-          There is minimal hw support for parity (a PSW bit seems to hold the
-          parity of whatever is in the accumulator). The UART either deals
-          with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
-          1 special, stop). So, with firmware changes, we could do:
-          8N1: 10 bit
-          8N2: 11 bit, extra bit always (mark?)
-          8[EOMS]1: 11 bit, extra bit is parity
-          7[EOMS]1: 10 bit, b0/b7 is parity
-          7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
-
-          HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS
-          bit.
-
-          For now, just do baud. */
-
+       /*
+        * cflag specifies lots of stuff: number of stop bits, parity, number
+        * of data bits, baud. What can the device actually handle?:
+        * CSTOPB (1 stop bit or 2)
+        * PARENB (parity)
+        * CSIZE (5bit .. 8bit)
+        * There is minimal hw support for parity (a PSW bit seems to hold the
+        * parity of whatever is in the accumulator). The UART either deals
+        * with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
+        * 1 special, stop). So, with firmware changes, we could do:
+        * 8N1: 10 bit
+        * 8N2: 11 bit, extra bit always (mark?)
+        * 8[EOMS]1: 11 bit, extra bit is parity
+        * 7[EOMS]1: 10 bit, b0/b7 is parity
+        * 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
+        *
+        * HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS
+        * bit.
+        *
+        * For now, just do baud.
+        */
        speed = tty_get_baud_rate(tty);
        speed = keyspan_pda_setbaud(serial, speed);
 
@@ -344,17 +364,19 @@ static void keyspan_pda_set_termios(struct tty_struct *tty,
                /* It hasn't changed so.. */
                speed = tty_termios_baud_rate(old_termios);
        }
-       /* Only speed can change so copy the old h/w parameters
-          then encode the new speed */
+       /*
+        * Only speed can change so copy the old h/w parameters then encode
+        * the new speed.
+        */
        tty_termios_copy_hw(&tty->termios, old_termios);
        tty_encode_baud_rate(tty, speed, speed);
 }
 
-
-/* modem control pins: DTR and RTS are outputs and can be controlled.
  DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
-   read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
-
+/*
+ * Modem control pins: DTR and RTS are outputs and can be controlled.
* DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
+ * read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused.
+ */
 static int keyspan_pda_get_modem_info(struct usb_serial *serial,
                                      unsigned char *value)
 {
@@ -378,7 +400,6 @@ static int keyspan_pda_get_modem_info(struct usb_serial *serial,
        return rc;
 }
 
-
 static int keyspan_pda_set_modem_info(struct usb_serial *serial,
                                      unsigned char value)
 {
@@ -401,13 +422,14 @@ static int keyspan_pda_tiocmget(struct tty_struct *tty)
        rc = keyspan_pda_get_modem_info(serial, &status);
        if (rc < 0)
                return rc;
-       value =
-               ((status & (1<<7)) ? TIOCM_DTR : 0) |
-               ((status & (1<<6)) ? TIOCM_CAR : 0) |
-               ((status & (1<<5)) ? TIOCM_RNG : 0) |
-               ((status & (1<<4)) ? TIOCM_DSR : 0) |
-               ((status & (1<<3)) ? TIOCM_CTS : 0) |
-               ((status & (1<<2)) ? TIOCM_RTS : 0);
+
+       value = ((status & BIT(7)) ? TIOCM_DTR : 0) |
+               ((status & BIT(6)) ? TIOCM_CAR : 0) |
+               ((status & BIT(5)) ? TIOCM_RNG : 0) |
+               ((status & BIT(4)) ? TIOCM_DSR : 0) |
+               ((status & BIT(3)) ? TIOCM_CTS : 0) |
+               ((status & BIT(2)) ? TIOCM_RTS : 0);
+
        return value;
 }
 
@@ -424,186 +446,127 @@ static int keyspan_pda_tiocmset(struct tty_struct *tty,
                return rc;
 
        if (set & TIOCM_RTS)
-               status |= (1<<2);
+               status |= BIT(2);
        if (set & TIOCM_DTR)
-               status |= (1<<7);
+               status |= BIT(7);
 
        if (clear & TIOCM_RTS)
-               status &= ~(1<<2);
+               status &= ~BIT(2);
        if (clear & TIOCM_DTR)
-               status &= ~(1<<7);
+               status &= ~BIT(7);
        rc = keyspan_pda_set_modem_info(serial, status);
        return rc;
 }
 
-static int keyspan_pda_write(struct tty_struct *tty,
-       struct usb_serial_port *port, const unsigned char *buf, int count)
+static int keyspan_pda_write_start(struct usb_serial_port *port)
 {
-       struct usb_serial *serial = port->serial;
-       int request_unthrottle = 0;
-       int rc = 0;
-       struct keyspan_pda_private *priv;
+       struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       struct urb *urb;
+       int count;
+       int room;
+       int rc;
 
-       priv = usb_get_serial_port_data(port);
-       /* guess how much room is left in the device's ring buffer, and if we
-          want to send more than that, check first, updating our notion of
-          what is left. If our write will result in no room left, ask the
-          device to give us an interrupt when the room available rises above
-          a threshold, and hold off all writers (eventually, those using
-          select() or poll() too) until we receive that unthrottle interrupt.
-          Block if we can't write anything at all, otherwise write as much as
-          we can. */
-       if (count == 0) {
-               dev_dbg(&port->dev, "write request of 0 bytes\n");
-               return 0;
-       }
+       /*
+        * Guess how much room is left in the device's ring buffer. If our
+        * write will result in no room left, ask the device to give us an
+        * interrupt when the room available rises above a threshold but also
+        * query how much room is currently available (in case our guess was
+        * too conservative and the buffer is already empty when the
+        * unthrottle work is scheduled).
+        */
+
+       /*
+        * We might block because of:
+        * the TX urb is in-flight (wait until it completes)
+        * the device is full (wait until it says there is room)
+        */
+       spin_lock_irqsave(&port->lock, flags);
+
+       room = priv->tx_room;
+       count = kfifo_len(&port->write_fifo);
 
-       /* we might block because of:
-          the TX urb is in-flight (wait until it completes)
-          the device is full (wait until it says there is room)
-       */
-       spin_lock_bh(&port->lock);
-       if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled) {
-               spin_unlock_bh(&port->lock);
+       if (!test_bit(0, &port->write_urbs_free) || count == 0 || room == 0) {
+               spin_unlock_irqrestore(&port->lock, flags);
                return 0;
        }
-       clear_bit(0, &port->write_urbs_free);
-       spin_unlock_bh(&port->lock);
-
-       /* At this point the URB is in our control, nobody else can submit it
-          again (the only sudden transition was the one from EINPROGRESS to
-          finished).  Also, the tx process is not throttled. So we are
-          ready to write. */
-
-       count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
-       /* Check if we might overrun the Tx buffer.   If so, ask the
-          device how much room it really has.  This is done only on
-          scheduler time, since usb_control_msg() sleeps. */
-       if (count > priv->tx_room && !in_interrupt()) {
-               u8 *room;
-
-               room = kmalloc(1, GFP_KERNEL);
-               if (!room) {
-                       rc = -ENOMEM;
-                       goto exit;
-               }
+       __clear_bit(0, &port->write_urbs_free);
 
-               rc = usb_control_msg(serial->dev,
-                                    usb_rcvctrlpipe(serial->dev, 0),
-                                    6, /* write_room */
-                                    USB_TYPE_VENDOR | USB_RECIP_INTERFACE
-                                    | USB_DIR_IN,
-                                    0, /* value: 0 means "remaining room" */
-                                    0, /* index */
-                                    room,
-                                    1,
-                                    2000);
-               if (rc > 0) {
-                       dev_dbg(&port->dev, "roomquery says %d\n", *room);
-                       priv->tx_room = *room;
-               }
-               kfree(room);
-               if (rc < 0) {
-                       dev_dbg(&port->dev, "roomquery failed\n");
-                       goto exit;
-               }
-               if (rc == 0) {
-                       dev_dbg(&port->dev, "roomquery returned 0 bytes\n");
-                       rc = -EIO; /* device didn't return any data */
-                       goto exit;
-               }
-       }
-       if (count > priv->tx_room) {
-               /* we're about to completely fill the Tx buffer, so
-                  we'll be throttled afterwards. */
-               count = priv->tx_room;
-               request_unthrottle = 1;
-       }
+       if (count > room)
+               count = room;
+       if (count > port->bulk_out_size)
+               count = port->bulk_out_size;
 
-       if (count) {
-               /* now transfer data */
-               memcpy(port->write_urb->transfer_buffer, buf, count);
-               /* send the data out the bulk port */
-               port->write_urb->transfer_buffer_length = count;
+       urb = port->write_urb;
+       count = kfifo_out(&port->write_fifo, urb->transfer_buffer, count);
+       urb->transfer_buffer_length = count;
 
-               priv->tx_room -= count;
+       port->tx_bytes += count;
+       priv->tx_room -= count;
 
-               rc = usb_submit_urb(port->write_urb, GFP_ATOMIC);
-               if (rc) {
-                       dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n");
-                       goto exit;
-               }
-       } else {
-               /* There wasn't any room left, so we are throttled until
-                  the buffer empties a bit */
-               request_unthrottle = 1;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       dev_dbg(&port->dev, "%s - count = %d, txroom = %d\n", __func__, count, room);
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+       if (rc) {
+               dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n");
+
+               spin_lock_irqsave(&port->lock, flags);
+               port->tx_bytes -= count;
+               priv->tx_room = max(priv->tx_room, room + count);
+               __set_bit(0, &port->write_urbs_free);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               return rc;
        }
 
-       if (request_unthrottle) {
-               priv->tx_throttled = 1; /* block writers */
+       if (count == room)
                schedule_work(&priv->unthrottle_work);
-       }
 
-       rc = count;
-exit:
-       if (rc < 0)
-               set_bit(0, &port->write_urbs_free);
-       return rc;
+       return count;
 }
 
-
 static void keyspan_pda_write_bulk_callback(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
-       struct keyspan_pda_private *priv;
+       unsigned long flags;
 
-       set_bit(0, &port->write_urbs_free);
-       priv = usb_get_serial_port_data(port);
+       spin_lock_irqsave(&port->lock, flags);
+       port->tx_bytes -= urb->transfer_buffer_length;
+       __set_bit(0, &port->write_urbs_free);
+       spin_unlock_irqrestore(&port->lock, flags);
 
-       /* queue up a wakeup at scheduler time */
-       schedule_work(&priv->wakeup_work);
-}
+       keyspan_pda_write_start(port);
 
+       usb_serial_port_softint(port);
+}
 
-static int keyspan_pda_write_room(struct tty_struct *tty)
+static int keyspan_pda_write(struct tty_struct *tty, struct usb_serial_port *port,
+               const unsigned char *buf, int count)
 {
-       struct usb_serial_port *port = tty->driver_data;
-       struct keyspan_pda_private *priv;
-       priv = usb_get_serial_port_data(port);
-       /* used by n_tty.c for processing of tabs and such. Giving it our
-          conservative guess is probably good enough, but needs testing by
-          running a console through the device. */
-       return priv->tx_room;
-}
+       int rc;
 
+       dev_dbg(&port->dev, "%s - count = %d\n", __func__, count);
 
-static int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       struct keyspan_pda_private *priv;
-       unsigned long flags;
-       int ret = 0;
+       if (!count)
+               return 0;
 
-       priv = usb_get_serial_port_data(port);
+       count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
 
-       /* when throttled, return at least WAKEUP_CHARS to tell select() (via
-          n_tty.c:normal_poll() ) that we're not writeable. */
+       rc = keyspan_pda_write_start(port);
+       if (rc)
+               return rc;
 
-       spin_lock_irqsave(&port->lock, flags);
-       if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled)
-               ret = 256;
-       spin_unlock_irqrestore(&port->lock, flags);
-       return ret;
+       return count;
 }
 
-
 static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
 {
        struct usb_serial *serial = port->serial;
 
        if (on)
-               keyspan_pda_set_modem_info(serial, (1 << 7) | (1 << 2));
+               keyspan_pda_set_modem_info(serial, BIT(7) | BIT(2));
        else
                keyspan_pda_set_modem_info(serial, 0);
 }
@@ -612,74 +575,63 @@ static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
 static int keyspan_pda_open(struct tty_struct *tty,
                                        struct usb_serial_port *port)
 {
-       struct usb_serial *serial = port->serial;
-       u8 *room;
-       int rc = 0;
-       struct keyspan_pda_private *priv;
+       struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+       int rc;
 
        /* find out how much room is in the Tx ring */
-       room = kmalloc(1, GFP_KERNEL);
-       if (!room)
-               return -ENOMEM;
+       rc = keyspan_pda_get_write_room(priv);
+       if (rc < 0)
+               return rc;
 
-       rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
-                            6, /* write_room */
-                            USB_TYPE_VENDOR | USB_RECIP_INTERFACE
-                            | USB_DIR_IN,
-                            0, /* value */
-                            0, /* index */
-                            room,
-                            1,
-                            2000);
-       if (rc < 0) {
-               dev_dbg(&port->dev, "%s - roomquery failed\n", __func__);
-               goto error;
-       }
-       if (rc == 0) {
-               dev_dbg(&port->dev, "%s - roomquery returned 0 bytes\n", __func__);
-               rc = -EIO;
-               goto error;
-       }
-       priv = usb_get_serial_port_data(port);
-       priv->tx_room = *room;
-       priv->tx_throttled = *room ? 0 : 1;
+       spin_lock_irq(&port->lock);
+       priv->tx_room = rc;
+       spin_unlock_irq(&port->lock);
 
-       /*Start reading from the device*/
        rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
        if (rc) {
                dev_dbg(&port->dev, "%s - usb_submit_urb(read int) failed\n", __func__);
-               goto error;
+               return rc;
        }
-error:
-       kfree(room);
-       return rc;
+
+       return 0;
 }
+
 static void keyspan_pda_close(struct usb_serial_port *port)
 {
-       usb_kill_urb(port->write_urb);
+       struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+
+       /*
+        * Stop the interrupt URB first as its completion handler may submit
+        * the write URB.
+        */
        usb_kill_urb(port->interrupt_in_urb);
-}
+       usb_kill_urb(port->write_urb);
+
+       cancel_work_sync(&priv->unthrottle_work);
 
+       spin_lock_irq(&port->lock);
+       kfifo_reset(&port->write_fifo);
+       spin_unlock_irq(&port->lock);
+}
 
 /* download the firmware to a "fake" device (pre-renumeration) */
 static int keyspan_pda_fake_startup(struct usb_serial *serial)
 {
+       unsigned int vid = le16_to_cpu(serial->dev->descriptor.idVendor);
        const char *fw_name;
 
        /* download the firmware here ... */
        ezusb_fx1_set_reset(serial->dev, 1);
 
-       if (0) { ; }
-#ifdef KEYSPAN
-       else if (le16_to_cpu(serial->dev->descriptor.idVendor) == KEYSPAN_VENDOR_ID)
+       switch (vid) {
+       case KEYSPAN_VENDOR_ID:
                fw_name = "keyspan_pda/keyspan_pda.fw";
-#endif
-#ifdef XIRCOM
-       else if ((le16_to_cpu(serial->dev->descriptor.idVendor) == XIRCOM_VENDOR_ID) ||
-                (le16_to_cpu(serial->dev->descriptor.idVendor) == ENTREGA_VENDOR_ID))
+               break;
+       case XIRCOM_VENDOR_ID:
+       case ENTREGA_VENDOR_ID:
                fw_name = "keyspan_pda/xircom_pgs.fw";
-#endif
-       else {
+               break;
+       default:
                dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n",
                        __func__);
                return -ENODEV;
@@ -691,19 +643,17 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial)
                return -ENOENT;
        }
 
-       /* after downloading firmware Renumeration will occur in a
-         moment and the new device will bind to the real driver */
+       /*
+        * After downloading firmware renumeration will occur in a moment and
+        * the new device will bind to the real driver.
+        */
 
-       /* we want this device to fail to have a driver assigned to it. */
+       /* We want this device to fail to have a driver assigned to it. */
        return 1;
 }
 
-#ifdef KEYSPAN
 MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
-#endif
-#ifdef XIRCOM
 MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
-#endif
 
 static int keyspan_pda_port_probe(struct usb_serial_port *port)
 {
@@ -714,9 +664,7 @@ static int keyspan_pda_port_probe(struct usb_serial_port *port)
        if (!priv)
                return -ENOMEM;
 
-       INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write);
        INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle);
-       priv->serial = port->serial;
        priv->port = port;
 
        usb_set_serial_port_data(port, priv);
@@ -734,7 +682,6 @@ static int keyspan_pda_port_remove(struct usb_serial_port *port)
        return 0;
 }
 
-#ifdef KEYSPAN
 static struct usb_serial_driver keyspan_pda_fake_device = {
        .driver = {
                .owner =        THIS_MODULE,
@@ -745,20 +692,6 @@ static struct usb_serial_driver keyspan_pda_fake_device = {
        .num_ports =            1,
        .attach =               keyspan_pda_fake_startup,
 };
-#endif
-
-#ifdef XIRCOM
-static struct usb_serial_driver xircom_pgs_fake_device = {
-       .driver = {
-               .owner =        THIS_MODULE,
-               .name =         "xircom_no_firm",
-       },
-       .description =          "Xircom / Entrega PGS - (prerenumeration)",
-       .id_table =             id_table_fake_xircom,
-       .num_ports =            1,
-       .attach =               keyspan_pda_fake_startup,
-};
-#endif
 
 static struct usb_serial_driver keyspan_pda_device = {
        .driver = {
@@ -774,10 +707,8 @@ static struct usb_serial_driver keyspan_pda_device = {
        .open =                 keyspan_pda_open,
        .close =                keyspan_pda_close,
        .write =                keyspan_pda_write,
-       .write_room =           keyspan_pda_write_room,
-       .write_bulk_callback =  keyspan_pda_write_bulk_callback,
+       .write_bulk_callback =  keyspan_pda_write_bulk_callback,
        .read_int_callback =    keyspan_pda_rx_interrupt,
-       .chars_in_buffer =      keyspan_pda_chars_in_buffer,
        .throttle =             keyspan_pda_rx_throttle,
        .unthrottle =           keyspan_pda_rx_unthrottle,
        .set_termios =          keyspan_pda_set_termios,
@@ -790,12 +721,7 @@ static struct usb_serial_driver keyspan_pda_device = {
 
 static struct usb_serial_driver * const serial_drivers[] = {
        &keyspan_pda_device,
-#ifdef KEYSPAN
        &keyspan_pda_fake_device,
-#endif
-#ifdef XIRCOM
-       &xircom_pgs_fake_device,
-#endif
        NULL
 };
 
index 5eed107..41ee298 100644 (file)
@@ -79,14 +79,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
 #define DCR_INIT_VAL       0x0c        /* SLCTIN, nINIT */
 #define ECR_INIT_VAL       0x00        /* SPP mode */
 
-struct urbtracker {
-       struct mos7715_parport  *mos_parport;
-       struct list_head        urblist_entry;
-       struct kref             ref_count;
-       struct urb              *urb;
-       struct usb_ctrlrequest  *setup;
-};
-
 enum mos7715_pp_modes {
        SPP = 0<<5,
        PS2 = 1<<5,      /* moschip calls this 'NIBBLE' mode */
@@ -96,12 +88,9 @@ enum mos7715_pp_modes {
 struct mos7715_parport {
        struct parport          *pp;           /* back to containing struct */
        struct kref             ref_count;     /* to instance of this struct */
-       struct list_head        deferred_urbs; /* list deferred async urbs */
-       struct list_head        active_urbs;   /* list async urbs in flight */
-       spinlock_t              listlock;      /* protects list access */
        bool                    msg_pending;   /* usb sync call pending */
        struct completion       syncmsg_compl; /* usb sync call completed */
-       struct tasklet_struct   urb_tasklet;   /* for sending deferred urbs */
+       struct work_struct      work;          /* restore deferred writes */
        struct usb_serial       *serial;       /* back to containing struct */
        __u8                    shadowECR;     /* parallel port regs... */
        __u8                    shadowDCR;
@@ -265,174 +254,8 @@ static void destroy_mos_parport(struct kref *kref)
        kfree(mos_parport);
 }
 
-static void destroy_urbtracker(struct kref *kref)
-{
-       struct urbtracker *urbtrack =
-               container_of(kref, struct urbtracker, ref_count);
-       struct mos7715_parport *mos_parport = urbtrack->mos_parport;
-
-       usb_free_urb(urbtrack->urb);
-       kfree(urbtrack->setup);
-       kfree(urbtrack);
-       kref_put(&mos_parport->ref_count, destroy_mos_parport);
-}
-
 /*
- * This runs as a tasklet when sending an urb in a non-blocking parallel
- * port callback had to be deferred because the disconnect mutex could not be
- * obtained at the time.
- */
-static void send_deferred_urbs(struct tasklet_struct *t)
-{
-       int ret_val;
-       unsigned long flags;
-       struct mos7715_parport *mos_parport = from_tasklet(mos_parport, t,
-                                                          urb_tasklet);
-       struct urbtracker *urbtrack, *tmp;
-       struct list_head *cursor, *next;
-       struct device *dev;
-
-       /* if release function ran, game over */
-       if (unlikely(mos_parport->serial == NULL))
-               return;
-
-       dev = &mos_parport->serial->dev->dev;
-
-       /* try again to get the mutex */
-       if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
-               dev_dbg(dev, "%s: rescheduling tasklet\n", __func__);
-               tasklet_schedule(&mos_parport->urb_tasklet);
-               return;
-       }
-
-       /* if device disconnected, game over */
-       if (unlikely(mos_parport->serial->disconnected)) {
-               mutex_unlock(&mos_parport->serial->disc_mutex);
-               return;
-       }
-
-       spin_lock_irqsave(&mos_parport->listlock, flags);
-       if (list_empty(&mos_parport->deferred_urbs)) {
-               spin_unlock_irqrestore(&mos_parport->listlock, flags);
-               mutex_unlock(&mos_parport->serial->disc_mutex);
-               dev_dbg(dev, "%s: deferred_urbs list empty\n", __func__);
-               return;
-       }
-
-       /* move contents of deferred_urbs list to active_urbs list and submit */
-       list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
-               list_move_tail(cursor, &mos_parport->active_urbs);
-       list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs,
-                           urblist_entry) {
-               ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
-               dev_dbg(dev, "%s: urb submitted\n", __func__);
-               if (ret_val) {
-                       dev_err(dev, "usb_submit_urb() failed: %d\n", ret_val);
-                       list_del(&urbtrack->urblist_entry);
-                       kref_put(&urbtrack->ref_count, destroy_urbtracker);
-               }
-       }
-       spin_unlock_irqrestore(&mos_parport->listlock, flags);
-       mutex_unlock(&mos_parport->serial->disc_mutex);
-}
-
-/* callback for parallel port control urbs submitted asynchronously */
-static void async_complete(struct urb *urb)
-{
-       struct urbtracker *urbtrack = urb->context;
-       int status = urb->status;
-       unsigned long flags;
-
-       if (unlikely(status))
-               dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
-
-       /* remove the urbtracker from the active_urbs list */
-       spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags);
-       list_del(&urbtrack->urblist_entry);
-       spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags);
-       kref_put(&urbtrack->ref_count, destroy_urbtracker);
-}
-
-static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
-                                     enum mos_regs reg, __u8 data)
-{
-       struct urbtracker *urbtrack;
-       int ret_val;
-       unsigned long flags;
-       struct usb_serial *serial = mos_parport->serial;
-       struct usb_device *usbdev = serial->dev;
-
-       /* create and initialize the control urb and containing urbtracker */
-       urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC);
-       if (!urbtrack)
-               return -ENOMEM;
-
-       urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!urbtrack->urb) {
-               kfree(urbtrack);
-               return -ENOMEM;
-       }
-       urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC);
-       if (!urbtrack->setup) {
-               usb_free_urb(urbtrack->urb);
-               kfree(urbtrack);
-               return -ENOMEM;
-       }
-       urbtrack->setup->bRequestType = (__u8)0x40;
-       urbtrack->setup->bRequest = (__u8)0x0e;
-       urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy));
-       urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg));
-       urbtrack->setup->wLength = 0;
-       usb_fill_control_urb(urbtrack->urb, usbdev,
-                            usb_sndctrlpipe(usbdev, 0),
-                            (unsigned char *)urbtrack->setup,
-                            NULL, 0, async_complete, urbtrack);
-       kref_get(&mos_parport->ref_count);
-       urbtrack->mos_parport = mos_parport;
-       kref_init(&urbtrack->ref_count);
-       INIT_LIST_HEAD(&urbtrack->urblist_entry);
-
-       /*
-        * get the disconnect mutex, or add tracker to the deferred_urbs list
-        * and schedule a tasklet to try again later
-        */
-       if (!mutex_trylock(&serial->disc_mutex)) {
-               spin_lock_irqsave(&mos_parport->listlock, flags);
-               list_add_tail(&urbtrack->urblist_entry,
-                             &mos_parport->deferred_urbs);
-               spin_unlock_irqrestore(&mos_parport->listlock, flags);
-               tasklet_schedule(&mos_parport->urb_tasklet);
-               dev_dbg(&usbdev->dev, "tasklet scheduled\n");
-               return 0;
-       }
-
-       /* bail if device disconnected */
-       if (serial->disconnected) {
-               kref_put(&urbtrack->ref_count, destroy_urbtracker);
-               mutex_unlock(&serial->disc_mutex);
-               return -ENODEV;
-       }
-
-       /* add the tracker to the active_urbs list and submit */
-       spin_lock_irqsave(&mos_parport->listlock, flags);
-       list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs);
-       spin_unlock_irqrestore(&mos_parport->listlock, flags);
-       ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
-       mutex_unlock(&serial->disc_mutex);
-       if (ret_val) {
-               dev_err(&usbdev->dev,
-                       "%s: submit_urb() failed: %d\n", __func__, ret_val);
-               spin_lock_irqsave(&mos_parport->listlock, flags);
-               list_del(&urbtrack->urblist_entry);
-               spin_unlock_irqrestore(&mos_parport->listlock, flags);
-               kref_put(&urbtrack->ref_count, destroy_urbtracker);
-               return ret_val;
-       }
-       return 0;
-}
-
-/*
- * This is the the common top part of all parallel port callback operations that
+ * This is the common top part of all parallel port callback operations that
  * send synchronous messages to the device.  This implements convoluted locking
  * that avoids two scenarios: (1) a port operation is called after usbserial
  * has called our release function, at which point struct mos7715_parport has
@@ -458,6 +281,10 @@ static int parport_prologue(struct parport *pp)
        reinit_completion(&mos_parport->syncmsg_compl);
        spin_unlock(&release_lock);
 
+       /* ensure writes from restore are submitted before new requests */
+       if (work_pending(&mos_parport->work))
+               flush_work(&mos_parport->work);
+
        mutex_lock(&mos_parport->serial->disc_mutex);
        if (mos_parport->serial->disconnected) {
                /* device disconnected */
@@ -482,6 +309,26 @@ static inline void parport_epilogue(struct parport *pp)
        complete(&mos_parport->syncmsg_compl);
 }
 
+static void deferred_restore_writes(struct work_struct *work)
+{
+       struct mos7715_parport *mos_parport;
+
+       mos_parport = container_of(work, struct mos7715_parport, work);
+
+       mutex_lock(&mos_parport->serial->disc_mutex);
+
+       /* if device disconnected, game over */
+       if (mos_parport->serial->disconnected)
+               goto done;
+
+       write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
+                     mos_parport->shadowDCR);
+       write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
+                     mos_parport->shadowECR);
+done:
+       mutex_unlock(&mos_parport->serial->disc_mutex);
+}
+
 static void parport_mos7715_write_data(struct parport *pp, unsigned char d)
 {
        struct mos7715_parport *mos_parport = pp->private_data;
@@ -639,10 +486,10 @@ static void parport_mos7715_restore_state(struct parport *pp,
                spin_unlock(&release_lock);
                return;
        }
-       write_parport_reg_nonblock(mos_parport, MOS7720_DCR,
-                                  mos_parport->shadowDCR);
-       write_parport_reg_nonblock(mos_parport, MOS7720_ECR,
-                                  mos_parport->shadowECR);
+       mos_parport->shadowDCR = s->u.pc.ctr;
+       mos_parport->shadowECR = s->u.pc.ecr;
+
+       schedule_work(&mos_parport->work);
        spin_unlock(&release_lock);
 }
 
@@ -712,12 +559,9 @@ static int mos7715_parport_init(struct usb_serial *serial)
 
        mos_parport->msg_pending = false;
        kref_init(&mos_parport->ref_count);
-       spin_lock_init(&mos_parport->listlock);
-       INIT_LIST_HEAD(&mos_parport->active_urbs);
-       INIT_LIST_HEAD(&mos_parport->deferred_urbs);
        usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
        mos_parport->serial = serial;
-       tasklet_setup(&mos_parport->urb_tasklet, send_deferred_urbs);
+       INIT_WORK(&mos_parport->work, deferred_restore_writes);
        init_completion(&mos_parport->syncmsg_compl);
 
        /* cycle parallel port reset bit */
@@ -1867,8 +1711,6 @@ static void mos7720_release(struct usb_serial *serial)
 
        if (le16_to_cpu(serial->dev->descriptor.idProduct)
            == MOSCHIP_DEVICE_ID_7715) {
-               struct urbtracker *urbtrack;
-               unsigned long flags;
                struct mos7715_parport *mos_parport =
                        usb_get_serial_data(serial);
 
@@ -1881,21 +1723,17 @@ static void mos7720_release(struct usb_serial *serial)
                if (mos_parport->msg_pending)
                        wait_for_completion_timeout(&mos_parport->syncmsg_compl,
                                            msecs_to_jiffies(MOS_WDR_TIMEOUT));
+               /*
+                * If delayed work is currently scheduled, wait for it to
+                * complete. This also implies barriers that ensure the
+                * below serial clearing is not hoisted above the ->work.
+                */
+               cancel_work_sync(&mos_parport->work);
 
                parport_remove_port(mos_parport->pp);
                usb_set_serial_data(serial, NULL);
                mos_parport->serial = NULL;
 
-               /* if tasklet currently scheduled, wait for it to complete */
-               tasklet_kill(&mos_parport->urb_tasklet);
-
-               /* unlink any urbs sent by the tasklet  */
-               spin_lock_irqsave(&mos_parport->listlock, flags);
-               list_for_each_entry(urbtrack,
-                                   &mos_parport->active_urbs,
-                                   urblist_entry)
-                       usb_unlink_urb(urbtrack->urb);
-               spin_unlock_irqrestore(&mos_parport->listlock, flags);
                parport_del_port(mos_parport->pp);
 
                kref_put(&mos_parport->ref_count, destroy_mos_parport);
index 56d6f6d..2c21e34 100644 (file)
@@ -563,6 +563,9 @@ static void option_instat_callback(struct urb *urb);
 
 /* Device flags */
 
+/* Highest interface number which can be used with NCTRL() and RSVD() */
+#define FLAG_IFNUM_MAX 7
+
 /* Interface does not support modem-control requests */
 #define NCTRL(ifnum)   ((BIT(ifnum) & 0xff) << 8)
 
@@ -2101,6 +2104,14 @@ static struct usb_serial_driver * const serial_drivers[] = {
 
 module_usb_serial_driver(serial_drivers, option_ids);
 
+static bool iface_is_reserved(unsigned long device_flags, u8 ifnum)
+{
+       if (ifnum > FLAG_IFNUM_MAX)
+               return false;
+
+       return device_flags & RSVD(ifnum);
+}
+
 static int option_probe(struct usb_serial *serial,
                        const struct usb_device_id *id)
 {
@@ -2117,7 +2128,7 @@ static int option_probe(struct usb_serial *serial,
         * the same class/subclass/protocol as the serial interfaces.  Look at
         * the Windows driver .INF files for reserved interface numbers.
         */
-       if (device_flags & RSVD(iface_desc->bInterfaceNumber))
+       if (iface_is_reserved(device_flags, iface_desc->bInterfaceNumber))
                return -ENODEV;
 
        /*
@@ -2133,6 +2144,14 @@ static int option_probe(struct usb_serial *serial,
        return 0;
 }
 
+static bool iface_no_modem_control(unsigned long device_flags, u8 ifnum)
+{
+       if (ifnum > FLAG_IFNUM_MAX)
+               return false;
+
+       return device_flags & NCTRL(ifnum);
+}
+
 static int option_attach(struct usb_serial *serial)
 {
        struct usb_interface_descriptor *iface_desc;
@@ -2148,7 +2167,7 @@ static int option_attach(struct usb_serial *serial)
 
        iface_desc = &serial->interface->cur_altsetting->desc;
 
-       if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber)))
+       if (!iface_no_modem_control(device_flags, iface_desc->bInterfaceNumber))
                data->use_send_setup = 1;
 
        if (device_flags & ZLP)
index 8e67eff..1c09b92 100644 (file)
@@ -62,7 +62,6 @@
  * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this
  *     port.
  * @flags: usb serial port flags
- * @write_wait: a wait_queue_head_t used by the port.
  * @work: work queue entry for the line discipline waking up.
  * @dev: pointer to the serial device
  *
@@ -108,7 +107,6 @@ struct usb_serial_port {
        int                     tx_bytes;
 
        unsigned long           flags;
-       wait_queue_head_t       write_wait;
        struct work_struct      work;
        unsigned long           sysrq; /* sysrq timeout */
        struct device           dev;