v4.19.13 snapshot.
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
new file mode 100644
index 0000000..533f127
--- /dev/null
+++ b/drivers/usb/serial/Kconfig
@@ -0,0 +1,659 @@
+#
+# USB Serial device configuration
+#
+
+menuconfig USB_SERIAL
+	tristate "USB Serial Converter support"
+	depends on TTY
+	---help---
+	  Say Y here if you have a USB device that provides normal serial
+	  ports, or acts like a serial device, and you want to connect it to
+	  your USB bus.
+
+	  Please read <file:Documentation/usb/usb-serial.txt> for more
+	  information on the specifics of the different devices that are
+	  supported, and on how to use them.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbserial.
+
+if USB_SERIAL
+
+config USB_SERIAL_CONSOLE
+	bool "USB Serial Console device support"
+	depends on USB_SERIAL=y
+	---help---
+	  If you say Y here, it will be possible to use a USB to serial
+	  converter port as the system console (the system console is the
+	  device which receives all kernel messages and warnings and which
+	  allows logins in single user mode). This could be useful if some
+	  terminal or printer is connected to that serial port.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyUSB0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+	  If you don't have a VGA card installed and you say Y here, the
+	  kernel will automatically use the first USB to serial converter
+	  port, /dev/ttyUSB0, as system console.
+
+	  If unsure, say N.
+
+config USB_SERIAL_GENERIC
+	bool "USB Generic Serial Driver"
+	help
+	  Say Y here if you want to use the generic USB serial driver.  Please
+	  read <file:Documentation/usb/usb-serial.txt> for more information on
+	  using this driver.  It is recommended that the "USB Serial converter
+	  support" be compiled as a module for this driver to be used
+	  properly.
+
+config USB_SERIAL_SIMPLE
+	tristate "USB Serial Simple Driver"
+	help
+	  Say Y here to use the USB serial "simple" driver.  This driver
+	  handles a wide range of very simple devices, all in one
+	  driver.  Specifically, it supports:
+		- Suunto ANT+ USB device.
+		- Medtronic CareLink USB device
+		- Fundamental Software dongle.
+		- Google USB serial devices
+		- HP4x calculators
+		- Libtransistor USB console
+		- a number of Motorola phones
+		- Motorola Tetra devices
+		- Novatel Wireless GPS receivers
+		- Siemens USB/MPI adapter.
+		- ViVOtech ViVOpay USB device.
+		- Infineon Modem Flashloader USB interface
+		- ZIO Motherboard USB serial interface
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called usb-serial-simple.
+
+config USB_SERIAL_AIRCABLE
+	tristate "USB AIRcable Bluetooth Dongle Driver"
+	help
+	    Say Y here if you want to use USB AIRcable Bluetooth Dongle.
+
+	    To compile this driver as a module, choose M here: the module
+	    will be called aircable.
+
+config USB_SERIAL_ARK3116
+	tristate "USB ARK Micro 3116 USB Serial Driver"
+	help
+	  Say Y here if you want to use a ARK Micro 3116 USB to Serial
+	  device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ark3116
+
+config USB_SERIAL_BELKIN
+	tristate "USB Belkin and Peracom Single Port Serial Driver"
+	help
+	  Say Y here if you want to use a Belkin USB Serial single port
+	  adaptor (F5U103 is one of the model numbers) or the Peracom single
+	  port USB to serial adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called belkin_sa.
+
+config USB_SERIAL_CH341
+	tristate "USB Winchiphead CH341 Single Port Serial Driver"
+	help
+	  Say Y here if you want to use a Winchiphead CH341 single port
+	  USB to serial adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ch341.
+
+config USB_SERIAL_WHITEHEAT
+	tristate "USB ConnectTech WhiteHEAT Serial Driver"
+	select USB_EZUSB_FX2
+	help
+	  Say Y here if you want to use a ConnectTech WhiteHEAT 4 port
+	  USB to serial converter device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called whiteheat.
+
+config USB_SERIAL_DIGI_ACCELEPORT
+	tristate "USB Digi International AccelePort USB Serial Driver"
+	---help---
+	  Say Y here if you want to use Digi AccelePort USB 2 or 4 devices,
+	  2 port (plus parallel port) and 4 port USB serial converters.  The
+	  parallel port on the USB 2 appears as a third serial port on Linux.
+	  The Digi Acceleport USB 8 is not yet supported by this driver.
+
+	  This driver works under SMP with the usb-uhci driver.  It does not
+	  work under SMP with the uhci driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called digi_acceleport.
+
+config USB_SERIAL_CP210X
+	tristate "USB CP210x family of UART Bridge Controllers"
+	help
+	  Say Y here if you want to use a CP2101/CP2102/CP2103 based USB
+	  to RS232 converters.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cp210x.
+
+config USB_SERIAL_CYPRESS_M8
+	tristate "USB Cypress M8 USB Serial Driver"
+	help
+	  Say Y here if you want to use a device that contains the Cypress
+	  USB to Serial microcontroller, such as the DeLorme Earthmate GPS.
+
+		Attempted SMP support... send bug reports!
+
+	  Supported microcontrollers in the CY4601 family are:
+		CY7C63741 CY7C63742 CY7C63743 CY7C64013
+	
+	  To compile this driver as a module, choose M here: the
+	  module will be called cypress_m8.
+
+config USB_SERIAL_EMPEG
+	tristate "USB Empeg empeg-car Mark I/II Driver"
+	help
+	  Say Y here if you want to connect to your Empeg empeg-car Mark I/II
+	  mp3 player via USB.  The driver uses a single ttyUSB{0,1,2,...}
+	  device node.  See <file:Documentation/usb/usb-serial.txt> for more
+	  tidbits of information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called empeg.
+
+config USB_SERIAL_FTDI_SIO
+	tristate "USB FTDI Single Port Serial Driver"
+	---help---
+	  Say Y here if you want to use a FTDI SIO single port USB to serial
+	  converter device. The implementation I have is called the USC-1000.
+	  This driver has also been tested with the 245 and 232 devices.
+
+	  See <http://ftdi-usb-sio.sourceforge.net/> for more
+	  information on this driver and the device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ftdi_sio.
+
+config USB_SERIAL_VISOR
+	tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver"
+	help
+	  Say Y here if you want to connect to your HandSpring Visor, Palm
+	  m500 or m505 through its USB docking station. See
+	  <http://usbvisor.sourceforge.net/index.php3> for more information on using this
+	  driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called visor.
+
+config USB_SERIAL_IPAQ
+	tristate "USB PocketPC PDA Driver"
+	help
+	  Say Y here if you want to connect to your Compaq iPAQ, HP Jornada
+	  or any other PDA running Windows CE 3.0 or PocketPC 2002
+	  using a USB cradle/cable. For information on using the driver,
+	  read <file:Documentation/usb/usb-serial.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ipaq.
+
+config USB_SERIAL_IR
+	tristate "USB IR Dongle Serial Driver"
+	help
+	  Say Y here if you want to enable simple serial support for USB IrDA
+	  devices.  This is useful if you do not want to use the full IrDA
+	  stack.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ir-usb.
+
+config USB_SERIAL_EDGEPORT
+	tristate "USB Inside Out Edgeport Serial Driver"
+	---help---
+	  Say Y here if you want to use any of the following devices from
+	  Inside Out Networks (Digi):
+	  Edgeport/4
+	  Rapidport/4
+	  Edgeport/4t
+	  Edgeport/2
+	  Edgeport/4i
+	  Edgeport/2i
+	  Edgeport/421
+	  Edgeport/21
+	  Edgeport/8
+	  Edgeport/8 Dual
+	  Edgeport/2D8
+	  Edgeport/4D8
+	  Edgeport/8i
+	  Edgeport/2 DIN
+	  Edgeport/4 DIN
+	  Edgeport/16 Dual
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called io_edgeport.
+
+config USB_SERIAL_EDGEPORT_TI
+	tristate "USB Inside Out Edgeport Serial Driver (TI devices)"
+	help
+	  Say Y here if you want to use any of the devices from Inside Out
+	  Networks (Digi) that are not supported by the io_edgeport driver.
+	  This includes the Edgeport/1 device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called io_ti.
+
+config USB_SERIAL_F81232
+	tristate "USB Fintek F81232 Single Port Serial Driver"
+	help
+	  Say Y here if you want to use the Fintek F81232 single
+	  port usb to serial adapter.
+
+	  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
+         Say Y here if you want to connect to your Garmin GPS.
+         Should work with most Garmin GPS devices which have a native USB port.
+
+         See <http://sourceforge.net/projects/garmin-gps> for the latest
+         version of the driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called garmin_gps.
+
+config USB_SERIAL_IPW
+        tristate "USB IPWireless (3G UMTS TDD) Driver"
+	select USB_SERIAL_WWAN
+	help
+	  Say Y here if you want to use a IPWireless USB modem such as
+	  the ones supplied by Axity3G/Sentech South Africa.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ipw.
+
+config USB_SERIAL_IUU
+	tristate "USB Infinity USB Unlimited Phoenix Driver"
+	help
+	  Say Y here if you want to use a IUU in phoenix mode and get
+	  an extra ttyUSBx device. More information available on
+	  http://eczema.ecze.com/iuu_phoenix.html
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iuu_phoenix.o
+
+config USB_SERIAL_KEYSPAN_PDA
+	tristate "USB Keyspan PDA 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.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called keyspan_pda.
+
+config USB_SERIAL_KEYSPAN
+	tristate "USB Keyspan USA-xxx Serial Driver"
+	select USB_EZUSB_FX2
+	---help---
+	  Say Y here if you want to use Keyspan USB to serial converter
+	  devices.  This driver makes use of Keyspan's official firmware
+	  and was developed with their support.  You must also include
+	  firmware to support your particular device(s).
+
+	  See <http://blemings.org/hugh/keyspan.html> for more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called keyspan.
+
+config USB_SERIAL_KLSI
+	tristate "USB KL5KUSB105 (Palmconnect) Driver"
+	---help---
+	  Say Y here if you want to use a KL5KUSB105 - based single port
+	  serial adapter. The most widely known -- and currently the only
+	  tested -- device in this category is the PalmConnect USB Serial
+	  adapter sold by Palm Inc. for use with their Palm III and Palm V
+	  series PDAs.
+
+	  Please read <file:Documentation/usb/usb-serial.txt> for more
+	  information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called kl5kusb105.
+
+config USB_SERIAL_KOBIL_SCT
+        tristate "USB KOBIL chipcard reader"
+        ---help---
+          Say Y here if you want to use one of the following KOBIL USB chipcard
+          readers:
+
+            - USB TWIN
+            - KAAN Standard Plus
+            - KAAN SIM
+            - SecOVID Reader Plus
+            - B1 Professional
+            - KAAN Professional
+
+          Note that you need a current CT-API.
+          To compile this driver as a module, choose M here: the
+	  module will be called kobil_sct.
+
+config USB_SERIAL_MCT_U232
+	tristate "USB MCT Single Port Serial Driver"
+	---help---
+	  Say Y here if you want to use a USB Serial single port adapter from
+	  Magic Control Technology Corp. (U232 is one of the model numbers).
+
+	  This driver also works with Sitecom U232-P25 and D-Link DU-H3SP USB
+	  BAY, Belkin F5U109, and Belkin F5U409 devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mct_u232.
+
+config USB_SERIAL_METRO
+	tristate "USB Metrologic Instruments USB-POS Barcode Scanner Driver"
+	---help---
+	  Say Y here if you want to use a USB POS Metrologic barcode scanner.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called metro-usb.
+
+config USB_SERIAL_MOS7720
+	tristate "USB Moschip 7720 Serial Driver"
+	---help---
+	  Say Y here if you want to use USB Serial single and double
+	  port adapters from Moschip Semiconductor Tech.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mos7720.
+
+config USB_SERIAL_MOS7715_PARPORT
+	bool "Support for parallel port on the Moschip 7715"
+	depends on USB_SERIAL_MOS7720
+	depends on PARPORT=y || PARPORT=USB_SERIAL_MOS7720
+	select PARPORT_NOT_PC
+	---help---
+	Say Y if you have a Moschip 7715 device and would like to use
+	the parallel port it provides.  The port will register with
+	the parport subsystem as a low-level driver.
+
+config USB_SERIAL_MOS7840
+	tristate "USB Moschip 7840/7820 USB Serial Driver"
+	---help---
+	  Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
+	  Dual-Serial port device from MosChip Semiconductor.
+
+	  The MCS7840 and MCS7820 have been developed to connect a wide range
+	  of standard serial devices to a USB host.  The MCS7840 has a USB
+	  device controller connected to four (4) individual UARTs while the
+	  MCS7820 controller connects to two (2) individual UARTs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mos7840.  If unsure, choose N.
+
+config USB_SERIAL_MXUPORT
+	tristate "USB Moxa UPORT Serial Driver"
+	---help---
+	  Say Y here if you want to use a MOXA UPort Serial hub.
+
+	  This driver supports:
+
+	  [2 Port]
+	  - UPort 1250 :  2 Port RS-232/422/485 USB to Serial Hub
+	  - UPort 1250I : 2 Port RS-232/422/485 USB to Serial Hub with
+			  Isolation
+
+	  [4 Port]
+	  - UPort 1410 :  4 Port RS-232 USB to Serial Hub
+	  - UPort 1450 :  4 Port RS-232/422/485 USB to Serial Hub
+	  - UPort 1450I : 4 Port RS-232/422/485 USB to Serial Hub with
+			  Isolation
+
+	  [8 Port]
+	  - UPort 1610-8 : 8 Port RS-232 USB to Serial Hub
+	  - UPort 1650-8 : 8 Port RS-232/422/485 USB to Serial Hub
+
+	  [16 Port]
+	  - UPort 1610-16 : 16 Port RS-232 USB to Serial Hub
+	  - UPort 1650-16 : 16 Port RS-232/422/485 USB to Serial Hub
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mxuport.
+
+config USB_SERIAL_NAVMAN
+	tristate "USB Navman GPS device"
+	help
+	  To compile this driver as a module, choose M here: the
+	  module will be called navman.
+
+config USB_SERIAL_PL2303
+	tristate "USB Prolific 2303 Single Port Serial Driver"
+	help
+	  Say Y here if you want to use the PL2303 USB Serial single port
+	  adapter from Prolific.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pl2303.
+
+config USB_SERIAL_OTI6858
+	tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller"
+	help
+	  Say Y here if you want to use the OTi-6858 single port USB to serial
+          converter device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called oti6858.
+
+config USB_SERIAL_QCAUX
+	tristate "USB Qualcomm Auxiliary Serial Port Driver"
+	help
+	  Say Y here if you want to use the auxiliary serial ports provided
+	  by many modems based on Qualcomm chipsets.  These ports often use
+	  a proprietary protocol called DM and cannot be used for AT- or
+	  PPP-based communication.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qcaux.  If unsure, choose N.
+
+config USB_SERIAL_QUALCOMM
+	tristate "USB Qualcomm Serial modem"
+	select USB_SERIAL_WWAN
+	help
+	  Say Y here if you have a Qualcomm USB modem device.  These are
+	  usually wireless cellular modems.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qcserial.
+
+config USB_SERIAL_SPCP8X5
+	tristate "USB SPCP8x5 USB To Serial Driver"
+	help
+	  Say Y here if you want to use the spcp8x5 converter chip.  This is
+	  commonly found in some Z-Wave USB devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spcp8x5.
+
+config USB_SERIAL_SAFE
+	tristate "USB Safe Serial (Encapsulated) Driver"
+
+config USB_SERIAL_SAFE_PADDED
+	bool "USB Secure Encapsulated Driver - Padded"
+	depends on USB_SERIAL_SAFE
+
+config USB_SERIAL_SIERRAWIRELESS
+	tristate "USB Sierra Wireless Driver"
+	help
+	  Say M here if you want to use Sierra Wireless devices.
+
+	  Many devices have a feature known as TRU-Install. For those devices
+	  to work properly, the USB Storage Sierra feature must be enabled.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sierra.
+
+config USB_SERIAL_SYMBOL
+	tristate "USB Symbol Barcode driver (serial mode)"
+	help
+	  Say Y here if you want to use a Symbol USB Barcode device
+	  in serial emulation mode.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called symbolserial.
+
+config USB_SERIAL_TI
+	tristate "USB TI 3410/5052 Serial Driver"
+	help
+	  Say Y here if you want to use the TI USB 3410 or 5052
+	  serial devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti_usb_3410_5052.
+
+config USB_SERIAL_CYBERJACK
+	tristate "USB REINER SCT cyberJack pinpad/e-com chipcard reader"
+	---help---
+	  Say Y here if you want to use a cyberJack pinpad/e-com USB chipcard
+	  reader. This is an interface to ISO 7816 compatible contact-based
+	  chipcards, e.g. GSM SIMs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called 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
+
+config USB_SERIAL_OPTION
+	tristate "USB driver for GSM and CDMA modems"
+	select USB_SERIAL_WWAN
+	help
+	  Say Y here if you have a GSM or CDMA modem that's connected to USB.
+
+	  This driver also supports several PCMCIA cards which have a
+	  built-in OHCI-USB adapter and an internally-connected GSM modem.
+	  The USB bus on these cards is not accessible externally.
+
+	  Supported devices include (some of?) those made by:
+	  Option, Huawei, Audiovox, Novatel Wireless, or Anydata.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called option.
+
+	  If this driver doesn't recognize your device,
+	  it might be accessible via the FTDI_SIO driver.
+
+config USB_SERIAL_OMNINET
+	tristate "USB ZyXEL omni.net LCD Plus Driver"
+	help
+	  Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called omninet.
+
+config USB_SERIAL_OPTICON
+	tristate "USB Opticon Barcode driver (serial mode)"
+	help
+	  Say Y here if you want to use a Opticon USB Barcode device
+	  in serial emulation mode.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called opticon.
+
+config USB_SERIAL_XSENS_MT
+	tristate "Xsens motion tracker serial interface driver"
+	help
+	  Say Y here if you want to use Xsens motion trackers.
+
+	  This driver supports the new generation of motion trackers
+	  by Xsens. Older devices can be accessed using the FTDI_SIO
+	  driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xsens_mt.
+
+config USB_SERIAL_WISHBONE
+	tristate "USB-Wishbone adapter interface driver"
+	help
+	  Say Y here if you want to use a USB attached Wishbone bus.
+
+	  Wishbone is an open hardware SoC bus commonly used in FPGA
+	  designs. Bus access can be serialized using the Etherbone
+	  protocol <http://www.ohwr.org/projects/etherbone-core>.
+
+	  This driver is intended to be used with devices which attach
+	  their internal Wishbone bus to a USB serial interface using
+	  the Etherbone protocol. A userspace library is required to
+	  speak the protocol made available by this driver as ttyUSBx.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wishbone-serial.
+
+config USB_SERIAL_SSU100
+	tristate "USB Quatech SSU-100 Single Port Serial Driver"
+	help
+	  Say Y here if you want to use the Quatech SSU-100 single
+	  port usb to serial adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ssu100.
+
+config USB_SERIAL_QT2
+	tristate "USB Quatech Serial Driver for USB 2 devices"
+	help
+	  Say Y here if you want to use the Quatech USB 2
+	  serial adapters.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called quatech-serial.
+
+config USB_SERIAL_UPD78F0730
+	tristate "USB Renesas uPD78F0730 Single Port Serial Driver"
+	help
+	  Say Y here if you want to use the Renesas uPD78F0730
+	  serial driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called upd78f0730.
+
+config USB_SERIAL_DEBUG
+	tristate "USB Debugging Device"
+	help
+	  Say Y here if you have a USB debugging device used to receive
+	  debugging data from another machine.  The most common of these
+	  devices is the NetChip TurboCONNECT device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usb-debug.
+
+endif # USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
new file mode 100644
index 0000000..2d491e4
--- /dev/null
+++ b/drivers/usb/serial/Makefile
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the USB serial device drivers.
+#
+
+# Object file lists.
+
+obj-$(CONFIG_USB_SERIAL)			+= usbserial.o
+
+usbserial-y := usb-serial.o generic.o bus.o
+
+usbserial-$(CONFIG_USB_SERIAL_CONSOLE)	+= console.o
+
+obj-$(CONFIG_USB_SERIAL_AIRCABLE)		+= aircable.o
+obj-$(CONFIG_USB_SERIAL_ARK3116)		+= ark3116.o
+obj-$(CONFIG_USB_SERIAL_BELKIN)			+= belkin_sa.o
+obj-$(CONFIG_USB_SERIAL_CH341)			+= ch341.o
+obj-$(CONFIG_USB_SERIAL_CP210X)			+= cp210x.o
+obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
+obj-$(CONFIG_USB_SERIAL_CYPRESS_M8)		+= cypress_m8.o
+obj-$(CONFIG_USB_SERIAL_DEBUG)			+= usb_debug.o
+obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT)	+= digi_acceleport.o
+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
+obj-$(CONFIG_USB_SERIAL_IPW)			+= ipw.o
+obj-$(CONFIG_USB_SERIAL_IR)			+= ir-usb.o
+obj-$(CONFIG_USB_SERIAL_IUU)			+= iuu_phoenix.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN)		+= keyspan.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
+obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
+obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
+obj-$(CONFIG_USB_SERIAL_MCT_U232)		+= mct_u232.o
+obj-$(CONFIG_USB_SERIAL_METRO)			+= metro-usb.o
+obj-$(CONFIG_USB_SERIAL_MOS7720)		+= mos7720.o
+obj-$(CONFIG_USB_SERIAL_MOS7840)		+= mos7840.o
+obj-$(CONFIG_USB_SERIAL_MXUPORT)		+= mxuport.o
+obj-$(CONFIG_USB_SERIAL_NAVMAN)			+= navman.o
+obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
+obj-$(CONFIG_USB_SERIAL_OPTICON)		+= opticon.o
+obj-$(CONFIG_USB_SERIAL_OPTION)			+= option.o
+obj-$(CONFIG_USB_SERIAL_OTI6858)		+= oti6858.o
+obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
+obj-$(CONFIG_USB_SERIAL_QCAUX)			+= qcaux.o
+obj-$(CONFIG_USB_SERIAL_QUALCOMM)		+= qcserial.o
+obj-$(CONFIG_USB_SERIAL_QT2)			+= quatech2.o
+obj-$(CONFIG_USB_SERIAL_SAFE)			+= safe_serial.o
+obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS)		+= sierra.o
+obj-$(CONFIG_USB_SERIAL_SIMPLE)			+= usb-serial-simple.o
+obj-$(CONFIG_USB_SERIAL_SPCP8X5)		+= spcp8x5.o
+obj-$(CONFIG_USB_SERIAL_SSU100)			+= ssu100.o
+obj-$(CONFIG_USB_SERIAL_SYMBOL)			+= symbolserial.o
+obj-$(CONFIG_USB_SERIAL_WWAN)			+= usb_wwan.o
+obj-$(CONFIG_USB_SERIAL_TI)			+= ti_usb_3410_5052.o
+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
diff --git a/drivers/usb/serial/Makefile-keyspan_pda_fw b/drivers/usb/serial/Makefile-keyspan_pda_fw
new file mode 100644
index 0000000..503b472
--- /dev/null
+++ b/drivers/usb/serial/Makefile-keyspan_pda_fw
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# some rules to handle the quirks of the 'as31' assembler, like
+# insisting upon fixed suffixes for the input and output files,
+# and its lack of preprocessor support
+
+all: keyspan_pda_fw.h
+
+%.asm: %.S
+	gcc -x assembler-with-cpp -P -E -o $@ $<
+
+%.hex: %.asm
+	as31 -l $<
+	mv $*.obj $@
+
+%_fw.h: %.hex ezusb_convert.pl
+	perl ezusb_convert.pl $* < $< > $@
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
new file mode 100644
index 0000000..84d5295
--- /dev/null
+++ b/drivers/usb/serial/aircable.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AIRcable USB Bluetooth Dongle Driver.
+ *
+ * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
+ *
+ * The device works as an standard CDC device, it has 2 interfaces, the first
+ * one is for firmware access and the second is the serial one.
+ * The protocol is very simply, there are two possibilities reading or writing.
+ * When writing the first urb must have a Header that starts with 0x20 0x29 the
+ * next two bytes must say how much data will be sent.
+ * When reading the process is almost equal except that the header starts with
+ * 0x00 0x20.
+ *
+ * The device simply need some stuff to understand data coming from the usb
+ * buffer: The First and Second byte is used for a Header, the Third and Fourth
+ * tells the  device the amount of information the package holds.
+ * Packages are 60 bytes long Header Stuff.
+ * When writing to the device the first two bytes of the header are 0x20 0x29
+ * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
+ * situation, when too much data arrives to the device because it sends the data
+ * but with out the header. I will use a simply hack to override this situation,
+ * if there is data coming that does not contain any header, then that is data
+ * that must go directly to the tty, as there is no documentation about if there
+ * is any other control code, I will simply check for the first
+ * one.
+ *
+ * I have taken some info from a Greg Kroah-Hartman article:
+ * http://www.linuxjournal.com/article/6573
+ * And from Linux Device Driver Kit CD, which is a great work, the authors taken
+ * the work to recompile lots of information an knowledge in drivers development
+ * and made it all available inside a cd.
+ * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
+ *
+ */
+
+#include <asm/unaligned.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/tty_flip.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* Vendor and Product ID */
+#define AIRCABLE_VID		0x16CA
+#define AIRCABLE_USB_PID	0x1502
+
+/* Protocol Stuff */
+#define HCI_HEADER_LENGTH	0x4
+#define TX_HEADER_0		0x20
+#define TX_HEADER_1		0x29
+#define RX_HEADER_0		0x00
+#define RX_HEADER_1		0x20
+#define HCI_COMPLETE_FRAME	64
+
+/* rx_flags */
+#define THROTTLED		0x01
+#define ACTUALLY_THROTTLED	0x02
+
+#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "AIRcable USB Driver"
+
+/* ID table that will be registered with USB core */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int aircable_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	int count;
+	unsigned char *buf = dest;
+
+	count = kfifo_out_locked(&port->write_fifo, buf + HCI_HEADER_LENGTH,
+					size - HCI_HEADER_LENGTH, &port->lock);
+	buf[0] = TX_HEADER_0;
+	buf[1] = TX_HEADER_1;
+	put_unaligned_le16(count, &buf[2]);
+
+	return count + HCI_HEADER_LENGTH;
+}
+
+static int aircable_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	/* Ignore the first interface, which has no bulk endpoints. */
+	if (epds->num_bulk_out == 0) {
+		dev_dbg(&serial->interface->dev,
+			"ignoring interface with no bulk-out endpoints\n");
+		return -ENODEV;
+	}
+
+	return 1;
+}
+
+static int aircable_process_packet(struct usb_serial_port *port,
+		int has_headers, char *packet, int len)
+{
+	if (has_headers) {
+		len -= HCI_HEADER_LENGTH;
+		packet += HCI_HEADER_LENGTH;
+	}
+	if (len <= 0) {
+		dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
+		return 0;
+	}
+
+	tty_insert_flip_string(&port->port, packet, len);
+
+	return len;
+}
+
+static void aircable_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	char *data = (char *)urb->transfer_buffer;
+	int has_headers;
+	int count;
+	int len;
+	int i;
+
+	has_headers = (urb->actual_length > 2 && data[0] == RX_HEADER_0);
+
+	count = 0;
+	for (i = 0; i < urb->actual_length; i += HCI_COMPLETE_FRAME) {
+		len = min_t(int, urb->actual_length - i, HCI_COMPLETE_FRAME);
+		count += aircable_process_packet(port, has_headers,
+								&data[i], len);
+	}
+
+	if (count)
+		tty_flip_buffer_push(&port->port);
+}
+
+static struct usb_serial_driver aircable_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"aircable",
+	},
+	.id_table = 		id_table,
+	.bulk_out_size =	HCI_COMPLETE_FRAME,
+	.calc_num_ports =	aircable_calc_num_ports,
+	.process_read_urb =	aircable_process_read_urb,
+	.prepare_write_buffer =	aircable_prepare_write_buffer,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&aircable_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
new file mode 100644
index 0000000..7796ad8
--- /dev/null
+++ b/drivers/usb/serial/ark3116.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com)
+ * Original version:
+ * Copyright (C) 2006
+ *   Simon Schulz (ark3116_driver <at> auctionant.de)
+ *
+ * ark3116
+ * - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
+ *   productid=0x0232) (used in a datacable called KQ-U8A)
+ *
+ * Supports full modem status lines, break, hardware flow control. Does not
+ * support software flow control, since I do not know how to enable it in hw.
+ *
+ * This driver is a essentially new implementation. I initially dug
+ * into the old ark3116.c driver and suddenly realized the ark3116 is
+ * a 16450 with a USB interface glued to it. See comments at the
+ * bottom of this file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ioctl.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>"
+#define DRIVER_DESC "USB ARK3116 serial/IrDA driver"
+#define DRIVER_DEV_DESC "ARK3116 RS232/IrDA"
+#define DRIVER_NAME "ark3116"
+
+/* usb timeout of 1 second */
+#define ARK_TIMEOUT 1000
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x6547, 0x0232) },
+	{ USB_DEVICE(0x18ec, 0x3118) },		/* USB to IrDA adapter */
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int is_irda(struct usb_serial *serial)
+{
+	struct usb_device *dev = serial->dev;
+	if (le16_to_cpu(dev->descriptor.idVendor) == 0x18ec &&
+			le16_to_cpu(dev->descriptor.idProduct) == 0x3118)
+		return 1;
+	return 0;
+}
+
+struct ark3116_private {
+	int			irda;	/* 1 for irda device */
+
+	/* protects hw register updates */
+	struct mutex		hw_lock;
+
+	int			quot;	/* baudrate divisor */
+	__u32			lcr;	/* line control register value */
+	__u32			hcr;	/* handshake control register (0x8)
+					 * value */
+	__u32			mcr;	/* modem control register value */
+
+	/* protects the status values below */
+	spinlock_t		status_lock;
+	__u32			msr;	/* modem status register value */
+	__u32			lsr;	/* line status register value */
+};
+
+static int ark3116_write_reg(struct usb_serial *serial,
+			     unsigned reg, __u8 val)
+{
+	int result;
+	 /* 0xfe 0x40 are magic values taken from original driver */
+	result = usb_control_msg(serial->dev,
+				 usb_sndctrlpipe(serial->dev, 0),
+				 0xfe, 0x40, val, reg,
+				 NULL, 0, ARK_TIMEOUT);
+	if (result)
+		return result;
+
+	return 0;
+}
+
+static int ark3116_read_reg(struct usb_serial *serial,
+			    unsigned reg, unsigned char *buf)
+{
+	int result;
+	/* 0xfe 0xc0 are magic values taken from original driver */
+	result = usb_control_msg(serial->dev,
+				 usb_rcvctrlpipe(serial->dev, 0),
+				 0xfe, 0xc0, 0, reg,
+				 buf, 1, ARK_TIMEOUT);
+	if (result < 1) {
+		dev_err(&serial->interface->dev,
+				"failed to read register %u: %d\n",
+				reg, result);
+		if (result >= 0)
+			result = -EIO;
+
+		return result;
+	}
+
+	return 0;
+}
+
+static inline int calc_divisor(int bps)
+{
+	/* Original ark3116 made some exceptions in rounding here
+	 * because windows did the same. Assume that is not really
+	 * necessary.
+	 * Crystal is 12MHz, probably because of USB, but we divide by 4?
+	 */
+	return (12000000 + 2*bps) / (4*bps);
+}
+
+static int ark3116_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct ark3116_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mutex_init(&priv->hw_lock);
+	spin_lock_init(&priv->status_lock);
+
+	priv->irda = is_irda(serial);
+
+	usb_set_serial_port_data(port, priv);
+
+	/* setup the hardware */
+	ark3116_write_reg(serial, UART_IER, 0);
+	/* disable DMA */
+	ark3116_write_reg(serial, UART_FCR, 0);
+	/* handshake control */
+	priv->hcr = 0;
+	ark3116_write_reg(serial, 0x8     , 0);
+	/* modem control */
+	priv->mcr = 0;
+	ark3116_write_reg(serial, UART_MCR, 0);
+
+	if (!(priv->irda)) {
+		ark3116_write_reg(serial, 0xb , 0);
+	} else {
+		ark3116_write_reg(serial, 0xb , 1);
+		ark3116_write_reg(serial, 0xc , 0);
+		ark3116_write_reg(serial, 0xd , 0x41);
+		ark3116_write_reg(serial, 0xa , 1);
+	}
+
+	/* setup baudrate */
+	ark3116_write_reg(serial, UART_LCR, UART_LCR_DLAB);
+
+	/* setup for 9600 8N1 */
+	priv->quot = calc_divisor(9600);
+	ark3116_write_reg(serial, UART_DLL, priv->quot & 0xff);
+	ark3116_write_reg(serial, UART_DLM, (priv->quot>>8) & 0xff);
+
+	priv->lcr = UART_LCR_WLEN8;
+	ark3116_write_reg(serial, UART_LCR, UART_LCR_WLEN8);
+
+	ark3116_write_reg(serial, 0xe, 0);
+
+	if (priv->irda)
+		ark3116_write_reg(serial, 0x9, 0);
+
+	dev_info(&port->dev, "using %s mode\n", priv->irda ? "IrDA" : "RS232");
+
+	return 0;
+}
+
+static int ark3116_port_remove(struct usb_serial_port *port)
+{
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+	/* device is closed, so URBs and DMA should be down */
+	mutex_destroy(&priv->hw_lock);
+	kfree(priv);
+
+	return 0;
+}
+
+static void ark3116_init_termios(struct tty_struct *tty)
+{
+	struct ktermios *termios = &tty->termios;
+	*termios = tty_std_termios;
+	termios->c_cflag = B9600 | CS8
+				      | CREAD | HUPCL | CLOCAL;
+	termios->c_ispeed = 9600;
+	termios->c_ospeed = 9600;
+}
+
+static void ark3116_set_termios(struct tty_struct *tty,
+				struct usb_serial_port *port,
+				struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	struct ktermios *termios = &tty->termios;
+	unsigned int cflag = termios->c_cflag;
+	int bps = tty_get_baud_rate(tty);
+	int quot;
+	__u8 lcr, hcr, eval;
+
+	/* set data bit count */
+	switch (cflag & CSIZE) {
+	case CS5:
+		lcr = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		lcr = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		lcr = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		lcr = UART_LCR_WLEN8;
+		break;
+	}
+	if (cflag & CSTOPB)
+		lcr |= UART_LCR_STOP;
+	if (cflag & PARENB)
+		lcr |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		lcr |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (cflag & CMSPAR)
+		lcr |= UART_LCR_SPAR;
+#endif
+	/* handshake control */
+	hcr = (cflag & CRTSCTS) ? 0x03 : 0x00;
+
+	/* calc baudrate */
+	dev_dbg(&port->dev, "%s - setting bps to %d\n", __func__, bps);
+	eval = 0;
+	switch (bps) {
+	case 0:
+		quot = calc_divisor(9600);
+		break;
+	default:
+		if ((bps < 75) || (bps > 3000000))
+			bps = 9600;
+		quot = calc_divisor(bps);
+		break;
+	case 460800:
+		eval = 1;
+		quot = calc_divisor(bps);
+		break;
+	case 921600:
+		eval = 2;
+		quot = calc_divisor(bps);
+		break;
+	}
+
+	/* Update state: synchronize */
+	mutex_lock(&priv->hw_lock);
+
+	/* keep old LCR_SBC bit */
+	lcr |= (priv->lcr & UART_LCR_SBC);
+
+	dev_dbg(&port->dev, "%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d\n",
+		__func__, hcr, lcr, quot);
+
+	/* handshake control */
+	if (priv->hcr != hcr) {
+		priv->hcr = hcr;
+		ark3116_write_reg(serial, 0x8, hcr);
+	}
+
+	/* baudrate */
+	if (priv->quot != quot) {
+		priv->quot = quot;
+		priv->lcr = lcr; /* need to write lcr anyway */
+
+		/* disable DMA since transmit/receive is
+		 * shadowed by UART_DLL
+		 */
+		ark3116_write_reg(serial, UART_FCR, 0);
+
+		ark3116_write_reg(serial, UART_LCR,
+				  lcr|UART_LCR_DLAB);
+		ark3116_write_reg(serial, UART_DLL, quot & 0xff);
+		ark3116_write_reg(serial, UART_DLM, (quot>>8) & 0xff);
+
+		/* restore lcr */
+		ark3116_write_reg(serial, UART_LCR, lcr);
+		/* magic baudrate thingy: not sure what it does,
+		 * but windows does this as well.
+		 */
+		ark3116_write_reg(serial, 0xe, eval);
+
+		/* enable DMA */
+		ark3116_write_reg(serial, UART_FCR, UART_FCR_DMA_SELECT);
+	} else if (priv->lcr != lcr) {
+		priv->lcr = lcr;
+		ark3116_write_reg(serial, UART_LCR, lcr);
+	}
+
+	mutex_unlock(&priv->hw_lock);
+
+	/* check for software flow control */
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		dev_warn(&port->dev,
+				"software flow control not implemented\n");
+	}
+
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, bps, bps);
+}
+
+static void ark3116_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+
+	/* disable DMA */
+	ark3116_write_reg(serial, UART_FCR, 0);
+
+	/* deactivate interrupts */
+	ark3116_write_reg(serial, UART_IER, 0);
+
+	usb_serial_generic_close(port);
+
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	unsigned char *buf;
+	int result;
+
+	buf = kmalloc(1, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	result = usb_serial_generic_open(tty, port);
+	if (result) {
+		dev_dbg(&port->dev,
+			"%s - usb_serial_generic_open failed: %d\n",
+			__func__, result);
+		goto err_free;
+	}
+
+	/* remove any data still left: also clears error state */
+	ark3116_read_reg(serial, UART_RX, buf);
+
+	/* read modem status */
+	result = ark3116_read_reg(serial, UART_MSR, buf);
+	if (result)
+		goto err_close;
+	priv->msr = *buf;
+
+	/* read line status */
+	result = ark3116_read_reg(serial, UART_LSR, buf);
+	if (result)
+		goto err_close;
+	priv->lsr = *buf;
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result) {
+		dev_err(&port->dev, "submit irq_in urb failed %d\n",
+			result);
+		goto err_close;
+	}
+
+	/* activate interrupts */
+	ark3116_write_reg(port->serial, UART_IER, UART_IER_MSI|UART_IER_RLSI);
+
+	/* enable DMA */
+	ark3116_write_reg(port->serial, UART_FCR, UART_FCR_DMA_SELECT);
+
+	/* setup termios */
+	if (tty)
+		ark3116_set_termios(tty, port, NULL);
+
+	kfree(buf);
+
+	return 0;
+
+err_close:
+	usb_serial_generic_close(port);
+err_free:
+	kfree(buf);
+
+	return result;
+}
+
+static int ark3116_get_serial_info(struct usb_serial_port *port,
+			struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type = PORT_16654;
+	tmp.line = port->minor;
+	tmp.port = port->port_number;
+	tmp.baud_base = 460800;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(tmp)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int ark3116_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	void __user *user_arg = (void __user *)arg;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return ark3116_get_serial_info(port, user_arg);
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int ark3116_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	__u32 status;
+	__u32 ctrl;
+	unsigned long flags;
+
+	mutex_lock(&priv->hw_lock);
+	ctrl = priv->mcr;
+	mutex_unlock(&priv->hw_lock);
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	status = priv->msr;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	return  (status & UART_MSR_DSR  ? TIOCM_DSR  : 0) |
+		(status & UART_MSR_CTS  ? TIOCM_CTS  : 0) |
+		(status & UART_MSR_RI   ? TIOCM_RI   : 0) |
+		(status & UART_MSR_DCD  ? TIOCM_CD   : 0) |
+		(ctrl   & UART_MCR_DTR  ? TIOCM_DTR  : 0) |
+		(ctrl   & UART_MCR_RTS  ? TIOCM_RTS  : 0) |
+		(ctrl   & UART_MCR_OUT1 ? TIOCM_OUT1 : 0) |
+		(ctrl   & UART_MCR_OUT2 ? TIOCM_OUT2 : 0);
+}
+
+static int ark3116_tiocmset(struct tty_struct *tty,
+			unsigned set, unsigned clr)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+	/* we need to take the mutex here, to make sure that the value
+	 * in priv->mcr is actually the one that is in the hardware
+	 */
+
+	mutex_lock(&priv->hw_lock);
+
+	if (set & TIOCM_RTS)
+		priv->mcr |= UART_MCR_RTS;
+	if (set & TIOCM_DTR)
+		priv->mcr |= UART_MCR_DTR;
+	if (set & TIOCM_OUT1)
+		priv->mcr |= UART_MCR_OUT1;
+	if (set & TIOCM_OUT2)
+		priv->mcr |= UART_MCR_OUT2;
+	if (clr & TIOCM_RTS)
+		priv->mcr &= ~UART_MCR_RTS;
+	if (clr & TIOCM_DTR)
+		priv->mcr &= ~UART_MCR_DTR;
+	if (clr & TIOCM_OUT1)
+		priv->mcr &= ~UART_MCR_OUT1;
+	if (clr & TIOCM_OUT2)
+		priv->mcr &= ~UART_MCR_OUT2;
+
+	ark3116_write_reg(port->serial, UART_MCR, priv->mcr);
+
+	mutex_unlock(&priv->hw_lock);
+
+	return 0;
+}
+
+static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+	/* LCR is also used for other things: protect access */
+	mutex_lock(&priv->hw_lock);
+
+	if (break_state)
+		priv->lcr |= UART_LCR_SBC;
+	else
+		priv->lcr &= ~UART_LCR_SBC;
+
+	ark3116_write_reg(port->serial, UART_LCR, priv->lcr);
+
+	mutex_unlock(&priv->hw_lock);
+}
+
+static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)
+{
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	priv->msr = msr;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	if (msr & UART_MSR_ANY_DELTA) {
+		/* 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);
+	}
+}
+
+static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr)
+{
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	/* combine bits */
+	priv->lsr |= lsr;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	if (lsr&UART_LSR_BRK_ERROR_BITS) {
+		if (lsr & UART_LSR_BI)
+			port->icount.brk++;
+		if (lsr & UART_LSR_FE)
+			port->icount.frame++;
+		if (lsr & UART_LSR_PE)
+			port->icount.parity++;
+		if (lsr & UART_LSR_OE)
+			port->icount.overrun++;
+	}
+}
+
+static void ark3116_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
+	const __u8 *data = urb->transfer_buffer;
+	int result;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		break;
+	case 0: /* success */
+		/* discovered this by trail and error... */
+		if ((urb->actual_length == 4) && (data[0] == 0xe8)) {
+			const __u8 id = data[1]&UART_IIR_ID;
+			dev_dbg(&port->dev, "%s: iir=%02x\n", __func__, data[1]);
+			if (id == UART_IIR_MSI) {
+				dev_dbg(&port->dev, "%s: msr=%02x\n",
+					__func__, data[3]);
+				ark3116_update_msr(port, data[3]);
+				break;
+			} else if (id == UART_IIR_RLSI) {
+				dev_dbg(&port->dev, "%s: lsr=%02x\n",
+					__func__, data[2]);
+				ark3116_update_lsr(port, data[2]);
+				break;
+			}
+		}
+		/*
+		 * Not sure what this data meant...
+		 */
+		usb_serial_debug_data(&port->dev, __func__,
+				      urb->actual_length,
+				      urb->transfer_buffer);
+		break;
+	}
+
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&port->dev, "failed to resubmit interrupt urb: %d\n",
+			result);
+}
+
+
+/* Data comes in via the bulk (data) URB, errors/interrupts via the int URB.
+ * This means that we cannot be sure which data byte has an associated error
+ * condition, so we report an error for all data in the next bulk read.
+ *
+ * Actually, there might even be a window between the bulk data leaving the
+ * ark and reading/resetting the lsr in the read_bulk_callback where an
+ * interrupt for the next data block could come in.
+ * Without somekind of ordering on the ark, we would have to report the
+ * error for the next block of data as well...
+ * For now, let's pretend this can't happen.
+ */
+static void ark3116_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	char tty_flag = TTY_NORMAL;
+	unsigned long flags;
+	__u32 lsr;
+
+	/* update line status */
+	spin_lock_irqsave(&priv->status_lock, flags);
+	lsr = priv->lsr;
+	priv->lsr &= ~UART_LSR_BRK_ERROR_BITS;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	if (!urb->actual_length)
+		return;
+
+	if (lsr & UART_LSR_BRK_ERROR_BITS) {
+		if (lsr & UART_LSR_BI)
+			tty_flag = TTY_BREAK;
+		else if (lsr & UART_LSR_PE)
+			tty_flag = TTY_PARITY;
+		else if (lsr & UART_LSR_FE)
+			tty_flag = TTY_FRAME;
+
+		/* overrun is special, not associated with a char */
+		if (lsr & UART_LSR_OE)
+			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+	}
+	tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
+							urb->actual_length);
+	tty_flip_buffer_push(&port->port);
+}
+
+static struct usb_serial_driver ark3116_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"ark3116",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_bulk_in =		1,
+	.num_bulk_out =		1,
+	.num_interrupt_in =	1,
+	.port_probe =		ark3116_port_probe,
+	.port_remove =		ark3116_port_remove,
+	.set_termios =		ark3116_set_termios,
+	.init_termios =		ark3116_init_termios,
+	.ioctl =		ark3116_ioctl,
+	.tiocmget =		ark3116_tiocmget,
+	.tiocmset =		ark3116_tiocmset,
+	.tiocmiwait =		usb_serial_generic_tiocmiwait,
+	.get_icount =		usb_serial_generic_get_icount,
+	.open =			ark3116_open,
+	.close =		ark3116_close,
+	.break_ctl = 		ark3116_break_ctl,
+	.read_int_callback = 	ark3116_read_int_callback,
+	.process_read_urb =	ark3116_process_read_urb,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ark3116_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+/*
+ * The following describes what I learned from studying the old
+ * ark3116.c driver, disassembling the windows driver, and some lucky
+ * guesses. Since I do not have any datasheet or other
+ * documentation, inaccuracies are almost guaranteed.
+ *
+ * Some specs for the ARK3116 can be found here:
+ * http://web.archive.org/web/20060318000438/
+ *   www.arkmicro.com/en/products/view.php?id=10
+ * On that page, 2 GPIO pins are mentioned: I assume these are the
+ * OUT1 and OUT2 pins of the UART, so I added support for those
+ * through the MCR. Since the pins are not available on my hardware,
+ * I could not verify this.
+ * Also, it states there is "on-chip hardware flow control". I have
+ * discovered how to enable that. Unfortunately, I do not know how to
+ * enable XON/XOFF (software) flow control, which would need support
+ * from the chip as well to work. Because of the wording on the web
+ * page there is a real possibility the chip simply does not support
+ * software flow control.
+ *
+ * I got my ark3116 as part of a mobile phone adapter cable. On the
+ * PCB, the following numbered contacts are present:
+ *
+ *  1:- +5V
+ *  2:o DTR
+ *  3:i RX
+ *  4:i DCD
+ *  5:o RTS
+ *  6:o TX
+ *  7:i RI
+ *  8:i DSR
+ * 10:- 0V
+ * 11:i CTS
+ *
+ * On my chip, all signals seem to be 3.3V, but 5V tolerant. But that
+ * may be different for the one you have ;-).
+ *
+ * The windows driver limits the registers to 0-F, so I assume there
+ * are actually 16 present on the device.
+ *
+ * On an UART interrupt, 4 bytes of data come in on the interrupt
+ * endpoint. The bytes are 0xe8 IIR LSR MSR.
+ *
+ * The baudrate seems to be generated from the 12MHz crystal, using
+ * 4-times subsampling. So quot=12e6/(4*baud). Also see description
+ * of register E.
+ *
+ * Registers 0-7:
+ * These seem to be the same as for a regular 16450. The FCR is set
+ * to UART_FCR_DMA_SELECT (0x8), I guess to enable transfers between
+ * the UART and the USB bridge/DMA engine.
+ *
+ * Register 8:
+ * By trial and error, I found out that bit 0 enables hardware CTS,
+ * stopping TX when CTS is +5V. Bit 1 does the same for RTS, making
+ * RTS +5V when the 3116 cannot transfer the data to the USB bus
+ * (verified by disabling the reading URB). Note that as far as I can
+ * tell, the windows driver does NOT use this, so there might be some
+ * hardware bug or something.
+ *
+ * According to a patch provided here
+ * (http://lkml.org/lkml/2009/7/26/56), the ARK3116 can also be used
+ * as an IrDA dongle. Since I do not have such a thing, I could not
+ * investigate that aspect. However, I can speculate ;-).
+ *
+ * - IrDA encodes data differently than RS232. Most likely, one of
+ *   the bits in registers 9..E enables the IR ENDEC (encoder/decoder).
+ * - Depending on the IR transceiver, the input and output need to be
+ *   inverted, so there are probably bits for that as well.
+ * - IrDA is half-duplex, so there should be a bit for selecting that.
+ *
+ * This still leaves at least two registers unaccounted for. Perhaps
+ * The chip can do XON/XOFF or CRC in HW?
+ *
+ * Register 9:
+ * Set to 0x00 for IrDA, when the baudrate is initialised.
+ *
+ * Register A:
+ * Set to 0x01 for IrDA, at init.
+ *
+ * Register B:
+ * Set to 0x01 for IrDA, 0x00 for RS232, at init.
+ *
+ * Register C:
+ * Set to 00 for IrDA, at init.
+ *
+ * Register D:
+ * Set to 0x41 for IrDA, at init.
+ *
+ * Register E:
+ * Somekind of baudrate override. The windows driver seems to set
+ * this to 0x00 for normal baudrates, 0x01 for 460800, 0x02 for 921600.
+ * Since 460800 and 921600 cannot be obtained by dividing 3MHz by an integer,
+ * it could be somekind of subdivisor thingy.
+ * However,it does not seem to do anything: selecting 921600 (divisor 3,
+ * reg E=2), still gets 1 MHz. I also checked if registers 9, C or F would
+ * work, but they don't.
+ *
+ * Register F: unknown
+ */
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
new file mode 100644
index 0000000..c1235d5
--- /dev/null
+++ b/drivers/usb/serial/belkin_sa.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Belkin USB Serial Adapter Driver
+ *
+ *  Copyright (C) 2000		William Greathouse (wgreathouse@smva.com)
+ *  Copyright (C) 2000-2001	Greg Kroah-Hartman (greg@kroah.com)
+ *  Copyright (C) 2010		Johan Hovold (jhovold@gmail.com)
+ *
+ *  This program is largely derived from work by the linux-usb group
+ *  and associated source files.  Please see the usb/serial files for
+ *  individual credits and copyrights.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * TODO:
+ * -- Add true modem control line query capability.  Currently we track the
+ *    states reported by the interrupt and the states we request.
+ * -- Add support for flush commands
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "belkin_sa.h"
+
+#define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>"
+#define DRIVER_DESC "USB Belkin Serial converter driver"
+
+/* function prototypes for a Belkin USB Serial Adapter F5U103 */
+static int belkin_sa_port_probe(struct usb_serial_port *port);
+static int belkin_sa_port_remove(struct usb_serial_port *port);
+static int  belkin_sa_open(struct tty_struct *tty,
+			struct usb_serial_port *port);
+static void belkin_sa_close(struct usb_serial_port *port);
+static void belkin_sa_read_int_callback(struct urb *urb);
+static void belkin_sa_process_read_urb(struct urb *urb);
+static void belkin_sa_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios * old);
+static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
+static int  belkin_sa_tiocmget(struct tty_struct *tty);
+static int  belkin_sa_tiocmset(struct tty_struct *tty,
+					unsigned int set, unsigned int clear);
+
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(BELKIN_SA_VID, BELKIN_SA_PID) },
+	{ USB_DEVICE(BELKIN_OLD_VID, BELKIN_OLD_PID) },
+	{ USB_DEVICE(PERACOM_VID, PERACOM_PID) },
+	{ USB_DEVICE(GOHUBS_VID, GOHUBS_PID) },
+	{ USB_DEVICE(GOHUBS_VID, HANDYLINK_PID) },
+	{ USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) },
+	{ }	/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* All of the device info needed for the serial converters */
+static struct usb_serial_driver belkin_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"belkin",
+	},
+	.description =		"Belkin / Peracom / GoHubs USB Serial Adapter",
+	.id_table =		id_table,
+	.num_ports =		1,
+	.open =			belkin_sa_open,
+	.close =		belkin_sa_close,
+	.read_int_callback =	belkin_sa_read_int_callback,
+	.process_read_urb =	belkin_sa_process_read_urb,
+	.set_termios =		belkin_sa_set_termios,
+	.break_ctl =		belkin_sa_break_ctl,
+	.tiocmget =		belkin_sa_tiocmget,
+	.tiocmset =		belkin_sa_tiocmset,
+	.port_probe =		belkin_sa_port_probe,
+	.port_remove =		belkin_sa_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&belkin_device, NULL
+};
+
+struct belkin_sa_private {
+	spinlock_t		lock;
+	unsigned long		control_state;
+	unsigned char		last_lsr;
+	unsigned char		last_msr;
+	int			bad_flow_control;
+};
+
+
+/*
+ * ***************************************************************************
+ * Belkin USB Serial Adapter F5U103 specific driver functions
+ * ***************************************************************************
+ */
+
+#define WDR_TIMEOUT 5000 /* default urb timeout */
+
+/* assumes that struct usb_serial *serial is available */
+#define BSA_USB_CMD(c, v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \
+					    (c), BELKIN_SA_SET_REQUEST_TYPE, \
+					    (v), 0, NULL, 0, WDR_TIMEOUT)
+
+static int belkin_sa_port_probe(struct usb_serial_port *port)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct belkin_sa_private *priv;
+
+	priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->control_state = 0;
+	priv->last_lsr = 0;
+	priv->last_msr = 0;
+	/* see comments at top of file */
+	priv->bad_flow_control =
+		(le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0;
+	dev_info(&dev->dev, "bcdDevice: %04x, bfc: %d\n",
+					le16_to_cpu(dev->descriptor.bcdDevice),
+					priv->bad_flow_control);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int belkin_sa_port_remove(struct usb_serial_port *port)
+{
+	struct belkin_sa_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int belkin_sa_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	int retval;
+
+	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
+		return retval;
+	}
+
+	retval = usb_serial_generic_open(tty, port);
+	if (retval)
+		usb_kill_urb(port->interrupt_in_urb);
+
+	return retval;
+}
+
+static void belkin_sa_close(struct usb_serial_port *port)
+{
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static void belkin_sa_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct belkin_sa_private *priv;
+	unsigned char *data = urb->transfer_buffer;
+	int retval;
+	int status = urb->status;
+	unsigned long flags;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	/* Handle known interrupt data */
+	/* ignore data[0] and data[1] */
+
+	priv = usb_get_serial_port_data(port);
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->last_msr = data[BELKIN_SA_MSR_INDEX];
+
+	/* Record Control Line states */
+	if (priv->last_msr & BELKIN_SA_MSR_DSR)
+		priv->control_state |= TIOCM_DSR;
+	else
+		priv->control_state &= ~TIOCM_DSR;
+
+	if (priv->last_msr & BELKIN_SA_MSR_CTS)
+		priv->control_state |= TIOCM_CTS;
+	else
+		priv->control_state &= ~TIOCM_CTS;
+
+	if (priv->last_msr & BELKIN_SA_MSR_RI)
+		priv->control_state |= TIOCM_RI;
+	else
+		priv->control_state &= ~TIOCM_RI;
+
+	if (priv->last_msr & BELKIN_SA_MSR_CD)
+		priv->control_state |= TIOCM_CD;
+	else
+		priv->control_state &= ~TIOCM_CD;
+
+	priv->last_lsr = data[BELKIN_SA_LSR_INDEX];
+	spin_unlock_irqrestore(&priv->lock, flags);
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&port->dev, "%s - usb_submit_urb failed with "
+			"result %d\n", __func__, retval);
+}
+
+static void belkin_sa_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	unsigned char status;
+	char tty_flag;
+
+	/* Update line status */
+	tty_flag = TTY_NORMAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	status = priv->last_lsr;
+	priv->last_lsr &= ~BELKIN_SA_LSR_ERR;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (!urb->actual_length)
+		return;
+
+	if (status & BELKIN_SA_LSR_ERR) {
+		/* Break takes precedence over parity, which takes precedence
+		 * over framing errors. */
+		if (status & BELKIN_SA_LSR_BI)
+			tty_flag = TTY_BREAK;
+		else if (status & BELKIN_SA_LSR_PE)
+			tty_flag = TTY_PARITY;
+		else if (status & BELKIN_SA_LSR_FE)
+			tty_flag = TTY_FRAME;
+		dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag);
+
+		/* Overrun is special, not associated with a char. */
+		if (status & BELKIN_SA_LSR_OE)
+			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+	}
+
+	tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
+							urb->actual_length);
+	tty_flip_buffer_push(&port->port);
+}
+
+static void belkin_sa_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
+	unsigned int iflag;
+	unsigned int cflag;
+	unsigned int old_iflag = 0;
+	unsigned int old_cflag = 0;
+	__u16 urb_value = 0; /* Will hold the new flags */
+	unsigned long flags;
+	unsigned long control_state;
+	int bad_flow_control;
+	speed_t baud;
+	struct ktermios *termios = &tty->termios;
+
+	iflag = termios->c_iflag;
+	cflag = termios->c_cflag;
+
+	termios->c_cflag &= ~CMSPAR;
+
+	/* get a local copy of the current port settings */
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+	bad_flow_control = priv->bad_flow_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	old_iflag = old_termios->c_iflag;
+	old_cflag = old_termios->c_cflag;
+
+	/* Set the baud rate */
+	if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
+		/* reassert DTR and (maybe) RTS on transition from B0 */
+		if ((old_cflag & CBAUD) == B0) {
+			control_state |= (TIOCM_DTR|TIOCM_RTS);
+			if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0)
+				dev_err(&port->dev, "Set DTR error\n");
+			/* don't set RTS if using hardware flow control */
+			if (!(old_cflag & CRTSCTS))
+				if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST
+								, 1) < 0)
+					dev_err(&port->dev, "Set RTS error\n");
+		}
+	}
+
+	baud = tty_get_baud_rate(tty);
+	if (baud) {
+		urb_value = BELKIN_SA_BAUD(baud);
+		/* Clip to maximum speed */
+		if (urb_value == 0)
+			urb_value = 1;
+		/* Turn it back into a resulting real baud rate */
+		baud = BELKIN_SA_BAUD(urb_value);
+
+		/* Report the actual baud rate back to the caller */
+		tty_encode_baud_rate(tty, baud, baud);
+		if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
+			dev_err(&port->dev, "Set baudrate error\n");
+	} else {
+		/* Disable flow control */
+		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST,
+						BELKIN_SA_FLOW_NONE) < 0)
+			dev_err(&port->dev, "Disable flowcontrol error\n");
+		/* Drop RTS and DTR */
+		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
+			dev_err(&port->dev, "DTR LOW error\n");
+		if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
+			dev_err(&port->dev, "RTS LOW error\n");
+	}
+
+	/* set the parity */
+	if ((cflag ^ old_cflag) & (PARENB | PARODD)) {
+		if (cflag & PARENB)
+			urb_value = (cflag & PARODD) ?  BELKIN_SA_PARITY_ODD
+						: BELKIN_SA_PARITY_EVEN;
+		else
+			urb_value = BELKIN_SA_PARITY_NONE;
+		if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0)
+			dev_err(&port->dev, "Set parity error\n");
+	}
+
+	/* set the number of data bits */
+	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+		switch (cflag & CSIZE) {
+		case CS5:
+			urb_value = BELKIN_SA_DATA_BITS(5);
+			break;
+		case CS6:
+			urb_value = BELKIN_SA_DATA_BITS(6);
+			break;
+		case CS7:
+			urb_value = BELKIN_SA_DATA_BITS(7);
+			break;
+		case CS8:
+			urb_value = BELKIN_SA_DATA_BITS(8);
+			break;
+		default:
+			dev_dbg(&port->dev,
+				"CSIZE was not CS5-CS8, using default of 8\n");
+			urb_value = BELKIN_SA_DATA_BITS(8);
+			break;
+		}
+		if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0)
+			dev_err(&port->dev, "Set data bits error\n");
+	}
+
+	/* set the number of stop bits */
+	if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+		urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2)
+						: BELKIN_SA_STOP_BITS(1);
+		if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST,
+							urb_value) < 0)
+			dev_err(&port->dev, "Set stop bits error\n");
+	}
+
+	/* Set flow control */
+	if (((iflag ^ old_iflag) & (IXOFF | IXON)) ||
+		((cflag ^ old_cflag) & CRTSCTS)) {
+		urb_value = 0;
+		if ((iflag & IXOFF) || (iflag & IXON))
+			urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);
+		else
+			urb_value &= ~(BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);
+
+		if (cflag & CRTSCTS)
+			urb_value |=  (BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);
+		else
+			urb_value &= ~(BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);
+
+		if (bad_flow_control)
+			urb_value &= ~(BELKIN_SA_FLOW_IRTS);
+
+		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0)
+			dev_err(&port->dev, "Set flow control error\n");
+	}
+
+	/* save off the modified port settings */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->control_state = control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+
+	if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0)
+		dev_err(&port->dev, "Set break_ctl %d\n", break_state);
+}
+
+static int belkin_sa_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
+	unsigned long control_state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return control_state;
+}
+
+static int belkin_sa_tiocmset(struct tty_struct *tty,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
+	unsigned long control_state;
+	unsigned long flags;
+	int retval;
+	int rts = 0;
+	int dtr = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+
+	if (set & TIOCM_RTS) {
+		control_state |= TIOCM_RTS;
+		rts = 1;
+	}
+	if (set & TIOCM_DTR) {
+		control_state |= TIOCM_DTR;
+		dtr = 1;
+	}
+	if (clear & TIOCM_RTS) {
+		control_state &= ~TIOCM_RTS;
+		rts = 0;
+	}
+	if (clear & TIOCM_DTR) {
+		control_state &= ~TIOCM_DTR;
+		dtr = 0;
+	}
+
+	priv->control_state = control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts);
+	if (retval < 0) {
+		dev_err(&port->dev, "Set RTS error %d\n", retval);
+		goto exit;
+	}
+
+	retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr);
+	if (retval < 0) {
+		dev_err(&port->dev, "Set DTR error %d\n", retval);
+		goto exit;
+	}
+exit:
+	return retval;
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h
new file mode 100644
index 0000000..51bc062
--- /dev/null
+++ b/drivers/usb/serial/belkin_sa.h
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Definitions for Belkin USB Serial Adapter Driver
+ *
+ *  Copyright (C) 2000
+ *      William Greathouse (wgreathouse@smva.com)
+ *
+ *  This program is largely derived from work by the linux-usb group
+ *  and associated source files.  Please see the usb/serial files for
+ *  individual credits and copyrights.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * 12-Mar-2001 gkh
+ *	Added GoHubs GO-COM232 device id.
+ *
+ * 06-Nov-2000 gkh
+ *	Added old Belkin and Peracom device ids, which this driver supports
+ *
+ * 12-Oct-2000 William Greathouse
+ *    First cut at supporting Belkin USB Serial Adapter F5U103
+ *    I did not have a copy of the original work to support this
+ *    adapter, so pardon any stupid mistakes.  All of the information
+ *    I am using to write this driver was acquired by using a modified
+ *    UsbSnoop on Windows2000.
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_BSA_H
+#define __LINUX_USB_SERIAL_BSA_H
+
+#define BELKIN_DOCKSTATION_VID	0x050d	/* Vendor Id */
+#define BELKIN_DOCKSTATION_PID	0x1203	/* Product Id */
+
+#define BELKIN_SA_VID	0x050d	/* Vendor Id */
+#define BELKIN_SA_PID	0x0103	/* Product Id */
+
+#define BELKIN_OLD_VID	0x056c	/* Belkin's "old" vendor id */
+#define BELKIN_OLD_PID	0x8007	/* Belkin's "old" single port serial converter's id */
+
+#define PERACOM_VID	0x0565	/* Peracom's vendor id */
+#define PERACOM_PID	0x0001	/* Peracom's single port serial converter's id */
+
+#define GOHUBS_VID	0x0921	/* GoHubs vendor id */
+#define GOHUBS_PID	0x1000	/* GoHubs single port serial converter's id (identical to the Peracom device) */
+#define HANDYLINK_PID	0x1200	/* HandyLink USB's id (identical to the Peracom device) */
+
+/* Vendor Request Interface */
+#define BELKIN_SA_SET_BAUDRATE_REQUEST	0  /* Set baud rate */
+#define BELKIN_SA_SET_STOP_BITS_REQUEST	1  /* Set stop bits (1,2) */
+#define BELKIN_SA_SET_DATA_BITS_REQUEST	2  /* Set data bits (5,6,7,8) */
+#define BELKIN_SA_SET_PARITY_REQUEST	3  /* Set parity (None, Even, Odd) */
+
+#define BELKIN_SA_SET_DTR_REQUEST	10 /* Set DTR state */
+#define BELKIN_SA_SET_RTS_REQUEST	11 /* Set RTS state */
+#define BELKIN_SA_SET_BREAK_REQUEST	12 /* Set BREAK state */
+
+#define BELKIN_SA_SET_FLOW_CTRL_REQUEST	16 /* Set flow control mode */
+
+
+#ifdef WHEN_I_LEARN_THIS
+#define BELKIN_SA_SET_MAGIC_REQUEST	17 /* I don't know, possibly flush */
+					   /* (always in Wininit sequence before flow control) */
+#define BELKIN_SA_RESET			xx /* Reset the port */
+#define BELKIN_SA_GET_MODEM_STATUS	xx /* Force return of modem status register */
+#endif
+
+#define BELKIN_SA_SET_REQUEST_TYPE	0x40
+
+#define BELKIN_SA_BAUD(b)		(230400/b)
+
+#define BELKIN_SA_STOP_BITS(b)		(b-1)
+
+#define BELKIN_SA_DATA_BITS(b)		(b-5)
+
+#define BELKIN_SA_PARITY_NONE		0
+#define BELKIN_SA_PARITY_EVEN		1
+#define BELKIN_SA_PARITY_ODD		2
+#define BELKIN_SA_PARITY_MARK		3
+#define BELKIN_SA_PARITY_SPACE		4
+
+#define BELKIN_SA_FLOW_NONE		0x0000	/* No flow control */
+#define BELKIN_SA_FLOW_OCTS		0x0001	/* use CTS input to throttle output */
+#define BELKIN_SA_FLOW_ODSR		0x0002	/* use DSR input to throttle output */
+#define BELKIN_SA_FLOW_IDSR		0x0004	/* use DSR input to enable receive */
+#define BELKIN_SA_FLOW_IDTR		0x0008	/* use DTR output for input flow control */
+#define BELKIN_SA_FLOW_IRTS		0x0010	/* use RTS output for input flow control */
+#define BELKIN_SA_FLOW_ORTS		0x0020	/* use RTS to indicate data available to send */
+#define BELKIN_SA_FLOW_ERRSUB		0x0040	/* ???? guess ???? substitute inline errors */
+#define BELKIN_SA_FLOW_OXON		0x0080	/* use XON/XOFF for output flow control */
+#define BELKIN_SA_FLOW_IXON		0x0100	/* use XON/XOFF for input flow control */
+
+/*
+ * It seems that the interrupt pipe is closely modelled after the
+ * 16550 register layout.  This is probably because the adapter can
+ * be used in a "DOS" environment to simulate a standard hardware port.
+ */
+#define BELKIN_SA_LSR_INDEX		2	/*     Line Status Register */
+#define BELKIN_SA_LSR_RDR		0x01	/* receive data ready */
+#define BELKIN_SA_LSR_OE		0x02	/* overrun error */
+#define BELKIN_SA_LSR_PE		0x04	/* parity error */
+#define BELKIN_SA_LSR_FE		0x08	/* framing error */
+#define BELKIN_SA_LSR_BI		0x10	/* break indicator */
+#define BELKIN_SA_LSR_THE		0x20	/* tx holding register empty */
+#define BELKIN_SA_LSR_TE		0x40	/* transmit register empty */
+#define BELKIN_SA_LSR_ERR		0x80	/* OE | PE | FE | BI */
+
+#define BELKIN_SA_MSR_INDEX		3	/*     Modem Status Register */
+#define BELKIN_SA_MSR_DCTS		0x01	/* Delta CTS */
+#define BELKIN_SA_MSR_DDSR		0x02	/* Delta DSR */
+#define BELKIN_SA_MSR_DRI		0x04	/* Delta RI */
+#define BELKIN_SA_MSR_DCD		0x08	/* Delta CD */
+#define BELKIN_SA_MSR_CTS		0x10	/* Current CTS */
+#define BELKIN_SA_MSR_DSR		0x20	/* Current DSR */
+#define BELKIN_SA_MSR_RI		0x40	/* Current RI */
+#define BELKIN_SA_MSR_CD		0x80	/* Current CD */
+
+#endif /* __LINUX_USB_SERIAL_BSA_H */
+
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
new file mode 100644
index 0000000..eb0195c
--- /dev/null
+++ b/drivers/usb/serial/bus.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Serial Converter Bus specific functions
+ *
+ * Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static int usb_serial_device_match(struct device *dev,
+						struct device_driver *drv)
+{
+	struct usb_serial_driver *driver;
+	const struct usb_serial_port *port;
+
+	/*
+	 * drivers are already assigned to ports in serial_probe so it's
+	 * a simple check here.
+	 */
+	port = to_usb_serial_port(dev);
+	if (!port)
+		return 0;
+
+	driver = to_usb_serial_driver(drv);
+
+	if (driver == port->serial->type)
+		return 1;
+
+	return 0;
+}
+
+static int usb_serial_device_probe(struct device *dev)
+{
+	struct usb_serial_driver *driver;
+	struct usb_serial_port *port;
+	struct device *tty_dev;
+	int retval = 0;
+	int minor;
+
+	port = to_usb_serial_port(dev);
+	if (!port)
+		return -ENODEV;
+
+	/* make sure suspend/resume doesn't race against port_probe */
+	retval = usb_autopm_get_interface(port->serial->interface);
+	if (retval)
+		return retval;
+
+	driver = port->serial->type;
+	if (driver->port_probe) {
+		retval = driver->port_probe(port);
+		if (retval)
+			goto err_autopm_put;
+	}
+
+	minor = port->minor;
+	tty_dev = tty_port_register_device(&port->port, usb_serial_tty_driver,
+					   minor, dev);
+	if (IS_ERR(tty_dev)) {
+		retval = PTR_ERR(tty_dev);
+		goto err_port_remove;
+	}
+
+	usb_autopm_put_interface(port->serial->interface);
+
+	dev_info(&port->serial->dev->dev,
+		 "%s converter now attached to ttyUSB%d\n",
+		 driver->description, minor);
+
+	return 0;
+
+err_port_remove:
+	if (driver->port_remove)
+		driver->port_remove(port);
+err_autopm_put:
+	usb_autopm_put_interface(port->serial->interface);
+
+	return retval;
+}
+
+static int usb_serial_device_remove(struct device *dev)
+{
+	struct usb_serial_driver *driver;
+	struct usb_serial_port *port;
+	int retval = 0;
+	int minor;
+	int autopm_err;
+
+	port = to_usb_serial_port(dev);
+	if (!port)
+		return -ENODEV;
+
+	/*
+	 * Make sure suspend/resume doesn't race against port_remove.
+	 *
+	 * Note that no further runtime PM callbacks will be made if
+	 * autopm_get fails.
+	 */
+	autopm_err = usb_autopm_get_interface(port->serial->interface);
+
+	minor = port->minor;
+	tty_unregister_device(usb_serial_tty_driver, minor);
+
+	driver = port->serial->type;
+	if (driver->port_remove)
+		retval = driver->port_remove(port);
+
+	dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
+		 driver->description, minor);
+
+	if (!autopm_err)
+		usb_autopm_put_interface(port->serial->interface);
+
+	return retval;
+}
+
+static ssize_t new_id_store(struct device_driver *driver,
+			    const char *buf, size_t count)
+{
+	struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
+	ssize_t retval = usb_store_new_id(&usb_drv->dynids, usb_drv->id_table,
+					 driver, buf, count);
+
+	if (retval >= 0 && usb_drv->usb_driver != NULL)
+		retval = usb_store_new_id(&usb_drv->usb_driver->dynids,
+					  usb_drv->usb_driver->id_table,
+					  &usb_drv->usb_driver->drvwrap.driver,
+					  buf, count);
+	return retval;
+}
+
+static ssize_t new_id_show(struct device_driver *driver, char *buf)
+{
+	struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
+
+	return usb_show_dynids(&usb_drv->dynids, buf);
+}
+static DRIVER_ATTR_RW(new_id);
+
+static struct attribute *usb_serial_drv_attrs[] = {
+	&driver_attr_new_id.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(usb_serial_drv);
+
+static void free_dynids(struct usb_serial_driver *drv)
+{
+	struct usb_dynid *dynid, *n;
+
+	spin_lock(&drv->dynids.lock);
+	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
+		list_del(&dynid->node);
+		kfree(dynid);
+	}
+	spin_unlock(&drv->dynids.lock);
+}
+
+struct bus_type usb_serial_bus_type = {
+	.name =		"usb-serial",
+	.match =	usb_serial_device_match,
+	.probe =	usb_serial_device_probe,
+	.remove =	usb_serial_device_remove,
+	.drv_groups = 	usb_serial_drv_groups,
+};
+
+int usb_serial_bus_register(struct usb_serial_driver *driver)
+{
+	int retval;
+
+	driver->driver.bus = &usb_serial_bus_type;
+	spin_lock_init(&driver->dynids.lock);
+	INIT_LIST_HEAD(&driver->dynids.list);
+
+	retval = driver_register(&driver->driver);
+
+	return retval;
+}
+
+void usb_serial_bus_deregister(struct usb_serial_driver *driver)
+{
+	free_dynids(driver);
+	driver_unregister(&driver->driver);
+}
+
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
new file mode 100644
index 0000000..3bb1fff
--- /dev/null
+++ b/drivers/usb/serial/ch341.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
+ * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
+ * Copyright 2009, Boris Hajduk <boris@hajduk.org>
+ *
+ * ch341.c implements a serial port driver for the Winchiphead CH341.
+ *
+ * The CH341 device can be used to implement an RS232 asynchronous
+ * serial port, an IEEE-1284 parallel printer port or a memory-like
+ * interface. In all cases the CH341 supports an I2C interface as well.
+ * This driver only supports the asynchronous serial interface.
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <asm/unaligned.h>
+
+#define DEFAULT_BAUD_RATE 9600
+#define DEFAULT_TIMEOUT   1000
+
+/* flags for IO-Bits */
+#define CH341_BIT_RTS (1 << 6)
+#define CH341_BIT_DTR (1 << 5)
+
+/******************************/
+/* interrupt pipe definitions */
+/******************************/
+/* always 4 interrupt bytes */
+/* first irq byte normally 0x08 */
+/* second irq byte base 0x7d + below */
+/* third irq byte base 0x94 + below */
+/* fourth irq byte normally 0xee */
+
+/* second interrupt byte */
+#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
+
+/* status returned in third interrupt answer byte, inverted in data
+   from irq */
+#define CH341_BIT_CTS 0x01
+#define CH341_BIT_DSR 0x02
+#define CH341_BIT_RI  0x04
+#define CH341_BIT_DCD 0x08
+#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
+
+/*******************************/
+/* baudrate calculation factor */
+/*******************************/
+#define CH341_BAUDBASE_FACTOR 1532620800
+#define CH341_BAUDBASE_DIVMAX 3
+
+/* Break support - the information used to implement this was gleaned from
+ * 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_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) },
+	{ USB_DEVICE(0x1a86, 0x7523) },
+	{ USB_DEVICE(0x1a86, 0x5523) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct ch341_private {
+	spinlock_t lock; /* access lock */
+	unsigned baud_rate; /* set baud rate */
+	u8 mcr;
+	u8 msr;
+	u8 lcr;
+};
+
+static void ch341_set_termios(struct tty_struct *tty,
+			      struct usb_serial_port *port,
+			      struct ktermios *old_termios);
+
+static int ch341_control_out(struct usb_device *dev, u8 request,
+			     u16 value, u16 index)
+{
+	int r;
+
+	dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__,
+		request, value, index);
+
+	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;
+}
+
+static int ch341_control_in(struct usb_device *dev,
+			    u8 request, u16 value, u16 index,
+			    char *buf, unsigned bufsize)
+{
+	int r;
+
+	dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__,
+		request, value, index, bufsize);
+
+	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);
+	if (r < (int)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_lcr(struct usb_device *dev,
+				  struct ch341_private *priv, u8 lcr)
+{
+	short a;
+	int r;
+	unsigned long factor;
+	short divisor;
+
+	if (!priv->baud_rate)
+		return -EINVAL;
+	factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
+	divisor = CH341_BAUDBASE_DIVMAX;
+
+	while ((factor > 0xfff0) && divisor) {
+		factor >>= 3;
+		divisor--;
+	}
+
+	if (factor > 0xfff0)
+		return -EINVAL;
+
+	factor = 0x10000 - factor;
+	a = (factor & 0xff00) | divisor;
+
+	/*
+	 * 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, 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;
+	unsigned long flags;
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
+	if (r < 0)
+		goto out;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+out:	kfree(buffer);
+	return r;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
+{
+	const unsigned int size = 2;
+	char *buffer;
+	int r;
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* expect two bytes 0x27 0x00 */
+	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, CH341_REQ_SERIAL_INIT, 0, 0);
+	if (r < 0)
+		goto out;
+
+	r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
+	if (r < 0)
+		goto out;
+
+	r = ch341_set_handshake(dev, priv->mcr);
+
+out:	kfree(buffer);
+	return r;
+}
+
+static int ch341_port_probe(struct usb_serial_port *port)
+{
+	struct ch341_private *priv;
+	int r;
+
+	priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->baud_rate = DEFAULT_BAUD_RATE;
+	/*
+	 * Some CH340 devices appear unable to change the initial LCR
+	 * settings, so set a sane 8N1 default.
+	 */
+	priv->lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8;
+
+	r = ch341_configure(port->serial->dev, priv);
+	if (r < 0)
+		goto error;
+
+	usb_set_serial_port_data(port, priv);
+	return 0;
+
+error:	kfree(priv);
+	return r;
+}
+
+static int ch341_port_remove(struct usb_serial_port *port)
+{
+	struct ch341_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int ch341_carrier_raised(struct usb_serial_port *port)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	if (priv->msr & CH341_BIT_DCD)
+		return 1;
+	return 0;
+}
+
+static void ch341_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	/* drop DTR and RTS */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (on)
+		priv->mcr |= CH341_BIT_RTS | CH341_BIT_DTR;
+	else
+		priv->mcr &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	ch341_set_handshake(port->serial->dev, priv->mcr);
+}
+
+static void ch341_close(struct usb_serial_port *port)
+{
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+
+/* open this device, set default parameters */
+static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	int r;
+
+	if (tty)
+		ch341_set_termios(tty, port, NULL);
+
+	dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
+	r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (r) {
+		dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
+			__func__, r);
+		return r;
+	}
+
+	r = ch341_get_status(port->serial->dev, priv);
+	if (r < 0) {
+		dev_err(&port->dev, "failed to read modem status: %d\n", r);
+		goto err_kill_interrupt_urb;
+	}
+
+	r = usb_serial_generic_open(tty, port);
+	if (r)
+		goto err_kill_interrupt_urb;
+
+	return 0;
+
+err_kill_interrupt_urb:
+	usb_kill_urb(port->interrupt_in_urb);
+
+	return r;
+}
+
+/* Old_termios contains the original termios settings and
+ * tty->termios contains the new setting to be used.
+ */
+static void ch341_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned baud_rate;
+	unsigned long flags;
+	u8 lcr;
+	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);
+
+	lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
+
+	switch (C_CSIZE(tty)) {
+	case CS5:
+		lcr |= CH341_LCR_CS5;
+		break;
+	case CS6:
+		lcr |= CH341_LCR_CS6;
+		break;
+	case CS7:
+		lcr |= CH341_LCR_CS7;
+		break;
+	case CS8:
+		lcr |= CH341_LCR_CS8;
+		break;
+	}
+
+	if (C_PARENB(tty)) {
+		lcr |= CH341_LCR_ENABLE_PAR;
+		if (C_PARODD(tty) == 0)
+			lcr |= CH341_LCR_PAR_EVEN;
+		if (C_CMSPAR(tty))
+			lcr |= CH341_LCR_MARK_SPACE;
+	}
+
+	if (C_CSTOPB(tty))
+		lcr |= CH341_LCR_STOP_BITS_2;
+
+	if (baud_rate) {
+		priv->baud_rate = baud_rate;
+
+		r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
+		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 = lcr;
+		}
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (C_BAUD(tty) == B0)
+		priv->mcr &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+		priv->mcr |= (CH341_BIT_DTR | CH341_BIT_RTS);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	ch341_set_handshake(port->serial->dev, priv->mcr);
+}
+
+static void ch341_break_ctl(struct tty_struct *tty, int break_state)
+{
+	const uint16_t ch341_break_reg =
+			((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
+	struct usb_serial_port *port = tty->driver_data;
+	int r;
+	uint16_t reg_contents;
+	uint8_t *break_reg;
+
+	break_reg = kmalloc(2, GFP_KERNEL);
+	if (!break_reg)
+		return;
+
+	r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
+			ch341_break_reg, 0, break_reg, 2);
+	if (r < 0) {
+		dev_err(&port->dev, "%s - USB control read error (%d)\n",
+				__func__, r);
+		goto out;
+	}
+	dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
+		__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;
+		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;
+		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]);
+	reg_contents = get_unaligned_le16(break_reg);
+	r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
+			ch341_break_reg, reg_contents);
+	if (r < 0)
+		dev_err(&port->dev, "%s - USB control write error (%d)\n",
+				__func__, r);
+out:
+	kfree(break_reg);
+}
+
+static int ch341_tiocmset(struct tty_struct *tty,
+			  unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (set & TIOCM_RTS)
+		priv->mcr |= CH341_BIT_RTS;
+	if (set & TIOCM_DTR)
+		priv->mcr |= CH341_BIT_DTR;
+	if (clear & TIOCM_RTS)
+		priv->mcr &= ~CH341_BIT_RTS;
+	if (clear & TIOCM_DTR)
+		priv->mcr &= ~CH341_BIT_DTR;
+	control = priv->mcr;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return ch341_set_handshake(port->serial->dev, control);
+}
+
+static void ch341_update_status(struct usb_serial_port *port,
+					unsigned char *data, size_t len)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	unsigned long flags;
+	u8 status;
+	u8 delta;
+
+	if (len < 4)
+		return;
+
+	status = ~data[2] & CH341_BITS_MODEM_STAT;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	delta = status ^ priv->msr;
+	priv->msr = status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (data[1] & CH341_MULT_STAT)
+		dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
+
+	if (!delta)
+		return;
+
+	if (delta & CH341_BIT_CTS)
+		port->icount.cts++;
+	if (delta & CH341_BIT_DSR)
+		port->icount.dsr++;
+	if (delta & CH341_BIT_RI)
+		port->icount.rng++;
+	if (delta & CH341_BIT_DCD) {
+		port->icount.dcd++;
+		tty = tty_port_tty_get(&port->port);
+		if (tty) {
+			usb_serial_handle_dcd_change(port, tty,
+						status & CH341_BIT_DCD);
+			tty_kref_put(tty);
+		}
+	}
+
+	wake_up_interruptible(&port->port.delta_msr_wait);
+}
+
+static void ch341_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int len = urb->actual_length;
+	int status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, len, data);
+	ch341_update_status(port, data, len);
+exit:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
+			__func__, status);
+	}
+}
+
+static int ch341_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 mcr;
+	u8 status;
+	unsigned int result;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	mcr = priv->mcr;
+	status = priv->msr;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	result = ((mcr & CH341_BIT_DTR)		? TIOCM_DTR : 0)
+		  | ((mcr & CH341_BIT_RTS)	? TIOCM_RTS : 0)
+		  | ((status & CH341_BIT_CTS)	? TIOCM_CTS : 0)
+		  | ((status & CH341_BIT_DSR)	? TIOCM_DSR : 0)
+		  | ((status & CH341_BIT_RI)	? TIOCM_RI  : 0)
+		  | ((status & CH341_BIT_DCD)	? TIOCM_CD  : 0);
+
+	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
+
+	return result;
+}
+
+static int ch341_reset_resume(struct usb_serial *serial)
+{
+	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);
+
+	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;
+		}
+
+		ret = ch341_get_status(port->serial->dev, priv);
+		if (ret < 0) {
+			dev_err(&port->dev, "failed to read modem status: %d\n",
+				ret);
+		}
+	}
+
+	return usb_serial_generic_resume(serial);
+}
+
+static struct usb_serial_driver ch341_device = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "ch341-uart",
+	},
+	.id_table          = id_table,
+	.num_ports         = 1,
+	.open              = ch341_open,
+	.dtr_rts	   = ch341_dtr_rts,
+	.carrier_raised	   = ch341_carrier_raised,
+	.close             = ch341_close,
+	.set_termios       = ch341_set_termios,
+	.break_ctl         = ch341_break_ctl,
+	.tiocmget          = ch341_tiocmget,
+	.tiocmset          = ch341_tiocmset,
+	.tiocmiwait        = usb_serial_generic_tiocmiwait,
+	.read_int_callback = ch341_read_int_callback,
+	.port_probe        = ch341_port_probe,
+	.port_remove       = ch341_port_remove,
+	.reset_resume      = ch341_reset_resume,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ch341_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
new file mode 100644
index 0000000..7d28930
--- /dev/null
+++ b/drivers/usb/serial/console.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Serial Console driver
+ *
+ * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * Thanks to Randy Dunlap for the original version of this code.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+struct usbcons_info {
+	int			magic;
+	int			break_flag;
+	struct usb_serial_port	*port;
+};
+
+static struct usbcons_info usbcons_info;
+static struct console usbcons;
+
+/*
+ * ------------------------------------------------------------
+ * USB Serial console driver
+ *
+ * Much of the code here is copied from drivers/char/serial.c
+ * and implements a phony serial console in the same way that
+ * serial.c does so that in case some software queries it,
+ * it will get the same results.
+ *
+ * Things that are different from the way the serial port code
+ * does things, is that we call the lower level usb-serial
+ * driver code to initialize the device, and we set the initial
+ * console speeds based on the command line arguments.
+ * ------------------------------------------------------------
+ */
+
+static const struct tty_operations usb_console_fake_tty_ops = {
+};
+
+/*
+ * The parsing of the command line works exactly like the
+ * serial.c code, except that the specifier is "ttyUSB" instead
+ * of "ttyS".
+ */
+static int usb_console_setup(struct console *co, char *options)
+{
+	struct usbcons_info *info = &usbcons_info;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int doflow = 0;
+	int cflag = CREAD | HUPCL | CLOCAL;
+	char *s;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int retval;
+	struct tty_struct *tty = NULL;
+	struct ktermios dummy;
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while (*s >= '0' && *s <= '9')
+			s++;
+		if (*s)
+			parity = *s++;
+		if (*s)
+			bits   = *s++ - '0';
+		if (*s)
+			doflow = (*s++ == 'r');
+	}
+	
+	/* Sane default */
+	if (baud == 0)
+		baud = 9600;
+
+	switch (bits) {
+	case 7:
+		cflag |= CS7;
+		break;
+	default:
+	case 8:
+		cflag |= CS8;
+		break;
+	}
+	switch (parity) {
+	case 'o': case 'O':
+		cflag |= PARODD;
+		break;
+	case 'e': case 'E':
+		cflag |= PARENB;
+		break;
+	}
+
+	/*
+	 * no need to check the index here: if the index is wrong, console
+	 * code won't call us
+	 */
+	port = usb_serial_port_get_by_minor(co->index);
+	if (port == NULL) {
+		/* no device is connected yet, sorry :( */
+		pr_err("No USB device connected to ttyUSB%i\n", co->index);
+		return -ENODEV;
+	}
+	serial = port->serial;
+
+	retval = usb_autopm_get_interface(serial->interface);
+	if (retval)
+		goto error_get_interface;
+
+	tty_port_tty_set(&port->port, NULL);
+
+	info->port = port;
+
+	++port->port.count;
+	if (!tty_port_initialized(&port->port)) {
+		if (serial->type->set_termios) {
+			/*
+			 * allocate a fake tty so the driver can initialize
+			 * the termios structure, then later call set_termios to
+			 * configure according to command line arguments
+			 */
+			tty = kzalloc(sizeof(*tty), GFP_KERNEL);
+			if (!tty) {
+				retval = -ENOMEM;
+				goto reset_open_count;
+			}
+			kref_init(&tty->kref);
+			tty->driver = usb_serial_tty_driver;
+			tty->index = co->index;
+			init_ldsem(&tty->ldisc_sem);
+			spin_lock_init(&tty->files_lock);
+			INIT_LIST_HEAD(&tty->tty_files);
+			kref_get(&tty->driver->kref);
+			__module_get(tty->driver->owner);
+			tty->ops = &usb_console_fake_tty_ops;
+			tty_init_termios(tty);
+			tty_port_tty_set(&port->port, tty);
+		}
+
+		/* only call the device specific open if this
+		 * is the first time the port is opened */
+		retval = serial->type->open(NULL, port);
+		if (retval) {
+			dev_err(&port->dev, "could not open USB console port\n");
+			goto fail;
+		}
+
+		if (serial->type->set_termios) {
+			tty->termios.c_cflag = cflag;
+			tty_termios_encode_baud_rate(&tty->termios, baud, baud);
+			memset(&dummy, 0, sizeof(struct ktermios));
+			serial->type->set_termios(tty, port, &dummy);
+
+			tty_port_tty_set(&port->port, NULL);
+			tty_save_termios(tty);
+			tty_kref_put(tty);
+		}
+		tty_port_set_initialized(&port->port, 1);
+	}
+	/* Now that any required fake tty operations are completed restore
+	 * the tty port count */
+	--port->port.count;
+	/* The console is special in terms of closing the device so
+	 * indicate this port is now acting as a system console. */
+	port->port.console = 1;
+
+	mutex_unlock(&serial->disc_mutex);
+	return retval;
+
+ fail:
+	tty_port_tty_set(&port->port, NULL);
+	tty_kref_put(tty);
+ reset_open_count:
+	port->port.count = 0;
+	info->port = NULL;
+	usb_autopm_put_interface(serial->interface);
+ error_get_interface:
+	usb_serial_put(serial);
+	mutex_unlock(&serial->disc_mutex);
+	return retval;
+}
+
+static void usb_console_write(struct console *co,
+					const char *buf, unsigned count)
+{
+	static struct usbcons_info *info = &usbcons_info;
+	struct usb_serial_port *port = info->port;
+	struct usb_serial *serial;
+	int retval = -ENODEV;
+
+	if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
+		return;
+	serial = port->serial;
+
+	if (count == 0)
+		return;
+
+	dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count);
+
+	if (!port->port.console) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	while (count) {
+		unsigned int i;
+		unsigned int lf;
+		/* search for LF so we can insert CR if necessary */
+		for (i = 0, lf = 0 ; i < count ; i++) {
+			if (*(buf + i) == 10) {
+				lf = 1;
+				i++;
+				break;
+			}
+		}
+		/* pass on to the driver specific version of this function if
+		   it is available */
+		retval = serial->type->write(NULL, port, buf, i);
+		dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval);
+		if (lf) {
+			/* append CR after LF */
+			unsigned char cr = 13;
+			retval = serial->type->write(NULL, port, &cr, 1);
+			dev_dbg(&port->dev, "%s - write cr: %d\n",
+							__func__, retval);
+		}
+		buf += i;
+		count -= i;
+	}
+}
+
+static struct tty_driver *usb_console_device(struct console *co, int *index)
+{
+	struct tty_driver **p = (struct tty_driver **)co->data;
+
+	if (!*p)
+		return NULL;
+
+	*index = co->index;
+	return *p;
+}
+
+static struct console usbcons = {
+	.name =		"ttyUSB",
+	.write =	usb_console_write,
+	.device =	usb_console_device,
+	.setup =	usb_console_setup,
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+	.data = 	&usb_serial_tty_driver,
+};
+
+void usb_serial_console_disconnect(struct usb_serial *serial)
+{
+	if (serial->port[0] && serial->port[0] == usbcons_info.port) {
+		usb_serial_console_exit();
+		usb_serial_put(serial);
+	}
+}
+
+void usb_serial_console_init(int minor)
+{
+	if (minor == 0) {
+		/*
+		 * Call register_console() if this is the first device plugged
+		 * in.  If we call it earlier, then the callback to
+		 * console_setup() will fail, as there is not a device seen by
+		 * the USB subsystem yet.
+		 */
+		/*
+		 * Register console.
+		 * NOTES:
+		 * console_setup() is called (back) immediately (from
+		 * register_console). console_write() is called immediately
+		 * from register_console iff CON_PRINTBUFFER is set in flags.
+		 */
+		pr_debug("registering the USB serial console.\n");
+		register_console(&usbcons);
+	}
+}
+
+void usb_serial_console_exit(void)
+{
+	if (usbcons_info.port) {
+		unregister_console(&usbcons);
+		usbcons_info.port->port.console = 0;
+		usbcons_info.port = NULL;
+	}
+}
+
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
new file mode 100644
index 0000000..c0777a3
--- /dev/null
+++ b/drivers/usb/serial/cp210x.c
@@ -0,0 +1,1807 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
+ *
+ * Support to set flow control line levels using TIOCMGET and TIOCMSET
+ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
+ * control thanks to Munir Nassar nassarmu@real-time.com
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#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"
+
+/*
+ * Function Prototypes
+ */
+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 *,
+							struct ktermios*);
+static bool cp210x_tx_empty(struct usb_serial_port *port);
+static int cp210x_tiocmget(struct tty_struct *);
+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);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
+	{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
+	{ USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+	{ USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+	{ USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */
+	{ USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */
+	{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
+	{ USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
+	{ USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */
+	{ USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
+	{ USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */
+	{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
+	{ USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
+	{ USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
+	{ USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */
+	{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
+	{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+	{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+	{ USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */
+	{ USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */
+	{ USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */
+	{ USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */
+	{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+	{ USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */
+	{ USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */
+	{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
+	{ USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */
+	{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
+	{ USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */
+	{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+	{ USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */
+	{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+	{ USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
+	{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+	{ USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */
+	{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+	{ USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */
+	{ USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
+	{ USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
+	{ USB_DEVICE(0x2405, 0x0003) }, /* West Mountain Radio RIGblaster Advantage */
+	{ USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */
+	{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
+	{ USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */
+	{ USB_DEVICE(0x10C4, 0x817C) }, /* CESINEL MEDCAL N Power Quality Monitor */
+	{ USB_DEVICE(0x10C4, 0x817D) }, /* CESINEL MEDCAL NT Power Quality Monitor */
+	{ USB_DEVICE(0x10C4, 0x817E) }, /* CESINEL MEDCAL S Power Quality Monitor */
+	{ USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */
+	{ USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */
+	{ USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */
+	{ USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */
+	{ USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */
+	{ USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */
+	{ USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
+	{ USB_DEVICE(0x10C4, 0x81D7) }, /* IAI Corp. RCB-CV-USB USB to RS485 Adaptor */
+	{ USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
+	{ USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */
+	{ USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */
+	{ USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */
+	{ USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
+	{ USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */
+	{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
+	{ USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
+	{ USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
+	{ USB_DEVICE(0x10C4, 0x82EF) }, /* CESINEL FALCO 6105 AC Power Supply */
+	{ USB_DEVICE(0x10C4, 0x82F1) }, /* CESINEL MEDCAL EFD Earth Fault Detector */
+	{ USB_DEVICE(0x10C4, 0x82F2) }, /* CESINEL MEDCAL ST Network Analyzer */
+	{ USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */
+	{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
+	{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
+	{ USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
+	{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
+	{ USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */
+	{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
+	{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
+	{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
+	{ USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
+	{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
+	{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
+	{ USB_DEVICE(0x10C4, 0x851E) }, /* CESINEL MEDCAL PT Network Analyzer */
+	{ USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */
+	{ USB_DEVICE(0x10C4, 0x85B8) }, /* CESINEL ReCon T Energy Logger */
+	{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
+	{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
+	{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
+	{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
+	{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
+	{ USB_DEVICE(0x10C4, 0x8856) },	/* CEL EM357 ZigBee USB Stick - LR */
+	{ USB_DEVICE(0x10C4, 0x8857) },	/* CEL EM357 ZigBee USB Stick */
+	{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
+	{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
+	{ USB_DEVICE(0x10C4, 0x88FB) }, /* CESINEL MEDCAL STII Network Analyzer */
+	{ USB_DEVICE(0x10C4, 0x8938) }, /* CESINEL MEDCAL S II Network Analyzer */
+	{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
+	{ USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */
+	{ USB_DEVICE(0x10C4, 0x8977) },	/* CEL MeshWorks DevKit Device */
+	{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
+	{ USB_DEVICE(0x10C4, 0x89A4) }, /* CESINEL FTBC Flexible Thyristor Bridge Controller */
+	{ USB_DEVICE(0x10C4, 0x89FB) }, /* Qivicon ZigBee USB Radio Stick */
+	{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
+	{ USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */
+	{ USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */
+	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA63) }, /* Silicon Labs Windows Update (CP2101-4/CP2102N) */
+	{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
+	{ USB_DEVICE(0x10C4, 0xEA7A) }, /* Silicon Labs Windows Update (CP2105) */
+	{ USB_DEVICE(0x10C4, 0xEA7B) }, /* Silicon Labs Windows Update (CP2108) */
+	{ USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
+	{ USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
+	{ USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */
+	{ USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
+	{ USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
+	{ USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
+	{ USB_DEVICE(0x12B8, 0xEC60) }, /* Link G4 ECU */
+	{ USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */
+	{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
+	{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+	{ USB_DEVICE(0x155A, 0x1006) },	/* ELDAT Easywave RX09 */
+	{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
+	{ USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
+	{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
+	{ USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */
+	{ USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */
+	{ USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */
+	{ USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */
+	{ USB_DEVICE(0x16C0, 0x09B0) }, /* Lunatico Seletek */
+	{ USB_DEVICE(0x16C0, 0x09B1) }, /* Lunatico Seletek */
+	{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
+	{ USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
+	{ USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
+	{ USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */
+	{ USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */
+	{ USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */
+	{ USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */
+	{ USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */
+	{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
+	{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
+	{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+	{ USB_DEVICE(0x18EF, 0xE030) }, /* ELV ALC 8xxx Battery Charger */
+	{ USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
+	{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
+	{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
+	{ USB_DEVICE(0x1901, 0x0194) },	/* GE Healthcare Remote Alarm Box */
+	{ USB_DEVICE(0x1901, 0x0195) },	/* GE B850/B650/B450 CP2104 DP UART interface */
+	{ USB_DEVICE(0x1901, 0x0196) },	/* GE B850 CP2105 DP UART interface */
+	{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
+	{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
+	{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
+	{ USB_DEVICE(0x1BA4, 0x0002) },	/* Silicon Labs 358x factory default */
+	{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
+	{ USB_DEVICE(0x1D6F, 0x0010) }, /* Seluxit ApS RF Dongle */
+	{ USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */
+	{ USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */
+	{ USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */
+	{ USB_DEVICE(0x1FB9, 0x0200) }, /* Lake Shore Model 218A Temperature Monitor */
+	{ USB_DEVICE(0x1FB9, 0x0201) }, /* Lake Shore Model 219 Temperature Monitor */
+	{ USB_DEVICE(0x1FB9, 0x0202) }, /* Lake Shore Model 233 Temperature Transmitter */
+	{ USB_DEVICE(0x1FB9, 0x0203) }, /* Lake Shore Model 235 Temperature Transmitter */
+	{ USB_DEVICE(0x1FB9, 0x0300) }, /* Lake Shore Model 335 Temperature Controller */
+	{ USB_DEVICE(0x1FB9, 0x0301) }, /* Lake Shore Model 336 Temperature Controller */
+	{ USB_DEVICE(0x1FB9, 0x0302) }, /* Lake Shore Model 350 Temperature Controller */
+	{ USB_DEVICE(0x1FB9, 0x0303) }, /* Lake Shore Model 371 AC Bridge */
+	{ USB_DEVICE(0x1FB9, 0x0400) }, /* Lake Shore Model 411 Handheld Gaussmeter */
+	{ USB_DEVICE(0x1FB9, 0x0401) }, /* Lake Shore Model 425 Gaussmeter */
+	{ USB_DEVICE(0x1FB9, 0x0402) }, /* Lake Shore Model 455A Gaussmeter */
+	{ USB_DEVICE(0x1FB9, 0x0403) }, /* Lake Shore Model 475A Gaussmeter */
+	{ USB_DEVICE(0x1FB9, 0x0404) }, /* Lake Shore Model 465 Three Axis Gaussmeter */
+	{ USB_DEVICE(0x1FB9, 0x0600) }, /* Lake Shore Model 625A Superconducting MPS */
+	{ USB_DEVICE(0x1FB9, 0x0601) }, /* Lake Shore Model 642A Magnet Power Supply */
+	{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
+	{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
+	{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
+	{ USB_DEVICE(0x2626, 0xEA60) }, /* Aruba Networks 7xxx USB Serial Console */
+	{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
+	{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
+	{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
+	{ USB_DEVICE(0x3923, 0x7A0B) }, /* National Instruments USB Serial Console */
+	{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
+	{ } /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct cp210x_serial_private {
+#ifdef CONFIG_GPIOLIB
+	struct gpio_chip	gc;
+	bool			gpio_registered;
+	u8			gpio_pushpull;
+	u8			gpio_altfunc;
+	u8			gpio_input;
+#endif
+	u8			partnum;
+	speed_t			max_speed;
+	bool			use_actual_rate;
+};
+
+struct cp210x_port_private {
+	__u8			bInterfaceNumber;
+	bool			has_swapped_line_ctl;
+};
+
+static struct usb_serial_driver cp210x_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"cp210x",
+	},
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.bulk_in_size		= 256,
+	.bulk_out_size		= 256,
+	.open			= cp210x_open,
+	.close			= cp210x_close,
+	.break_ctl		= cp210x_break_ctl,
+	.set_termios		= cp210x_set_termios,
+	.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
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&cp210x_device, NULL
+};
+
+/* Config request types */
+#define REQTYPE_HOST_TO_INTERFACE	0x41
+#define REQTYPE_INTERFACE_TO_HOST	0xc1
+#define REQTYPE_HOST_TO_DEVICE	0x40
+#define REQTYPE_DEVICE_TO_HOST	0xc0
+
+/* Config request codes */
+#define CP210X_IFC_ENABLE	0x00
+#define CP210X_SET_BAUDDIV	0x01
+#define CP210X_GET_BAUDDIV	0x02
+#define CP210X_SET_LINE_CTL	0x03
+#define CP210X_GET_LINE_CTL	0x04
+#define CP210X_SET_BREAK	0x05
+#define CP210X_IMM_CHAR		0x06
+#define CP210X_SET_MHS		0x07
+#define CP210X_GET_MDMSTS	0x08
+#define CP210X_SET_XON		0x09
+#define CP210X_SET_XOFF		0x0A
+#define CP210X_SET_EVENTMASK	0x0B
+#define CP210X_GET_EVENTMASK	0x0C
+#define CP210X_SET_CHAR		0x0D
+#define CP210X_GET_CHARS	0x0E
+#define CP210X_GET_PROPS	0x0F
+#define CP210X_GET_COMM_STATUS	0x10
+#define CP210X_RESET		0x11
+#define CP210X_PURGE		0x12
+#define CP210X_SET_FLOW		0x13
+#define CP210X_GET_FLOW		0x14
+#define CP210X_EMBED_EVENTS	0x15
+#define CP210X_GET_EVENTSTATE	0x16
+#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
+#define UART_DISABLE		0x0000
+
+/* CP210X_(SET|GET)_BAUDDIV */
+#define BAUD_RATE_GEN_FREQ	0x384000
+
+/* CP210X_(SET|GET)_LINE_CTL */
+#define BITS_DATA_MASK		0X0f00
+#define BITS_DATA_5		0X0500
+#define BITS_DATA_6		0X0600
+#define BITS_DATA_7		0X0700
+#define BITS_DATA_8		0X0800
+#define BITS_DATA_9		0X0900
+
+#define BITS_PARITY_MASK	0x00f0
+#define BITS_PARITY_NONE	0x0000
+#define BITS_PARITY_ODD		0x0010
+#define BITS_PARITY_EVEN	0x0020
+#define BITS_PARITY_MARK	0x0030
+#define BITS_PARITY_SPACE	0x0040
+
+#define BITS_STOP_MASK		0x000f
+#define BITS_STOP_1		0x0000
+#define BITS_STOP_1_5		0x0001
+#define BITS_STOP_2		0x0002
+
+/* CP210X_SET_BREAK */
+#define BREAK_ON		0x0001
+#define BREAK_OFF		0x0000
+
+/* CP210X_(SET_MHS|GET_MDMSTS) */
+#define CONTROL_DTR		0x0001
+#define CONTROL_RTS		0x0002
+#define CONTROL_CTS		0x0010
+#define CONTROL_DSR		0x0020
+#define CONTROL_RING		0x0040
+#define CONTROL_DCD		0x0080
+#define CONTROL_WRITE_DTR	0x0100
+#define CONTROL_WRITE_RTS	0x0200
+
+/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_READ_2NCONFIG	0x000E
+#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
+#define CP210X_PARTNUM_CP2102N_QFN28	0x20
+#define CP210X_PARTNUM_CP2102N_QFN24	0x21
+#define CP210X_PARTNUM_CP2102N_QFN20	0x22
+#define CP210X_PARTNUM_UNKNOWN	0xFF
+
+/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
+struct cp210x_comm_status {
+	__le32   ulErrors;
+	__le32   ulHoldReasons;
+	__le32   ulAmountInInQueue;
+	__le32   ulAmountInOutQueue;
+	u8       bEofReceived;
+	u8       bWaitForImmediate;
+	u8       bReserved;
+} __packed;
+
+/*
+ * CP210X_PURGE - 16 bits passed in wValue of USB request.
+ * SiLabs app note AN571 gives a strange description of the 4 bits:
+ * bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
+ * writing 1 to all, however, purges cp2108 well enough to avoid the hang.
+ */
+#define PURGE_ALL		0x000f
+
+/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
+struct cp210x_flow_ctl {
+	__le32	ulControlHandshake;
+	__le32	ulFlowReplace;
+	__le32	ulXonLimit;
+	__le32	ulXoffLimit;
+} __packed;
+
+/* cp210x_flow_ctl::ulControlHandshake */
+#define CP210X_SERIAL_DTR_MASK		GENMASK(1, 0)
+#define CP210X_SERIAL_DTR_SHIFT(_mode)	(_mode)
+#define CP210X_SERIAL_CTS_HANDSHAKE	BIT(3)
+#define CP210X_SERIAL_DSR_HANDSHAKE	BIT(4)
+#define CP210X_SERIAL_DCD_HANDSHAKE	BIT(5)
+#define CP210X_SERIAL_DSR_SENSITIVITY	BIT(6)
+
+/* values for cp210x_flow_ctl::ulControlHandshake::CP210X_SERIAL_DTR_MASK */
+#define CP210X_SERIAL_DTR_INACTIVE	0
+#define CP210X_SERIAL_DTR_ACTIVE	1
+#define CP210X_SERIAL_DTR_FLOW_CTL	2
+
+/* cp210x_flow_ctl::ulFlowReplace */
+#define CP210X_SERIAL_AUTO_TRANSMIT	BIT(0)
+#define CP210X_SERIAL_AUTO_RECEIVE	BIT(1)
+#define CP210X_SERIAL_ERROR_CHAR	BIT(2)
+#define CP210X_SERIAL_NULL_STRIPPING	BIT(3)
+#define CP210X_SERIAL_BREAK_CHAR	BIT(4)
+#define CP210X_SERIAL_RTS_MASK		GENMASK(7, 6)
+#define CP210X_SERIAL_RTS_SHIFT(_mode)	(_mode << 6)
+#define CP210X_SERIAL_XOFF_CONTINUE	BIT(31)
+
+/* values for cp210x_flow_ctl::ulFlowReplace::CP210X_SERIAL_RTS_MASK */
+#define CP210X_SERIAL_RTS_INACTIVE	0
+#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)
+
+/* CP2102N configuration array indices */
+#define CP210X_2NCONFIG_CONFIG_VERSION_IDX	2
+#define CP210X_2NCONFIG_GPIO_MODE_IDX		581
+#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX	587
+#define CP210X_2NCONFIG_GPIO_CONTROL_IDX	600
+
+/* 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.
+ */
+static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
+		void *buf, int bufsize)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	void *dmabuf;
+	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);
+		return -ENOMEM;
+	}
+
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			req, REQTYPE_INTERFACE_TO_HOST, 0,
+			port_priv->bInterfaceNumber, dmabuf, bufsize,
+			USB_CTRL_SET_TIMEOUT);
+	if (result == bufsize) {
+		memcpy(buf, dmabuf, bufsize);
+		result = 0;
+	} else {
+		dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n",
+				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);
+
+	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.
+ */
+static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
+{
+	return cp210x_read_reg_block(port, req, val, sizeof(*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.
+ */
+static int cp210x_write_u16_reg(struct usb_serial_port *port, u8 req, u16 val)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	int result;
+
+	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			req, REQTYPE_HOST_TO_INTERFACE, val,
+			port_priv->bInterfaceNumber, NULL, 0,
+			USB_CTRL_SET_TIMEOUT);
+	if (result < 0) {
+		dev_err(&port->dev, "failed set request 0x%x status: %d\n",
+				req, result);
+	}
+
+	return result;
+}
+
+/*
+ * Writes a variable-sized block of CP210X_ registers, identified by req.
+ * Data in buf must be in native USB byte order.
+ */
+static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
+		void *buf, int bufsize)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	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),
+			req, REQTYPE_HOST_TO_INTERFACE, 0,
+			port_priv->bInterfaceNumber, dmabuf, bufsize,
+			USB_CTRL_SET_TIMEOUT);
+
+	kfree(dmabuf);
+
+	if (result == bufsize) {
+		result = 0;
+	} else {
+		dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n",
+				req, bufsize, result);
+		if (result >= 0)
+			result = -EIO;
+	}
+
+	return result;
+}
+
+/*
+ * Writes any 32-bit CP210X_ register identified by req.
+ */
+static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
+{
+	__le32 le32_val;
+
+	le32_val = cpu_to_le32(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.
+ * 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)
+{
+	int result;
+
+	result = cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_ENABLE);
+	if (result) {
+		dev_err(&port->dev, "%s - Unable to enable UART\n", __func__);
+		return result;
+	}
+
+	/* Configure the termios structure */
+	cp210x_get_termios(tty, port);
+
+	/* The baud rate must be initialised on cp2104 */
+	if (tty)
+		cp210x_change_speed(tty, port, NULL);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static void cp210x_close(struct usb_serial_port *port)
+{
+	usb_serial_generic_close(port);
+
+	/* Clear both queues; cp2108 needs this to avoid an occasional hang */
+	cp210x_write_u16_reg(port, CP210X_PURGE, PURGE_ALL);
+
+	cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);
+}
+
+/*
+ * Read how many bytes are waiting in the TX queue.
+ */
+static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
+		u32 *count)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	struct cp210x_comm_status *sts;
+	int result;
+
+	sts = kmalloc(sizeof(*sts), GFP_KERNEL);
+	if (!sts)
+		return -ENOMEM;
+
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			CP210X_GET_COMM_STATUS, REQTYPE_INTERFACE_TO_HOST,
+			0, port_priv->bInterfaceNumber, sts, sizeof(*sts),
+			USB_CTRL_GET_TIMEOUT);
+	if (result == sizeof(*sts)) {
+		*count = le32_to_cpu(sts->ulAmountInOutQueue);
+		result = 0;
+	} else {
+		dev_err(&port->dev, "failed to get comm status: %d\n", result);
+		if (result >= 0)
+			result = -EIO;
+	}
+
+	kfree(sts);
+
+	return result;
+}
+
+static bool cp210x_tx_empty(struct usb_serial_port *port)
+{
+	int err;
+	u32 count;
+
+	err = cp210x_get_tx_queue_byte_count(port, &count);
+	if (err)
+		return true;
+
+	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;
+
+	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__);
+		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;
+};
+
+static const struct cp210x_rate cp210x_an205_table1[] = {
+	{ 300, 300 },
+	{ 600, 600 },
+	{ 1200, 1200 },
+	{ 1800, 1800 },
+	{ 2400, 2400 },
+	{ 4000, 4000 },
+	{ 4800, 4803 },
+	{ 7200, 7207 },
+	{ 9600, 9612 },
+	{ 14400, 14428 },
+	{ 16000, 16062 },
+	{ 19200, 19250 },
+	{ 28800, 28912 },
+	{ 38400, 38601 },
+	{ 51200, 51558 },
+	{ 56000, 56280 },
+	{ 57600, 58053 },
+	{ 64000, 64111 },
+	{ 76800, 77608 },
+	{ 115200, 117028 },
+	{ 128000, 129347 },
+	{ 153600, 156868 },
+	{ 230400, 237832 },
+	{ 250000, 254234 },
+	{ 256000, 273066 },
+	{ 460800, 491520 },
+	{ 500000, 567138 },
+	{ 576000, 670254 },
+	{ 921600, UINT_MAX }
+};
+
+/*
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static speed_t cp210x_get_an205_rate(speed_t baud)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
+		if (baud <= cp210x_an205_table1[i].high)
+			break;
+	}
+
+	return cp210x_an205_table1[i].rate;
+}
+
+static speed_t cp210x_get_actual_rate(struct usb_serial *serial, speed_t baud)
+{
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	unsigned int prescale = 1;
+	unsigned int div;
+
+	baud = clamp(baud, 300u, priv->max_speed);
+
+	if (baud <= 365)
+		prescale = 4;
+
+	div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
+	baud = 48000000 / (2 * prescale * div);
+
+	return baud;
+}
+
+/*
+ * CP2101 supports the following baud rates:
+ *
+ *	300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
+ *	38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
+ *
+ * CP2102 and CP2103 support the following additional rates:
+ *
+ *	4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
+ *	576000
+ *
+ * The device will map a requested rate to a supported one, but the result
+ * of requests for rates greater than 1053257 is undefined (see AN205).
+ *
+ * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
+ * respectively, with an error less than 1%. The actual rates are determined
+ * by
+ *
+ *	div = round(freq / (2 x prescale x request))
+ *	actual = freq / (2 x prescale x div)
+ *
+ * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
+ * or 1 otherwise.
+ * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
+ * otherwise.
+ */
+static void cp210x_change_speed(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	u32 baud;
+
+	baud = tty->termios.c_ospeed;
+
+	/*
+	 * This maps the requested rate to the actual rate, a valid rate on
+	 * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
+	 *
+	 * NOTE: B0 is not implemented.
+	 */
+	if (priv->use_actual_rate)
+		baud = cp210x_get_actual_rate(serial, baud);
+	else if (baud < 1000000)
+		baud = cp210x_get_an205_rate(baud);
+	else if (baud > priv->max_speed)
+		baud = priv->max_speed;
+
+	dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
+	if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) {
+		dev_warn(&port->dev, "failed to set baud rate to %u\n", baud);
+		if (old_termios)
+			baud = old_termios->c_ospeed;
+		else
+			baud = 9600;
+	}
+
+	tty_encode_baud_rate(tty, baud, baud);
+}
+
+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;
+	u16 bits;
+
+	cflag = tty->termios.c_cflag;
+	old_cflag = old_termios->c_cflag;
+
+	if (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");
+	}
+
+	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");
+	}
+
+	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__);
+		} else {
+			bits |= BITS_STOP_1;
+			dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+		}
+		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;
+
+			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__);
+		}
+
+		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));
+	}
+
+}
+
+static int cp210x_tiocmset(struct tty_struct *tty,
+		unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	return cp210x_tiocmset_port(port, set, clear);
+}
+
+static int cp210x_tiocmset_port(struct usb_serial_port *port,
+		unsigned int set, unsigned int clear)
+{
+	u16 control = 0;
+
+	if (set & TIOCM_RTS) {
+		control |= CONTROL_RTS;
+		control |= CONTROL_WRITE_RTS;
+	}
+	if (set & TIOCM_DTR) {
+		control |= CONTROL_DTR;
+		control |= CONTROL_WRITE_DTR;
+	}
+	if (clear & TIOCM_RTS) {
+		control &= ~CONTROL_RTS;
+		control |= CONTROL_WRITE_RTS;
+	}
+	if (clear & TIOCM_DTR) {
+		control &= ~CONTROL_DTR;
+		control |= CONTROL_WRITE_DTR;
+	}
+
+	dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control);
+
+	return cp210x_write_u16_reg(port, CP210X_SET_MHS, control);
+}
+
+static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
+{
+	if (on)
+		cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0);
+	else
+		cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS);
+}
+
+static int cp210x_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	u8 control;
+	int result;
+
+	result = cp210x_read_u8_reg(port, CP210X_GET_MDMSTS, &control);
+	if (result)
+		return result;
+
+	result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
+		|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
+		|((control & CONTROL_CTS) ? TIOCM_CTS : 0)
+		|((control & CONTROL_DSR) ? TIOCM_DSR : 0)
+		|((control & CONTROL_RING)? TIOCM_RI  : 0)
+		|((control & CONTROL_DCD) ? TIOCM_CD  : 0);
+
+	dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control);
+
+	return result;
+}
+
+static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	u16 state;
+
+	if (break_state == 0)
+		state = BREAK_OFF;
+	else
+		state = BREAK_ON;
+	dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
+		state == BREAK_OFF ? "off" : "on");
+	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);
+
+	if (priv->gpio_altfunc & BIT(offset))
+		return -ENODEV;
+
+	return 0;
+}
+
+static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct usb_serial *serial = gpiochip_get_data(gc);
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	u8 req_type = REQTYPE_DEVICE_TO_HOST;
+	int result;
+	u8 buf;
+
+	if (priv->partnum == CP210X_PARTNUM_CP2105)
+		req_type = REQTYPE_INTERFACE_TO_HOST;
+
+	result = cp210x_read_vendor_block(serial, req_type,
+					  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_serial_private *priv = usb_get_serial_data(serial);
+	struct cp210x_gpio_write buf;
+	int result;
+
+	if (value == 1)
+		buf.state = BIT(gpio);
+	else
+		buf.state = 0;
+
+	buf.mask = BIT(gpio);
+
+	if (priv->partnum == CP210X_PARTNUM_CP2105) {
+		result = cp210x_write_vendor_block(serial,
+						   REQTYPE_HOST_TO_INTERFACE,
+						   CP210X_WRITE_LATCH, &buf,
+						   sizeof(buf));
+	} else {
+		u16 wIndex = buf.state << 8 | buf.mask;
+
+		result = usb_control_msg(serial->dev,
+					 usb_sndctrlpipe(serial->dev, 0),
+					 CP210X_VENDOR_SPECIFIC,
+					 REQTYPE_HOST_TO_DEVICE,
+					 CP210X_WRITE_LATCH,
+					 wIndex,
+					 NULL, 0, USB_CTRL_SET_TIMEOUT);
+	}
+
+	if (result < 0) {
+		dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n",
+				result);
+	}
+}
+
+static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct usb_serial *serial = gpiochip_get_data(gc);
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+	return priv->gpio_input & BIT(gpio);
+}
+
+static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct usb_serial *serial = gpiochip_get_data(gc);
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+	if (priv->partnum == CP210X_PARTNUM_CP2105) {
+		/* hardware does not support an input mode */
+		return -ENOTSUPP;
+	}
+
+	/* push-pull pins cannot be changed to be inputs */
+	if (priv->gpio_pushpull & BIT(gpio))
+		return -EINVAL;
+
+	/* make sure to release pin if it is being driven low */
+	cp210x_gpio_set(gc, gpio, 1);
+
+	priv->gpio_input |= BIT(gpio);
+
+	return 0;
+}
+
+static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
+					int value)
+{
+	struct usb_serial *serial = gpiochip_get_data(gc);
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+	priv->gpio_input &= ~BIT(gpio);
+	cp210x_gpio_set(gc, gpio, value);
+
+	return 0;
+}
+
+static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
+				  unsigned long config)
+{
+	struct usb_serial *serial = gpiochip_get_data(gc);
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	enum pin_config_param param = pinconf_to_config_param(config);
+
+	/* Succeed only if in correct mode (this can't be set at runtime) */
+	if ((param == PIN_CONFIG_DRIVE_PUSH_PULL) &&
+	    (priv->gpio_pushpull & BIT(gpio)))
+		return 0;
+
+	if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) &&
+	    !(priv->gpio_pushpull & 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_gpioconf_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);
+	u8 iface_config;
+	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) {
+			/* mark all GPIOs of this interface as reserved */
+			priv->gpio_altfunc = 0xff;
+			return 0;
+		}
+
+		iface_config = config.eci_cfg;
+		priv->gpio_pushpull = (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) {
+			/* mark all GPIOs of this interface as reserved */
+			priv->gpio_altfunc = 0xff;
+			return 0;
+		}
+
+		iface_config = config.sci_cfg;
+		priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
+						CP210X_SCI_GPIO_MODE_MASK) >>
+						CP210X_SCI_GPIO_MODE_OFFSET);
+		priv->gc.ngpio = 3;
+	} else {
+		return -ENODEV;
+	}
+
+	/* mark all pins which are not in GPIO mode */
+	if (iface_config & CP2105_GPIO0_TXLED_MODE)	/* GPIO 0 */
+		priv->gpio_altfunc |= BIT(0);
+	if (iface_config & (CP2105_GPIO1_RXLED_MODE |	/* GPIO 1 */
+			CP2105_GPIO1_RS485_MODE))
+		priv->gpio_altfunc |= BIT(1);
+
+	/* driver implementation for CP2105 only supports outputs */
+	priv->gpio_input = 0;
+
+	return 0;
+}
+
+static int cp2102n_gpioconf_init(struct usb_serial *serial)
+{
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	const u16 config_size = 0x02a6;
+	u8 gpio_rst_latch;
+	u8 config_version;
+	u8 gpio_pushpull;
+	u8 *config_buf;
+	u8 gpio_latch;
+	u8 gpio_ctrl;
+	int result;
+	u8 i;
+
+	/*
+	 * Retrieve device configuration from the device.
+	 * The array received contains all customization settings done at the
+	 * factory/manufacturer. Format of the array is documented at the
+	 * time of writing at:
+	 * https://www.silabs.com/community/interface/knowledge-base.entry.html/2017/03/31/cp2102n_setconfig-xsfa
+	 */
+	config_buf = kmalloc(config_size, GFP_KERNEL);
+	if (!config_buf)
+		return -ENOMEM;
+
+	result = cp210x_read_vendor_block(serial,
+					  REQTYPE_DEVICE_TO_HOST,
+					  CP210X_READ_2NCONFIG,
+					  config_buf,
+					  config_size);
+	if (result < 0) {
+		kfree(config_buf);
+		return result;
+	}
+
+	config_version = config_buf[CP210X_2NCONFIG_CONFIG_VERSION_IDX];
+	gpio_pushpull = config_buf[CP210X_2NCONFIG_GPIO_MODE_IDX];
+	gpio_ctrl = config_buf[CP210X_2NCONFIG_GPIO_CONTROL_IDX];
+	gpio_rst_latch = config_buf[CP210X_2NCONFIG_GPIO_RSTLATCH_IDX];
+
+	kfree(config_buf);
+
+	/* Make sure this is a config format we understand. */
+	if (config_version != 0x01)
+		return -ENOTSUPP;
+
+	/*
+	 * We only support 4 GPIOs even on the QFN28 package, because
+	 * config locations of GPIOs 4-6 determined using reverse
+	 * engineering revealed conflicting offsets with other
+	 * documented functions. So we'll just play it safe for now.
+	 */
+	priv->gc.ngpio = 4;
+
+	/*
+	 * Get default pin states after reset. Needed so we can determine
+	 * the direction of an open-drain pin.
+	 */
+	gpio_latch = (gpio_rst_latch >> 3) & 0x0f;
+
+	/* 0 indicates open-drain mode, 1 is push-pull */
+	priv->gpio_pushpull = (gpio_pushpull >> 3) & 0x0f;
+
+	/* 0 indicates GPIO mode, 1 is alternate function */
+	priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
+
+	/*
+	 * The CP2102N does not strictly has input and output pin modes,
+	 * it only knows open-drain and push-pull modes which is set at
+	 * factory. An open-drain pin can function both as an
+	 * input or an output. We emulate input mode for open-drain pins
+	 * by making sure they are not driven low, and we do not allow
+	 * push-pull pins to be set as an input.
+	 */
+	for (i = 0; i < priv->gc.ngpio; ++i) {
+		/*
+		 * Set direction to "input" iff pin is open-drain and reset
+		 * value is 1.
+		 */
+		if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i)))
+			priv->gpio_input |= BIT(i);
+	}
+
+	return 0;
+}
+
+static int cp210x_gpio_init(struct usb_serial *serial)
+{
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	int result;
+
+	switch (priv->partnum) {
+	case CP210X_PARTNUM_CP2105:
+		result = cp2105_gpioconf_init(serial);
+		break;
+	case CP210X_PARTNUM_CP2102N_QFN28:
+	case CP210X_PARTNUM_CP2102N_QFN24:
+	case CP210X_PARTNUM_CP2102N_QFN20:
+		result = cp2102n_gpioconf_init(serial);
+		break;
+	default:
+		return 0;
+	}
+
+	if (result < 0)
+		return result;
+
+	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_config = cp210x_gpio_set_config;
+	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 cp210x_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 cp210x_port_private *port_priv;
+	int ret;
+
+	port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+	if (!port_priv)
+		return -ENOMEM;
+
+	port_priv->bInterfaceNumber = cp210x_interface_num(serial);
+
+	usb_set_serial_port_data(port, port_priv);
+
+	ret = cp210x_detect_swapped_line_ctl(port);
+	if (ret) {
+		kfree(port_priv);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cp210x_port_remove(struct usb_serial_port *port)
+{
+	struct cp210x_port_private *port_priv;
+
+	port_priv = usb_get_serial_port_data(port);
+	kfree(port_priv);
+
+	return 0;
+}
+
+static void cp210x_init_max_speed(struct usb_serial *serial)
+{
+	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+	bool use_actual_rate = false;
+	speed_t max;
+
+	switch (priv->partnum) {
+	case CP210X_PARTNUM_CP2101:
+		max = 921600;
+		break;
+	case CP210X_PARTNUM_CP2102:
+	case CP210X_PARTNUM_CP2103:
+		max = 1000000;
+		break;
+	case CP210X_PARTNUM_CP2104:
+		use_actual_rate = true;
+		max = 2000000;
+		break;
+	case CP210X_PARTNUM_CP2108:
+		max = 2000000;
+		break;
+	case CP210X_PARTNUM_CP2105:
+		if (cp210x_interface_num(serial) == 0) {
+			use_actual_rate = true;
+			max = 2000000;	/* ECI */
+		} else {
+			max = 921600;	/* SCI */
+		}
+		break;
+	case CP210X_PARTNUM_CP2102N_QFN28:
+	case CP210X_PARTNUM_CP2102N_QFN24:
+	case CP210X_PARTNUM_CP2102N_QFN20:
+		use_actual_rate = true;
+		max = 3000000;
+		break;
+	default:
+		max = 2000000;
+		break;
+	}
+
+	priv->max_speed = max;
+	priv->use_actual_rate = use_actual_rate;
+}
+
+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) {
+		dev_warn(&serial->interface->dev,
+			 "querying part number failed\n");
+		priv->partnum = CP210X_PARTNUM_UNKNOWN;
+	}
+
+	usb_set_serial_data(serial, priv);
+
+	cp210x_init_max_speed(serial);
+
+	result = cp210x_gpio_init(serial);
+	if (result < 0) {
+		dev_err(&serial->interface->dev, "GPIO initialisation failed: %d\n",
+				result);
+	}
+
+	return 0;
+}
+
+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);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
new file mode 100644
index 0000000..ebd76ab
--- /dev/null
+++ b/drivers/usb/serial/cyberjack.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver
+ *
+ *  Copyright (C) 2001  REINER SCT
+ *  Author: Matthias Bruestle
+ *
+ *  Contact: support@reiner-sct.com (see MAINTAINERS)
+ *
+ *  This program is largely derived from work by the linux-usb group
+ *  and associated source files.  Please see the usb/serial files for
+ *  individual credits and copyrights.
+ *
+ *  Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
+ *  patience.
+ *
+ *  In case of problems, please write to the contact e-mail address
+ *  mentioned above.
+ *
+ *  Please note that later models of the cyberjack reader family are
+ *  supported by a libusb-based userspace device driver.
+ *
+ *  Homepage: http://www.reiner-sct.de/support/treiber_cyberjack.php#linux
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define CYBERJACK_LOCAL_BUF_SIZE 32
+
+#define DRIVER_AUTHOR "Matthias Bruestle"
+#define DRIVER_DESC "REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver"
+
+
+#define CYBERJACK_VENDOR_ID	0x0C4B
+#define CYBERJACK_PRODUCT_ID	0x0100
+
+/* Function prototypes */
+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,
+	struct usb_serial_port *port);
+static void cyberjack_close(struct usb_serial_port *port);
+static int cyberjack_write(struct tty_struct *tty,
+	struct usb_serial_port *port, const unsigned char *buf, int count);
+static int cyberjack_write_room(struct tty_struct *tty);
+static void cyberjack_read_int_callback(struct urb *urb);
+static void cyberjack_read_bulk_callback(struct urb *urb);
+static void cyberjack_write_bulk_callback(struct urb *urb);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) },
+	{ }			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver cyberjack_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"cyberjack",
+	},
+	.description =		"Reiner SCT Cyberjack USB card reader",
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_bulk_out =		1,
+	.port_probe =		cyberjack_port_probe,
+	.port_remove =		cyberjack_port_remove,
+	.open =			cyberjack_open,
+	.close =		cyberjack_close,
+	.write =		cyberjack_write,
+	.write_room =		cyberjack_write_room,
+	.read_int_callback =	cyberjack_read_int_callback,
+	.read_bulk_callback =	cyberjack_read_bulk_callback,
+	.write_bulk_callback =	cyberjack_write_bulk_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&cyberjack_device, NULL
+};
+
+struct cyberjack_private {
+	spinlock_t	lock;		/* Lock for SMP */
+	short		rdtodo;		/* Bytes still to read */
+	unsigned char	wrbuf[5*64];	/* Buffer for collecting data to write */
+	short		wrfilled;	/* Overall data size we already got */
+	short		wrsent;		/* Data already sent */
+};
+
+static int cyberjack_port_probe(struct usb_serial_port *port)
+{
+	struct cyberjack_private *priv;
+	int result;
+
+	priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->rdtodo = 0;
+	priv->wrfilled = 0;
+	priv->wrsent = 0;
+
+	usb_set_serial_port_data(port, priv);
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result)
+		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
+
+	return 0;
+}
+
+static int cyberjack_port_remove(struct usb_serial_port *port)
+{
+	struct cyberjack_private *priv;
+
+	usb_kill_urb(port->interrupt_in_urb);
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int  cyberjack_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	struct cyberjack_private *priv;
+	unsigned long flags;
+
+	dev_dbg(&port->dev, "%s - usb_clear_halt\n", __func__);
+	usb_clear_halt(port->serial->dev, port->write_urb->pipe);
+
+	priv = usb_get_serial_port_data(port);
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->rdtodo = 0;
+	priv->wrfilled = 0;
+	priv->wrsent = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static void cyberjack_close(struct usb_serial_port *port)
+{
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+}
+
+static int cyberjack_write(struct tty_struct *tty,
+	struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+	struct device *dev = &port->dev;
+	struct cyberjack_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int result;
+	int wrexpected;
+
+	if (count == 0) {
+		dev_dbg(dev, "%s - write request of 0 bytes\n", __func__);
+		return 0;
+	}
+
+	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
+		dev_dbg(dev, "%s - already writing\n", __func__);
+		return 0;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (count+priv->wrfilled > sizeof(priv->wrbuf)) {
+		/* To much data for buffer. Reset buffer. */
+		priv->wrfilled = 0;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		set_bit(0, &port->write_urbs_free);
+		return 0;
+	}
+
+	/* Copy data */
+	memcpy(priv->wrbuf + priv->wrfilled, buf, count);
+
+	usb_serial_debug_data(dev, __func__, count, priv->wrbuf + priv->wrfilled);
+	priv->wrfilled += count;
+
+	if (priv->wrfilled >= 3) {
+		wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
+		dev_dbg(dev, "%s - expected data: %d\n", __func__, wrexpected);
+	} else
+		wrexpected = sizeof(priv->wrbuf);
+
+	if (priv->wrfilled >= wrexpected) {
+		/* We have enough data to begin transmission */
+		int length;
+
+		dev_dbg(dev, "%s - transmitting data (frame 1)\n", __func__);
+		length = (wrexpected > port->bulk_out_size) ?
+					port->bulk_out_size : wrexpected;
+
+		memcpy(port->write_urb->transfer_buffer, priv->wrbuf, length);
+		priv->wrsent = length;
+
+		/* set up our urb */
+		port->write_urb->transfer_buffer_length = length;
+
+		/* send the data out the bulk port */
+		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (result) {
+			dev_err(&port->dev,
+				"%s - failed submitting write urb, error %d\n",
+				__func__, result);
+			/* Throw away data. No better idea what to do with it. */
+			priv->wrfilled = 0;
+			priv->wrsent = 0;
+			spin_unlock_irqrestore(&priv->lock, flags);
+			set_bit(0, &port->write_urbs_free);
+			return 0;
+		}
+
+		dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
+		dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
+
+		if (priv->wrsent >= priv->wrfilled) {
+			dev_dbg(dev, "%s - buffer cleaned\n", __func__);
+			memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
+			priv->wrfilled = 0;
+			priv->wrsent = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return count;
+}
+
+static int cyberjack_write_room(struct tty_struct *tty)
+{
+	/* FIXME: .... */
+	return CYBERJACK_LOCAL_BUF_SIZE;
+}
+
+static void cyberjack_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct cyberjack_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	unsigned long flags;
+	int result;
+
+	/* the urb might have been killed. */
+	if (status)
+		return;
+
+	usb_serial_debug_data(dev, __func__, urb->actual_length, data);
+
+	/* React only to interrupts signaling a bulk_in transfer */
+	if (urb->actual_length == 4 && data[0] == 0x01) {
+		short old_rdtodo;
+
+		/* This is a announcement of coming bulk_ins. */
+		unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3;
+
+		spin_lock_irqsave(&priv->lock, flags);
+
+		old_rdtodo = priv->rdtodo;
+
+		if (old_rdtodo > SHRT_MAX - size) {
+			dev_dbg(dev, "To many bulk_in urbs to do.\n");
+			spin_unlock_irqrestore(&priv->lock, flags);
+			goto resubmit;
+		}
+
+		/* "+=" is probably more fault tolerant than "=" */
+		priv->rdtodo += size;
+
+		dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo);
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		if (!old_rdtodo) {
+			result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (result)
+				dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
+					__func__, result);
+			dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
+		}
+	}
+
+resubmit:
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
+	dev_dbg(dev, "%s - usb_submit_urb(int urb)\n", __func__);
+}
+
+static void cyberjack_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct cyberjack_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	short todo;
+	int result;
+	int status = urb->status;
+
+	usb_serial_debug_data(dev, __func__, urb->actual_length, data);
+	if (status) {
+		dev_dbg(dev, "%s - nonzero read bulk status received: %d\n",
+			__func__, status);
+		return;
+	}
+
+	if (urb->actual_length) {
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Reduce urbs to do by one. */
+	priv->rdtodo -= urb->actual_length;
+	/* Just to be sure */
+	if (priv->rdtodo < 0)
+		priv->rdtodo = 0;
+	todo = priv->rdtodo;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo);
+
+	/* Continue to read if we have still urbs to do. */
+	if (todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/) {
+		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (result)
+			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
+				__func__, result);
+		dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
+	}
+}
+
+static void cyberjack_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct cyberjack_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	int status = urb->status;
+	unsigned long flags;
+
+	set_bit(0, &port->write_urbs_free);
+	if (status) {
+		dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
+			__func__, status);
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* only do something if we have more data to send */
+	if (priv->wrfilled) {
+		int length, blksize, result;
+
+		dev_dbg(dev, "%s - transmitting data (frame n)\n", __func__);
+
+		length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ?
+			port->bulk_out_size : (priv->wrfilled - priv->wrsent);
+
+		memcpy(port->write_urb->transfer_buffer,
+					priv->wrbuf + priv->wrsent, length);
+		priv->wrsent += length;
+
+		/* set up our urb */
+		port->write_urb->transfer_buffer_length = length;
+
+		/* send the data out the bulk port */
+		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (result) {
+			dev_err(dev, "%s - failed submitting write urb, error %d\n",
+				__func__, result);
+			/* Throw away data. No better idea what to do with it. */
+			priv->wrfilled = 0;
+			priv->wrsent = 0;
+			goto exit;
+		}
+
+		dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
+		dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
+
+		blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
+
+		if (priv->wrsent >= priv->wrfilled ||
+					priv->wrsent >= blksize) {
+			dev_dbg(dev, "%s - buffer cleaned\n", __func__);
+			memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
+			priv->wrfilled = 0;
+			priv->wrsent = 0;
+		}
+	}
+
+exit:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	usb_serial_port_softint(port);
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
new file mode 100644
index 0000000..e0035c0
--- /dev/null
+++ b/drivers/usb/serial/cypress_m8.c
@@ -0,0 +1,1256 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Cypress M8 driver
+ *
+ * 	Copyright (C) 2004
+ * 	    Lonnie Mendez (dignome@gmail.com)
+ *	Copyright (C) 2003,2004
+ *	    Neil Whelchel (koyama@firstlight.net)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * See http://geocities.com/i0xox0i for information on this driver and the
+ * earthmate usb device.
+ */
+
+/* Thanks to Neil Whelchel for writing the first cypress m8 implementation
+   for linux. */
+/* Thanks to cypress for providing references for the hid reports. */
+/* Thanks to Jiang Zhang for providing links and for general help. */
+/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <linux/kfifo.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "cypress_m8.h"
+
+
+static bool stats;
+static int interval;
+static bool unstable_bauds;
+
+#define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
+#define DRIVER_DESC "Cypress USB to Serial Driver"
+
+/* write buffer size defines */
+#define CYPRESS_BUF_SIZE	1024
+
+static const struct usb_device_id id_table_earthmate[] = {
+	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
+	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_cyphidcomrs232[] = {
+	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
+	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_nokiaca42v2[] = {
+	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
+	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
+	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
+	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
+	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+enum packet_format {
+	packet_format_1,  /* b0:status, b1:payload count */
+	packet_format_2   /* b0[7:3]:status, b0[2:0]:payload count */
+};
+
+struct cypress_private {
+	spinlock_t lock;		   /* private lock */
+	int chiptype;			   /* identifier of device, for quirks/etc */
+	int bytes_in;			   /* used for statistics */
+	int bytes_out;			   /* used for statistics */
+	int cmd_count;			   /* used for statistics */
+	int cmd_ctrl;			   /* always set this to 1 before issuing a command */
+	struct kfifo write_fifo;	   /* write fifo */
+	int write_urb_in_use;		   /* write urb in use indicator */
+	int write_urb_interval;            /* interval to use for write urb */
+	int read_urb_interval;             /* interval to use for read urb */
+	int comm_is_ok;                    /* true if communication is (still) ok */
+	int termios_initialized;
+	__u8 line_control;	   	   /* holds dtr / rts value */
+	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */
+	__u8 current_config;	   	   /* stores the current configuration byte */
+	__u8 rx_flags;			   /* throttling - used from whiteheat/ftdi_sio */
+	enum packet_format pkt_fmt;	   /* format to use for packet send / receive */
+	int get_cfg_unsafe;		   /* If true, the CYPRESS_GET_CONFIG is unsafe */
+	int baud_rate;			   /* stores current baud rate in
+					      integer form */
+	int isthrottled;		   /* if throttled, discard reads */
+	char prev_status;		   /* used for TIOCMIWAIT */
+	/* we pass a pointer to this as the argument sent to
+	   cypress_set_termios old_termios */
+	struct ktermios tmp_termios; 	   /* stores the old termios settings */
+};
+
+/* function prototypes for the Cypress USB to serial device */
+static int  cypress_earthmate_port_probe(struct usb_serial_port *port);
+static int  cypress_hidcom_port_probe(struct usb_serial_port *port);
+static int  cypress_ca42v2_port_probe(struct usb_serial_port *port);
+static int  cypress_port_remove(struct usb_serial_port *port);
+static int  cypress_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void cypress_close(struct usb_serial_port *port);
+static void cypress_dtr_rts(struct usb_serial_port *port, int on);
+static int  cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count);
+static void cypress_send(struct usb_serial_port *port);
+static int  cypress_write_room(struct tty_struct *tty);
+static void cypress_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static int  cypress_tiocmget(struct tty_struct *tty);
+static int  cypress_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear);
+static int  cypress_chars_in_buffer(struct tty_struct *tty);
+static void cypress_throttle(struct tty_struct *tty);
+static void cypress_unthrottle(struct tty_struct *tty);
+static void cypress_set_dead(struct usb_serial_port *port);
+static void cypress_read_int_callback(struct urb *urb);
+static void cypress_write_int_callback(struct urb *urb);
+
+static struct usb_serial_driver cypress_earthmate_device = {
+	.driver = {
+		.owner =		THIS_MODULE,
+		.name =			"earthmate",
+	},
+	.description =			"DeLorme Earthmate USB",
+	.id_table =			id_table_earthmate,
+	.num_ports =			1,
+	.port_probe =			cypress_earthmate_port_probe,
+	.port_remove =			cypress_port_remove,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.tiocmiwait =			usb_serial_generic_tiocmiwait,
+	.chars_in_buffer =		cypress_chars_in_buffer,
+	.throttle =		 	cypress_throttle,
+	.unthrottle =			cypress_unthrottle,
+	.read_int_callback =		cypress_read_int_callback,
+	.write_int_callback =		cypress_write_int_callback,
+};
+
+static struct usb_serial_driver cypress_hidcom_device = {
+	.driver = {
+		.owner =		THIS_MODULE,
+		.name =			"cyphidcom",
+	},
+	.description =			"HID->COM RS232 Adapter",
+	.id_table =			id_table_cyphidcomrs232,
+	.num_ports =			1,
+	.port_probe =			cypress_hidcom_port_probe,
+	.port_remove =			cypress_port_remove,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.tiocmiwait =			usb_serial_generic_tiocmiwait,
+	.chars_in_buffer =		cypress_chars_in_buffer,
+	.throttle =			cypress_throttle,
+	.unthrottle =			cypress_unthrottle,
+	.read_int_callback =		cypress_read_int_callback,
+	.write_int_callback =		cypress_write_int_callback,
+};
+
+static struct usb_serial_driver cypress_ca42v2_device = {
+	.driver = {
+		.owner =		THIS_MODULE,
+		.name =			"nokiaca42v2",
+	},
+	.description =			"Nokia CA-42 V2 Adapter",
+	.id_table =			id_table_nokiaca42v2,
+	.num_ports =			1,
+	.port_probe =			cypress_ca42v2_port_probe,
+	.port_remove =			cypress_port_remove,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.tiocmiwait =			usb_serial_generic_tiocmiwait,
+	.chars_in_buffer =		cypress_chars_in_buffer,
+	.throttle =			cypress_throttle,
+	.unthrottle =			cypress_unthrottle,
+	.read_int_callback =		cypress_read_int_callback,
+	.write_int_callback =		cypress_write_int_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&cypress_earthmate_device, &cypress_hidcom_device,
+	&cypress_ca42v2_device, NULL
+};
+
+/*****************************************************************************
+ * Cypress serial helper functions
+ *****************************************************************************/
+
+/* FRWD Dongle hidcom needs to skip reset and speed checks */
+static inline bool is_frwd(struct usb_device *dev)
+{
+	return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) &&
+		(le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD));
+}
+
+static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
+{
+	struct cypress_private *priv;
+	priv = usb_get_serial_port_data(port);
+
+	if (unstable_bauds)
+		return new_rate;
+
+	/* FRWD Dongle uses 115200 bps */
+	if (is_frwd(port->serial->dev))
+		return new_rate;
+
+	/*
+	 * The general purpose firmware for the Cypress M8 allows for
+	 * a maximum speed of 57600bps (I have no idea whether DeLorme
+	 * chose to use the general purpose firmware or not), if you
+	 * need to modify this speed setting for your own project
+	 * please add your own chiptype and modify the code likewise.
+	 * The Cypress HID->COM device will work successfully up to
+	 * 115200bps (but the actual throughput is around 3kBps).
+	 */
+	if (port->serial->dev->speed == USB_SPEED_LOW) {
+		/*
+		 * Mike Isely <isely@pobox.com> 2-Feb-2008: The
+		 * Cypress app note that describes this mechanism
+		 * states the the low-speed part can't handle more
+		 * than 800 bytes/sec, in which case 4800 baud is the
+		 * safest speed for a part like that.
+		 */
+		if (new_rate > 4800) {
+			dev_dbg(&port->dev,
+				"%s - failed setting baud rate, device incapable speed %d\n",
+				__func__, new_rate);
+			return -1;
+		}
+	}
+	switch (priv->chiptype) {
+	case CT_EARTHMATE:
+		if (new_rate <= 600) {
+			/* 300 and 600 baud rates are supported under
+			 * the generic firmware, but are not used with
+			 * NMEA and SiRF protocols */
+			dev_dbg(&port->dev,
+				"%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
+				__func__, new_rate);
+			return -1;
+		}
+		break;
+	default:
+		break;
+	}
+	return new_rate;
+}
+
+
+/* This function can either set or retrieve the current serial line settings */
+static int cypress_serial_control(struct tty_struct *tty,
+	struct usb_serial_port *port, speed_t baud_rate, int data_bits,
+	int stop_bits, int parity_enable, int parity_type, int reset,
+	int cypress_request_type)
+{
+	int new_baudrate = 0, retval = 0, tries = 0;
+	struct cypress_private *priv;
+	struct device *dev = &port->dev;
+	u8 *feature_buffer;
+	const unsigned int feature_len = 5;
+	unsigned long flags;
+
+	priv = usb_get_serial_port_data(port);
+
+	if (!priv->comm_is_ok)
+		return -ENODEV;
+
+	feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL);
+	if (!feature_buffer)
+		return -ENOMEM;
+
+	switch (cypress_request_type) {
+	case CYPRESS_SET_CONFIG:
+		/* 0 means 'Hang up' so doesn't change the true bit rate */
+		new_baudrate = priv->baud_rate;
+		if (baud_rate && baud_rate != priv->baud_rate) {
+			dev_dbg(dev, "%s - baud rate is changing\n", __func__);
+			retval = analyze_baud_rate(port, baud_rate);
+			if (retval >= 0) {
+				new_baudrate = retval;
+				dev_dbg(dev, "%s - New baud rate set to %d\n",
+					__func__, new_baudrate);
+			}
+		}
+		dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
+			new_baudrate);
+
+		/* fill the feature_buffer with new configuration */
+		put_unaligned_le32(new_baudrate, feature_buffer);
+		feature_buffer[4] |= data_bits;   /* assign data bits in 2 bit space ( max 3 ) */
+		/* 1 bit gap */
+		feature_buffer[4] |= (stop_bits << 3);   /* assign stop bits in 1 bit space */
+		feature_buffer[4] |= (parity_enable << 4);   /* assign parity flag in 1 bit space */
+		feature_buffer[4] |= (parity_type << 5);   /* assign parity type in 1 bit space */
+		/* 1 bit gap */
+		feature_buffer[4] |= (reset << 7);   /* assign reset at end of byte, 1 bit space */
+
+		dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
+		dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
+			feature_buffer[0], feature_buffer[1],
+			feature_buffer[2], feature_buffer[3],
+			feature_buffer[4]);
+
+		do {
+			retval = usb_control_msg(port->serial->dev,
+					usb_sndctrlpipe(port->serial->dev, 0),
+					HID_REQ_SET_REPORT,
+					USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+					0x0300, 0, feature_buffer,
+					feature_len, 500);
+
+			if (tries++ >= 3)
+				break;
+
+		} while (retval != feature_len &&
+			 retval != -ENODEV);
+
+		if (retval != feature_len) {
+			dev_err(dev, "%s - failed sending serial line settings - %d\n",
+				__func__, retval);
+			cypress_set_dead(port);
+		} else {
+			spin_lock_irqsave(&priv->lock, flags);
+			priv->baud_rate = new_baudrate;
+			priv->current_config = feature_buffer[4];
+			spin_unlock_irqrestore(&priv->lock, flags);
+			/* If we asked for a speed change encode it */
+			if (baud_rate)
+				tty_encode_baud_rate(tty,
+					new_baudrate, new_baudrate);
+		}
+	break;
+	case CYPRESS_GET_CONFIG:
+		if (priv->get_cfg_unsafe) {
+			/* Not implemented for this device,
+			   and if we try to do it we're likely
+			   to crash the hardware. */
+			retval = -ENOTTY;
+			goto out;
+		}
+		dev_dbg(dev, "%s - retreiving serial line settings\n", __func__);
+		do {
+			retval = usb_control_msg(port->serial->dev,
+					usb_rcvctrlpipe(port->serial->dev, 0),
+					HID_REQ_GET_REPORT,
+					USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+					0x0300, 0, feature_buffer,
+					feature_len, 500);
+
+			if (tries++ >= 3)
+				break;
+		} while (retval != feature_len
+						&& retval != -ENODEV);
+
+		if (retval != feature_len) {
+			dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
+				__func__, retval);
+			cypress_set_dead(port);
+			goto out;
+		} else {
+			spin_lock_irqsave(&priv->lock, flags);
+			/* store the config in one byte, and later
+			   use bit masks to check values */
+			priv->current_config = feature_buffer[4];
+			priv->baud_rate = get_unaligned_le32(feature_buffer);
+			spin_unlock_irqrestore(&priv->lock, flags);
+		}
+	}
+	spin_lock_irqsave(&priv->lock, flags);
+	++priv->cmd_count;
+	spin_unlock_irqrestore(&priv->lock, flags);
+out:
+	kfree(feature_buffer);
+	return retval;
+} /* cypress_serial_control */
+
+
+static void cypress_set_dead(struct usb_serial_port *port)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (!priv->comm_is_ok) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+	priv->comm_is_ok = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_err(&port->dev, "cypress_m8 suspending failing port %d - "
+		"interval might be too short\n", port->port_number);
+}
+
+
+/*****************************************************************************
+ * Cypress serial driver functions
+ *****************************************************************************/
+
+
+static int cypress_generic_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct cypress_private *priv;
+
+	if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
+		dev_err(&port->dev, "required endpoint is missing\n");
+		return -ENODEV;
+	}
+
+	priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->comm_is_ok = !0;
+	spin_lock_init(&priv->lock);
+	if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	/* Skip reset for FRWD device. It is a workaound:
+	   device hangs if it receives SET_CONFIGURE in Configured
+	   state. */
+	if (!is_frwd(serial->dev))
+		usb_reset_configuration(serial->dev);
+
+	priv->cmd_ctrl = 0;
+	priv->line_control = 0;
+	priv->termios_initialized = 0;
+	priv->rx_flags = 0;
+	/* Default packet format setting is determined by packet size.
+	   Anything with a size larger then 9 must have a separate
+	   count field since the 3 bit count field is otherwise too
+	   small.  Otherwise we can use the slightly more compact
+	   format.  This is in accordance with the cypress_m8 serial
+	   converter app note. */
+	if (port->interrupt_out_size > 9)
+		priv->pkt_fmt = packet_format_1;
+	else
+		priv->pkt_fmt = packet_format_2;
+
+	if (interval > 0) {
+		priv->write_urb_interval = interval;
+		priv->read_urb_interval = interval;
+		dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n",
+			__func__, interval);
+	} else {
+		priv->write_urb_interval = port->interrupt_out_urb->interval;
+		priv->read_urb_interval = port->interrupt_in_urb->interval;
+		dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n",
+			__func__, priv->read_urb_interval,
+			priv->write_urb_interval);
+	}
+	usb_set_serial_port_data(port, priv);
+
+	port->port.drain_delay = 256;
+
+	return 0;
+}
+
+
+static int cypress_earthmate_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct cypress_private *priv;
+	int ret;
+
+	ret = cypress_generic_port_probe(port);
+	if (ret) {
+		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
+		return ret;
+	}
+
+	priv = usb_get_serial_port_data(port);
+	priv->chiptype = CT_EARTHMATE;
+	/* All Earthmate devices use the separated-count packet
+	   format!  Idiotic. */
+	priv->pkt_fmt = packet_format_1;
+	if (serial->dev->descriptor.idProduct !=
+				cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) {
+		/* The old original USB Earthmate seemed able to
+		   handle GET_CONFIG requests; everything they've
+		   produced since that time crashes if this command is
+		   attempted :-( */
+		dev_dbg(&port->dev,
+			"%s - Marking this device as unsafe for GET_CONFIG commands\n",
+			__func__);
+		priv->get_cfg_unsafe = !0;
+	}
+
+	return 0;
+}
+
+static int cypress_hidcom_port_probe(struct usb_serial_port *port)
+{
+	struct cypress_private *priv;
+	int ret;
+
+	ret = cypress_generic_port_probe(port);
+	if (ret) {
+		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
+		return ret;
+	}
+
+	priv = usb_get_serial_port_data(port);
+	priv->chiptype = CT_CYPHIDCOM;
+
+	return 0;
+}
+
+static int cypress_ca42v2_port_probe(struct usb_serial_port *port)
+{
+	struct cypress_private *priv;
+	int ret;
+
+	ret = cypress_generic_port_probe(port);
+	if (ret) {
+		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
+		return ret;
+	}
+
+	priv = usb_get_serial_port_data(port);
+	priv->chiptype = CT_CA42V2;
+
+	return 0;
+}
+
+static int cypress_port_remove(struct usb_serial_port *port)
+{
+	struct cypress_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+
+	kfifo_free(&priv->write_fifo);
+	kfree(priv);
+
+	return 0;
+}
+
+static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+	int result = 0;
+
+	if (!priv->comm_is_ok)
+		return -EIO;
+
+	/* clear halts before open */
+	usb_clear_halt(serial->dev, 0x81);
+	usb_clear_halt(serial->dev, 0x02);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* reset read/write statistics */
+	priv->bytes_in = 0;
+	priv->bytes_out = 0;
+	priv->cmd_count = 0;
+	priv->rx_flags = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Set termios */
+	cypress_send(port);
+
+	if (tty)
+		cypress_set_termios(tty, port, &priv->tmp_termios);
+
+	/* setup the port and start reading from the device */
+	usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
+		usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
+		port->interrupt_in_urb->transfer_buffer,
+		port->interrupt_in_urb->transfer_buffer_length,
+		cypress_read_int_callback, port, priv->read_urb_interval);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+
+	if (result) {
+		dev_err(&port->dev,
+			"%s - failed submitting read urb, error %d\n",
+							__func__, result);
+		cypress_set_dead(port);
+	}
+
+	return result;
+} /* cypress_open */
+
+static void cypress_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	/* drop dtr and rts */
+	spin_lock_irq(&priv->lock);
+	if (on == 0)
+		priv->line_control = 0;
+	else 
+		priv->line_control = CONTROL_DTR | CONTROL_RTS;
+	priv->cmd_ctrl = 1;
+	spin_unlock_irq(&priv->lock);
+	cypress_write(NULL, port, NULL, 0);
+}
+
+static void cypress_close(struct usb_serial_port *port)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	kfifo_reset_out(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(&port->dev, "%s - stopping urbs\n", __func__);
+	usb_kill_urb(port->interrupt_in_urb);
+	usb_kill_urb(port->interrupt_out_urb);
+
+	if (stats)
+		dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
+			priv->bytes_in, priv->bytes_out, priv->cmd_count);
+} /* cypress_close */
+
+
+static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *buf, int count)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+
+	dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count);
+
+	/* line control commands, which need to be executed immediately,
+	   are not put into the buffer for obvious reasons.
+	 */
+	if (priv->cmd_ctrl) {
+		count = 0;
+		goto finish;
+	}
+
+	if (!count)
+		return count;
+
+	count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock);
+
+finish:
+	cypress_send(port);
+
+	return count;
+} /* cypress_write */
+
+
+static void cypress_send(struct usb_serial_port *port)
+{
+	int count = 0, result, offset, actual_size;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned long flags;
+
+	if (!priv->comm_is_ok)
+		return;
+
+	dev_dbg(dev, "%s - interrupt out size is %d\n", __func__,
+		port->interrupt_out_size);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->write_urb_in_use) {
+		dev_dbg(dev, "%s - can't write, urb in use\n", __func__);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* clear buffer */
+	memset(port->interrupt_out_urb->transfer_buffer, 0,
+						port->interrupt_out_size);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	switch (priv->pkt_fmt) {
+	default:
+	case packet_format_1:
+		/* this is for the CY7C64013... */
+		offset = 2;
+		port->interrupt_out_buffer[0] = priv->line_control;
+		break;
+	case packet_format_2:
+		/* this is for the CY7C63743... */
+		offset = 1;
+		port->interrupt_out_buffer[0] = priv->line_control;
+		break;
+	}
+
+	if (priv->line_control & CONTROL_RESET)
+		priv->line_control &= ~CONTROL_RESET;
+
+	if (priv->cmd_ctrl) {
+		priv->cmd_count++;
+		dev_dbg(dev, "%s - line control command being issued\n", __func__);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		goto send;
+	} else
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+	count = kfifo_out_locked(&priv->write_fifo,
+					&port->interrupt_out_buffer[offset],
+					port->interrupt_out_size - offset,
+					&priv->lock);
+	if (count == 0)
+		return;
+
+	switch (priv->pkt_fmt) {
+	default:
+	case packet_format_1:
+		port->interrupt_out_buffer[1] = count;
+		break;
+	case packet_format_2:
+		port->interrupt_out_buffer[0] |= count;
+	}
+
+	dev_dbg(dev, "%s - count is %d\n", __func__, count);
+
+send:
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->write_urb_in_use = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->cmd_ctrl)
+		actual_size = 1;
+	else
+		actual_size = count +
+			      (priv->pkt_fmt == packet_format_1 ? 2 : 1);
+
+	usb_serial_debug_data(dev, __func__, port->interrupt_out_size,
+			      port->interrupt_out_urb->transfer_buffer);
+
+	usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
+		usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+		port->interrupt_out_buffer, port->interrupt_out_size,
+		cypress_write_int_callback, port, priv->write_urb_interval);
+	result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
+	if (result) {
+		dev_err_console(port,
+				"%s - failed submitting write urb, error %d\n",
+							__func__, result);
+		priv->write_urb_in_use = 0;
+		cypress_set_dead(port);
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->cmd_ctrl)
+		priv->cmd_ctrl = 0;
+
+	/* do not count the line control and size bytes */
+	priv->bytes_out += count;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	usb_serial_port_softint(port);
+} /* cypress_send */
+
+
+/* returns how much space is available in the soft buffer */
+static int cypress_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int room = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	room = kfifo_avail(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+
+static int cypress_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	__u8 status, control;
+	unsigned int result = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	status = priv->current_status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	result = ((control & CONTROL_DTR)        ? TIOCM_DTR : 0)
+		| ((control & CONTROL_RTS)       ? TIOCM_RTS : 0)
+		| ((status & UART_CTS)        ? TIOCM_CTS : 0)
+		| ((status & UART_DSR)        ? TIOCM_DSR : 0)
+		| ((status & UART_RI)         ? TIOCM_RI  : 0)
+		| ((status & UART_CD)         ? TIOCM_CD  : 0);
+
+	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
+
+	return result;
+}
+
+
+static int cypress_tiocmset(struct tty_struct *tty,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (set & TIOCM_RTS)
+		priv->line_control |= CONTROL_RTS;
+	if (set & TIOCM_DTR)
+		priv->line_control |= CONTROL_DTR;
+	if (clear & TIOCM_RTS)
+		priv->line_control &= ~CONTROL_RTS;
+	if (clear & TIOCM_DTR)
+		priv->line_control &= ~CONTROL_DTR;
+	priv->cmd_ctrl = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return cypress_write(tty, port, NULL, 0);
+}
+
+static void cypress_set_termios(struct tty_struct *tty,
+	struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	int data_bits, stop_bits, parity_type, parity_enable;
+	unsigned cflag, iflag;
+	unsigned long flags;
+	__u8 oldlines;
+	int linechange = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* We can't clean this one up as we don't know the device type
+	   early enough */
+	if (!priv->termios_initialized) {
+		if (priv->chiptype == CT_EARTHMATE) {
+			tty->termios = tty_std_termios;
+			tty->termios.c_cflag = B4800 | CS8 | CREAD | HUPCL |
+				CLOCAL;
+			tty->termios.c_ispeed = 4800;
+			tty->termios.c_ospeed = 4800;
+		} else if (priv->chiptype == CT_CYPHIDCOM) {
+			tty->termios = tty_std_termios;
+			tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+				CLOCAL;
+			tty->termios.c_ispeed = 9600;
+			tty->termios.c_ospeed = 9600;
+		} else if (priv->chiptype == CT_CA42V2) {
+			tty->termios = tty_std_termios;
+			tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+				CLOCAL;
+			tty->termios.c_ispeed = 9600;
+			tty->termios.c_ospeed = 9600;
+		}
+		priv->termios_initialized = 1;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Unsupported features need clearing */
+	tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
+
+	cflag = tty->termios.c_cflag;
+	iflag = tty->termios.c_iflag;
+
+	/* check if there are new settings */
+	if (old_termios) {
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->tmp_termios = tty->termios;
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	/* set number of data bits, parity, stop bits */
+	/* when parity is disabled the parity type bit is ignored */
+
+	/* 1 means 2 stop bits, 0 means 1 stop bit */
+	stop_bits = cflag & CSTOPB ? 1 : 0;
+
+	if (cflag & PARENB) {
+		parity_enable = 1;
+		/* 1 means odd parity, 0 means even parity */
+		parity_type = cflag & PARODD ? 1 : 0;
+	} else
+		parity_enable = parity_type = 0;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		data_bits = 0;
+		break;
+	case CS6:
+		data_bits = 1;
+		break;
+	case CS7:
+		data_bits = 2;
+		break;
+	case CS8:
+		data_bits = 3;
+		break;
+	default:
+		dev_err(dev, "%s - CSIZE was set, but not CS5-CS8\n", __func__);
+		data_bits = 3;
+	}
+	spin_lock_irqsave(&priv->lock, flags);
+	oldlines = priv->line_control;
+	if ((cflag & CBAUD) == B0) {
+		/* drop dtr and rts */
+		dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__);
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	} else
+		priv->line_control = (CONTROL_DTR | CONTROL_RTS);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n",
+		__func__, stop_bits, parity_enable, parity_type, data_bits);
+
+	cypress_serial_control(tty, port, tty_get_baud_rate(tty),
+			data_bits, stop_bits,
+			parity_enable, parity_type,
+			0, CYPRESS_SET_CONFIG);
+
+	/* we perform a CYPRESS_GET_CONFIG so that the current settings are
+	 * filled into the private structure this should confirm that all is
+	 * working if it returns what we just set */
+	cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
+
+	/* Here we can define custom tty settings for devices; the main tty
+	 * termios flag base comes from empeg.c */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
+		dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n");
+		/* define custom termios settings for NMEA protocol */
+
+		tty->termios.c_iflag /* input modes - */
+			&= ~(IGNBRK  /* disable ignore break */
+			| BRKINT     /* disable break causes interrupt */
+			| PARMRK     /* disable mark parity errors */
+			| ISTRIP     /* disable clear high bit of input char */
+			| INLCR      /* disable translate NL to CR */
+			| IGNCR      /* disable ignore CR */
+			| ICRNL      /* disable translate CR to NL */
+			| IXON);     /* disable enable XON/XOFF flow control */
+
+		tty->termios.c_oflag /* output modes */
+			&= ~OPOST;    /* disable postprocess output char */
+
+		tty->termios.c_lflag /* line discipline modes */
+			&= ~(ECHO     /* disable echo input characters */
+			| ECHONL      /* disable echo new line */
+			| ICANON      /* disable erase, kill, werase, and rprnt
+					 special characters */
+			| ISIG        /* disable interrupt, quit, and suspend
+					 special characters */
+			| IEXTEN);    /* disable non-POSIX special characters */
+	} /* CT_CYPHIDCOM: Application should handle this for device */
+
+	linechange = (priv->line_control != oldlines);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* if necessary, set lines */
+	if (linechange) {
+		priv->cmd_ctrl = 1;
+		cypress_write(tty, port, NULL, 0);
+	}
+} /* cypress_set_termios */
+
+
+/* returns amount of data still left in soft buffer */
+static int cypress_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int chars = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	chars = kfifo_len(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+}
+
+
+static void cypress_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+
+	spin_lock_irq(&priv->lock);
+	priv->rx_flags = THROTTLED;
+	spin_unlock_irq(&priv->lock);
+}
+
+
+static void cypress_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int actually_throttled, result;
+
+	spin_lock_irq(&priv->lock);
+	actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+	priv->rx_flags = 0;
+	spin_unlock_irq(&priv->lock);
+
+	if (!priv->comm_is_ok)
+		return;
+
+	if (actually_throttled) {
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (result) {
+			dev_err(&port->dev, "%s - failed submitting read urb, "
+					"error %d\n", __func__, result);
+			cypress_set_dead(port);
+		}
+	}
+}
+
+
+static void cypress_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &urb->dev->dev;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	char tty_flag = TTY_NORMAL;
+	int bytes = 0;
+	int result;
+	int i = 0;
+	int status = urb->status;
+
+	switch (status) {
+	case 0: /* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* precursor to disconnect so just go away */
+		return;
+	case -EPIPE:
+		/* Can't call usb_clear_halt while in_interrupt */
+		/* FALLS THROUGH */
+	default:
+		/* something ugly is going on... */
+		dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
+			__func__, status);
+		cypress_set_dead(port);
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->rx_flags & THROTTLED) {
+		dev_dbg(dev, "%s - now throttling\n", __func__);
+		priv->rx_flags |= ACTUALLY_THROTTLED;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty) {
+		dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	result = urb->actual_length;
+	switch (priv->pkt_fmt) {
+	default:
+	case packet_format_1:
+		/* This is for the CY7C64013... */
+		priv->current_status = data[0] & 0xF8;
+		bytes = data[1] + 2;
+		i = 2;
+		break;
+	case packet_format_2:
+		/* This is for the CY7C63743... */
+		priv->current_status = data[0] & 0xF8;
+		bytes = (data[0] & 0x07) + 1;
+		i = 1;
+		break;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (result < bytes) {
+		dev_dbg(dev,
+			"%s - wrong packet size - received %d bytes but packet said %d bytes\n",
+			__func__, result, bytes);
+		goto continue_read;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* check to see if status has changed */
+	if (priv->current_status != priv->prev_status) {
+		u8 delta = priv->current_status ^ priv->prev_status;
+
+		if (delta & UART_MSR_MASK) {
+			if (delta & UART_CTS)
+				port->icount.cts++;
+			if (delta & UART_DSR)
+				port->icount.dsr++;
+			if (delta & UART_RI)
+				port->icount.rng++;
+			if (delta & UART_CD)
+				port->icount.dcd++;
+
+			wake_up_interruptible(&port->port.delta_msr_wait);
+		}
+
+		priv->prev_status = priv->current_status;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* hangup, as defined in acm.c... this might be a bad place for it
+	 * though */
+	if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) {
+		dev_dbg(dev, "%s - calling hangup\n", __func__);
+		tty_hangup(tty);
+		goto continue_read;
+	}
+
+	/* There is one error bit... I'm assuming it is a parity error
+	 * indicator as the generic firmware will set this bit to 1 if a
+	 * parity error occurs.
+	 * I can not find reference to any other error events. */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->current_status & CYP_ERROR) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		tty_flag = TTY_PARITY;
+		dev_dbg(dev, "%s - Parity Error detected\n", __func__);
+	} else
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* process read if there is data other than line status */
+	if (bytes > i) {
+		tty_insert_flip_string_fixed_flag(&port->port, data + i,
+				tty_flag, bytes - i);
+		tty_flip_buffer_push(&port->port);
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* control and status byte(s) are also counted */
+	priv->bytes_in += bytes;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+continue_read:
+	tty_kref_put(tty);
+
+	/* Continue trying to always read */
+
+	if (priv->comm_is_ok) {
+		usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
+				usb_rcvintpipe(port->serial->dev,
+					port->interrupt_in_endpointAddress),
+				port->interrupt_in_urb->transfer_buffer,
+				port->interrupt_in_urb->transfer_buffer_length,
+				cypress_read_int_callback, port,
+				priv->read_urb_interval);
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+		if (result && result != -EPERM) {
+			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
+				__func__, result);
+			cypress_set_dead(port);
+		}
+	}
+} /* cypress_read_int_callback */
+
+
+static void cypress_write_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &urb->dev->dev;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		priv->write_urb_in_use = 0;
+		return;
+	case -EPIPE:
+		/* Cannot call usb_clear_halt while in_interrupt */
+		/* FALLTHROUGH */
+	default:
+		dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
+			__func__, status);
+		cypress_set_dead(port);
+		break;
+	}
+	priv->write_urb_in_use = 0;
+
+	/* send any buffered data */
+	cypress_send(port);
+}
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(stats, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(stats, "Enable statistics or not");
+module_param(interval, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(interval, "Overrides interrupt interval");
+module_param(unstable_bauds, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates");
diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h
new file mode 100644
index 0000000..35e2237
--- /dev/null
+++ b/drivers/usb/serial/cypress_m8.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef CYPRESS_M8_H
+#define CYPRESS_M8_H
+
+/*
+ * definitions and function prototypes used for the cypress USB to Serial
+ * controller
+ */
+
+/*
+ * For sending our feature buffer - controlling serial communication states.
+ * Linux HID has no support for serial devices so we do this through the driver
+ */
+#define HID_REQ_GET_REPORT	0x01
+#define HID_REQ_SET_REPORT	0x09
+
+/* List other cypress USB to Serial devices here, and add them to the id_table */
+
+/* DeLorme Earthmate USB - a GPS device */
+#define VENDOR_ID_DELORME		0x1163
+#define PRODUCT_ID_EARTHMATEUSB		0x0100
+#define PRODUCT_ID_EARTHMATEUSB_LT20	0x0200
+
+/* Cypress HID->COM RS232 Adapter */
+#define VENDOR_ID_CYPRESS		0x04b4
+#define PRODUCT_ID_CYPHIDCOM		0x5500
+
+/* FRWD Dongle - a GPS sports watch */
+#define VENDOR_ID_FRWD			0x6737
+#define PRODUCT_ID_CYPHIDCOM_FRWD	0x0001
+
+/* Powercom UPS, chip CY7C63723 */
+#define VENDOR_ID_POWERCOM		0x0d9f
+#define PRODUCT_ID_UPS			0x0002
+
+/* Nokia CA-42 USB to serial cable */
+#define VENDOR_ID_DAZZLE		0x07d0
+#define PRODUCT_ID_CA42			0x4101
+/* End of device listing */
+
+/* Used for setting / requesting serial line settings */
+#define CYPRESS_SET_CONFIG	0x01
+#define CYPRESS_GET_CONFIG	0x02
+
+/* Used for throttle control */
+#define THROTTLED		0x1
+#define ACTUALLY_THROTTLED	0x2
+
+/*
+ * chiptypes - used in case firmware differs from the generic form ... offering
+ * different baud speeds/etc.
+ */
+#define CT_EARTHMATE	0x01
+#define CT_CYPHIDCOM	0x02
+#define CT_CA42V2	0x03
+#define CT_GENERIC	0x0F
+/* End of chiptype definitions */
+
+/*
+ * RS-232 serial data communication protocol definitions.
+ *
+ * These are sent / read at byte 0 of the input/output hid reports.
+ * You can find these values defined in the CY4601 USB to Serial design notes.
+ */
+
+#define CONTROL_DTR	0x20	/* data terminal ready */
+#define CONTROL_RTS	0x10	/* request to send */
+#define CONTROL_RESET	0x08	/* sent with output report */
+
+#define UART_MSR_MASK	0xf0
+#define UART_RI		0x80	/* ring indicator */
+#define UART_CD		0x40	/* carrier detect */
+#define UART_DSR	0x20	/* data set ready */
+#define UART_CTS	0x10	/* clear to send */
+#define CYP_ERROR	0x08	/* received from input report */
+
+/* End of RS-232 protocol definitions */
+
+#endif /* CYPRESS_M8_H */
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
new file mode 100644
index 0000000..e7f244c
--- /dev/null
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -0,0 +1,1555 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+*  Digi AccelePort USB-4 and USB-2 Serial Converters
+*
+*  Copyright 2000 by Digi International
+*
+*  Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's
+*  usb-serial driver.
+*
+*  Peter Berger (pberger@brimson.com)
+*  Al Borchers (borchers@steinerpoint.com)
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/sched/signal.h>
+#include <linux/usb/serial.h>
+
+/* Defines */
+
+#define DRIVER_AUTHOR "Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>"
+#define DRIVER_DESC "Digi AccelePort USB-2/USB-4 Serial Converter driver"
+
+/* port output buffer length -- must be <= transfer buffer length - 2 */
+/* so we can be sure to send the full buffer in one urb */
+#define DIGI_OUT_BUF_SIZE		8
+
+/* port input buffer length -- must be >= transfer buffer length - 3 */
+/* so we can be sure to hold at least one full buffer from one urb */
+#define DIGI_IN_BUF_SIZE		64
+
+/* retry timeout while sleeping */
+#define DIGI_RETRY_TIMEOUT		(HZ/10)
+
+/* timeout while waiting for tty output to drain in close */
+/* this delay is used twice in close, so the total delay could */
+/* be twice this value */
+#define DIGI_CLOSE_TIMEOUT		(5*HZ)
+
+
+/* AccelePort USB Defines */
+
+/* ids */
+#define DIGI_VENDOR_ID			0x05c5
+#define DIGI_2_ID			0x0002	/* USB-2 */
+#define DIGI_4_ID			0x0004	/* USB-4 */
+
+/* commands
+ * "INB": can be used on the in-band endpoint
+ * "OOB": can be used on the out-of-band endpoint
+ */
+#define DIGI_CMD_SET_BAUD_RATE			0	/* INB, OOB */
+#define DIGI_CMD_SET_WORD_SIZE			1	/* INB, OOB */
+#define DIGI_CMD_SET_PARITY			2	/* INB, OOB */
+#define DIGI_CMD_SET_STOP_BITS			3	/* INB, OOB */
+#define DIGI_CMD_SET_INPUT_FLOW_CONTROL		4	/* INB, OOB */
+#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL	5	/* INB, OOB */
+#define DIGI_CMD_SET_DTR_SIGNAL			6	/* INB, OOB */
+#define DIGI_CMD_SET_RTS_SIGNAL			7	/* INB, OOB */
+#define DIGI_CMD_READ_INPUT_SIGNALS		8	/*      OOB */
+#define DIGI_CMD_IFLUSH_FIFO			9	/*      OOB */
+#define DIGI_CMD_RECEIVE_ENABLE			10	/* INB, OOB */
+#define DIGI_CMD_BREAK_CONTROL			11	/* INB, OOB */
+#define DIGI_CMD_LOCAL_LOOPBACK			12	/* INB, OOB */
+#define DIGI_CMD_TRANSMIT_IDLE			13	/* INB, OOB */
+#define DIGI_CMD_READ_UART_REGISTER		14	/*      OOB */
+#define DIGI_CMD_WRITE_UART_REGISTER		15	/* INB, OOB */
+#define DIGI_CMD_AND_UART_REGISTER		16	/* INB, OOB */
+#define DIGI_CMD_OR_UART_REGISTER		17	/* INB, OOB */
+#define DIGI_CMD_SEND_DATA			18	/* INB      */
+#define DIGI_CMD_RECEIVE_DATA			19	/* INB      */
+#define DIGI_CMD_RECEIVE_DISABLE		20	/* INB      */
+#define DIGI_CMD_GET_PORT_TYPE			21	/*      OOB */
+
+/* baud rates */
+#define DIGI_BAUD_50				0
+#define DIGI_BAUD_75				1
+#define DIGI_BAUD_110				2
+#define DIGI_BAUD_150				3
+#define DIGI_BAUD_200				4
+#define DIGI_BAUD_300				5
+#define DIGI_BAUD_600				6
+#define DIGI_BAUD_1200				7
+#define DIGI_BAUD_1800				8
+#define DIGI_BAUD_2400				9
+#define DIGI_BAUD_4800				10
+#define DIGI_BAUD_7200				11
+#define DIGI_BAUD_9600				12
+#define DIGI_BAUD_14400				13
+#define DIGI_BAUD_19200				14
+#define DIGI_BAUD_28800				15
+#define DIGI_BAUD_38400				16
+#define DIGI_BAUD_57600				17
+#define DIGI_BAUD_76800				18
+#define DIGI_BAUD_115200			19
+#define DIGI_BAUD_153600			20
+#define DIGI_BAUD_230400			21
+#define DIGI_BAUD_460800			22
+
+/* arguments */
+#define DIGI_WORD_SIZE_5			0
+#define DIGI_WORD_SIZE_6			1
+#define DIGI_WORD_SIZE_7			2
+#define DIGI_WORD_SIZE_8			3
+
+#define DIGI_PARITY_NONE			0
+#define DIGI_PARITY_ODD				1
+#define DIGI_PARITY_EVEN			2
+#define DIGI_PARITY_MARK			3
+#define DIGI_PARITY_SPACE			4
+
+#define DIGI_STOP_BITS_1			0
+#define DIGI_STOP_BITS_2			1
+
+#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF	1
+#define DIGI_INPUT_FLOW_CONTROL_RTS		2
+#define DIGI_INPUT_FLOW_CONTROL_DTR		4
+
+#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF	1
+#define DIGI_OUTPUT_FLOW_CONTROL_CTS		2
+#define DIGI_OUTPUT_FLOW_CONTROL_DSR		4
+
+#define DIGI_DTR_INACTIVE			0
+#define DIGI_DTR_ACTIVE				1
+#define DIGI_DTR_INPUT_FLOW_CONTROL		2
+
+#define DIGI_RTS_INACTIVE			0
+#define DIGI_RTS_ACTIVE				1
+#define DIGI_RTS_INPUT_FLOW_CONTROL		2
+#define DIGI_RTS_TOGGLE				3
+
+#define DIGI_FLUSH_TX				1
+#define DIGI_FLUSH_RX				2
+#define DIGI_RESUME_TX				4 /* clears xoff condition */
+
+#define DIGI_TRANSMIT_NOT_IDLE			0
+#define DIGI_TRANSMIT_IDLE			1
+
+#define DIGI_DISABLE				0
+#define DIGI_ENABLE				1
+
+#define DIGI_DEASSERT				0
+#define DIGI_ASSERT				1
+
+/* in band status codes */
+#define DIGI_OVERRUN_ERROR			4
+#define DIGI_PARITY_ERROR			8
+#define DIGI_FRAMING_ERROR			16
+#define DIGI_BREAK_ERROR			32
+
+/* out of band status */
+#define DIGI_NO_ERROR				0
+#define DIGI_BAD_FIRST_PARAMETER		1
+#define DIGI_BAD_SECOND_PARAMETER		2
+#define DIGI_INVALID_LINE			3
+#define DIGI_INVALID_OPCODE			4
+
+/* input signals */
+#define DIGI_READ_INPUT_SIGNALS_SLOT		1
+#define DIGI_READ_INPUT_SIGNALS_ERR		2
+#define DIGI_READ_INPUT_SIGNALS_BUSY		4
+#define DIGI_READ_INPUT_SIGNALS_PE		8
+#define DIGI_READ_INPUT_SIGNALS_CTS		16
+#define DIGI_READ_INPUT_SIGNALS_DSR		32
+#define DIGI_READ_INPUT_SIGNALS_RI		64
+#define DIGI_READ_INPUT_SIGNALS_DCD		128
+
+
+/* Structures */
+
+struct digi_serial {
+	spinlock_t ds_serial_lock;
+	struct usb_serial_port *ds_oob_port;	/* out-of-band port */
+	int ds_oob_port_num;			/* index of out-of-band port */
+	int ds_device_started;
+};
+
+struct digi_port {
+	spinlock_t dp_port_lock;
+	int dp_port_num;
+	int dp_out_buf_len;
+	unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE];
+	int dp_write_urb_in_use;
+	unsigned int dp_modem_signals;
+	int dp_transmit_idle;
+	wait_queue_head_t dp_transmit_idle_wait;
+	int dp_throttled;
+	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;
+	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,
+	unsigned char *buf, int count, unsigned long timeout);
+static int digi_set_modem_signals(struct usb_serial_port *port,
+	unsigned int modem_signals, int interruptible);
+static int digi_transmit_idle(struct usb_serial_port *port,
+	unsigned long timeout);
+static void digi_rx_throttle(struct tty_struct *tty);
+static void digi_rx_unthrottle(struct tty_struct *tty);
+static void digi_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios);
+static void digi_break_ctl(struct tty_struct *tty, int break_state);
+static int digi_tiocmget(struct tty_struct *tty);
+static int digi_tiocmset(struct tty_struct *tty, unsigned int set,
+		unsigned int clear);
+static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
+		const unsigned char *buf, int count);
+static void digi_write_bulk_callback(struct urb *urb);
+static int digi_write_room(struct tty_struct *tty);
+static int digi_chars_in_buffer(struct tty_struct *tty);
+static int digi_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void digi_close(struct usb_serial_port *port);
+static void digi_dtr_rts(struct usb_serial_port *port, int on);
+static int digi_startup_device(struct usb_serial *serial);
+static int digi_startup(struct usb_serial *serial);
+static void digi_disconnect(struct usb_serial *serial);
+static void digi_release(struct usb_serial *serial);
+static int digi_port_probe(struct usb_serial_port *port);
+static int digi_port_remove(struct usb_serial_port *port);
+static void digi_read_bulk_callback(struct urb *urb);
+static int digi_read_inb_callback(struct urb *urb);
+static int digi_read_oob_callback(struct urb *urb);
+
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
+	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_2[] = {
+	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_4[] = {
+	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+/* device info needed for the Digi serial converter */
+
+static struct usb_serial_driver digi_acceleport_2_device = {
+	.driver = {
+		.owner =		THIS_MODULE,
+		.name =			"digi_2",
+	},
+	.description =			"Digi 2 port USB adapter",
+	.id_table =			id_table_2,
+	.num_ports =			3,
+	.num_bulk_in =			4,
+	.num_bulk_out =			4,
+	.open =				digi_open,
+	.close =			digi_close,
+	.dtr_rts =			digi_dtr_rts,
+	.write =			digi_write,
+	.write_room =			digi_write_room,
+	.write_bulk_callback = 		digi_write_bulk_callback,
+	.read_bulk_callback =		digi_read_bulk_callback,
+	.chars_in_buffer =		digi_chars_in_buffer,
+	.throttle =			digi_rx_throttle,
+	.unthrottle =			digi_rx_unthrottle,
+	.set_termios =			digi_set_termios,
+	.break_ctl =			digi_break_ctl,
+	.tiocmget =			digi_tiocmget,
+	.tiocmset =			digi_tiocmset,
+	.attach =			digi_startup,
+	.disconnect =			digi_disconnect,
+	.release =			digi_release,
+	.port_probe =			digi_port_probe,
+	.port_remove =			digi_port_remove,
+};
+
+static struct usb_serial_driver digi_acceleport_4_device = {
+	.driver = {
+		.owner =		THIS_MODULE,
+		.name =			"digi_4",
+	},
+	.description =			"Digi 4 port USB adapter",
+	.id_table =			id_table_4,
+	.num_ports =			4,
+	.num_bulk_in =			5,
+	.num_bulk_out =			5,
+	.open =				digi_open,
+	.close =			digi_close,
+	.write =			digi_write,
+	.write_room =			digi_write_room,
+	.write_bulk_callback = 		digi_write_bulk_callback,
+	.read_bulk_callback =		digi_read_bulk_callback,
+	.chars_in_buffer =		digi_chars_in_buffer,
+	.throttle =			digi_rx_throttle,
+	.unthrottle =			digi_rx_unthrottle,
+	.set_termios =			digi_set_termios,
+	.break_ctl =			digi_break_ctl,
+	.tiocmget =			digi_tiocmget,
+	.tiocmset =			digi_tiocmset,
+	.attach =			digi_startup,
+	.disconnect =			digi_disconnect,
+	.release =			digi_release,
+	.port_probe =			digi_port_probe,
+	.port_remove =			digi_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&digi_acceleport_2_device, &digi_acceleport_4_device, NULL
+};
+
+/* Functions */
+
+/*
+ *  Cond Wait Interruptible Timeout Irqrestore
+ *
+ *  Do spin_unlock_irqrestore and interruptible_sleep_on_timeout
+ *  so that wake ups are not lost if they occur between the unlock
+ *  and the sleep.  In other words, spin_unlock_irqrestore and
+ *  interruptible_sleep_on_timeout are "atomic" with respect to
+ *  wake ups.  This is used to implement condition variables.
+ *
+ *  interruptible_sleep_on_timeout is deprecated and has been replaced
+ *  with the equivalent code.
+ */
+
+static long cond_wait_interruptible_timeout_irqrestore(
+	wait_queue_head_t *q, long timeout,
+	spinlock_t *lock, unsigned long flags)
+__releases(lock)
+{
+	DEFINE_WAIT(wait);
+
+	prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE);
+	spin_unlock_irqrestore(lock, flags);
+	timeout = schedule_timeout(timeout);
+	finish_wait(q, &wait);
+
+	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
+ *
+ *  Write commands on the out of band port.  Commands are 4
+ *  bytes each, multiple commands can be sent at once, and
+ *  no command will be split across USB packets.  Returns 0
+ *  if successful, -EINTR if interrupted while sleeping and
+ *  the interruptible flag is true, or a negative error
+ *  returned by usb_submit_urb.
+ */
+
+static int digi_write_oob_command(struct usb_serial_port *port,
+	unsigned char *buf, int count, int interruptible)
+{
+	int ret = 0;
+	int len;
+	struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
+	struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
+	unsigned long flags = 0;
+
+	dev_dbg(&port->dev,
+		"digi_write_oob_command: TOP: port=%d, count=%d\n",
+		oob_priv->dp_port_num, count);
+
+	spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
+	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->dp_port_lock, flags);
+			if (interruptible && signal_pending(current))
+				return -EINTR;
+			spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
+		}
+
+		/* len must be a multiple of 4, so commands are not split */
+		len = min(count, oob_port->bulk_out_size);
+		if (len > 4)
+			len &= ~3;
+		memcpy(oob_port->write_urb->transfer_buffer, buf, len);
+		oob_port->write_urb->transfer_buffer_length = len;
+		ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
+		if (ret == 0) {
+			oob_priv->dp_write_urb_in_use = 1;
+			count -= len;
+			buf += len;
+		}
+	}
+	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
+	if (ret)
+		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n",
+			__func__, ret);
+	return ret;
+
+}
+
+
+/*
+ *  Digi Write In Band Command
+ *
+ *  Write commands on the given port.  Commands are 4
+ *  bytes each, multiple commands can be sent at once, and
+ *  no command will be split across USB packets.  If timeout
+ *  is non-zero, write in band command will return after
+ *  waiting unsuccessfully for the URB status to clear for
+ *  timeout ticks.  Returns 0 if successful, or a negative
+ *  error returned by digi_write.
+ */
+
+static int digi_write_inb_command(struct usb_serial_port *port,
+	unsigned char *buf, int count, unsigned long timeout)
+{
+	int ret = 0;
+	int len;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned char *data = port->write_urb->transfer_buffer;
+	unsigned long flags = 0;
+
+	dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
+		priv->dp_port_num, count);
+
+	if (timeout)
+		timeout += jiffies;
+	else
+		timeout = ULONG_MAX;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	while (count > 0 && ret == 0) {
+		while (priv->dp_write_urb_in_use &&
+		       time_before(jiffies, timeout)) {
+			cond_wait_interruptible_timeout_irqrestore(
+				&port->write_wait, DIGI_RETRY_TIMEOUT,
+				&priv->dp_port_lock, flags);
+			if (signal_pending(current))
+				return -EINTR;
+			spin_lock_irqsave(&priv->dp_port_lock, flags);
+		}
+
+		/* len must be a multiple of 4 and small enough to */
+		/* guarantee the write will send buffered data first, */
+		/* so commands are in order with data and not split */
+		len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
+		if (len > 4)
+			len &= ~3;
+
+		/* write any buffered data first */
+		if (priv->dp_out_buf_len > 0) {
+			data[0] = DIGI_CMD_SEND_DATA;
+			data[1] = priv->dp_out_buf_len;
+			memcpy(data + 2, priv->dp_out_buf,
+				priv->dp_out_buf_len);
+			memcpy(data + 2 + priv->dp_out_buf_len, buf, len);
+			port->write_urb->transfer_buffer_length
+				= priv->dp_out_buf_len + 2 + len;
+		} else {
+			memcpy(data, buf, len);
+			port->write_urb->transfer_buffer_length = len;
+		}
+
+		ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (ret == 0) {
+			priv->dp_write_urb_in_use = 1;
+			priv->dp_out_buf_len = 0;
+			count -= len;
+			buf += len;
+		}
+
+	}
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
+	if (ret)
+		dev_err(&port->dev,
+			"%s: usb_submit_urb failed, ret=%d, port=%d\n",
+			__func__, ret, priv->dp_port_num);
+	return ret;
+}
+
+
+/*
+ *  Digi Set Modem Signals
+ *
+ *  Sets or clears DTR and RTS on the port, according to the
+ *  modem_signals argument.  Use TIOCM_DTR and TIOCM_RTS flags
+ *  for the modem_signals argument.  Returns 0 if successful,
+ *  -EINTR if interrupted while sleeping, or a non-zero error
+ *  returned by usb_submit_urb.
+ */
+
+static int digi_set_modem_signals(struct usb_serial_port *port,
+	unsigned int modem_signals, int interruptible)
+{
+
+	int ret;
+	struct digi_port *port_priv = usb_get_serial_port_data(port);
+	struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
+	struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
+	unsigned char *data = oob_port->write_urb->transfer_buffer;
+	unsigned long flags = 0;
+
+
+	dev_dbg(&port->dev,
+		"digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n",
+		port_priv->dp_port_num, modem_signals);
+
+	spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
+	spin_lock(&port_priv->dp_port_lock);
+
+	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->dp_port_lock, flags);
+		if (interruptible && signal_pending(current))
+			return -EINTR;
+		spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
+		spin_lock(&port_priv->dp_port_lock);
+	}
+	data[0] = DIGI_CMD_SET_DTR_SIGNAL;
+	data[1] = port_priv->dp_port_num;
+	data[2] = (modem_signals & TIOCM_DTR) ?
+					DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE;
+	data[3] = 0;
+	data[4] = DIGI_CMD_SET_RTS_SIGNAL;
+	data[5] = port_priv->dp_port_num;
+	data[6] = (modem_signals & TIOCM_RTS) ?
+					DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE;
+	data[7] = 0;
+
+	oob_port->write_urb->transfer_buffer_length = 8;
+
+	ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
+	if (ret == 0) {
+		oob_priv->dp_write_urb_in_use = 1;
+		port_priv->dp_modem_signals =
+			(port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS))
+			| (modem_signals&(TIOCM_DTR|TIOCM_RTS));
+	}
+	spin_unlock(&port_priv->dp_port_lock);
+	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
+	if (ret)
+		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n",
+			__func__, ret);
+	return ret;
+}
+
+/*
+ *  Digi Transmit Idle
+ *
+ *  Digi transmit idle waits, up to timeout ticks, for the transmitter
+ *  to go idle.  It returns 0 if successful or a negative error.
+ *
+ *  There are race conditions here if more than one process is calling
+ *  digi_transmit_idle on the same port at the same time.  However, this
+ *  is only called from close, and only one process can be in close on a
+ *  port at a time, so its ok.
+ */
+
+static int digi_transmit_idle(struct usb_serial_port *port,
+	unsigned long timeout)
+{
+	int ret;
+	unsigned char buf[2];
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	priv->dp_transmit_idle = 0;
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
+	buf[0] = DIGI_CMD_TRANSMIT_IDLE;
+	buf[1] = 0;
+
+	timeout += jiffies;
+
+	ret = digi_write_inb_command(port, buf, 2, timeout - jiffies);
+	if (ret != 0)
+		return ret;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+
+	while (time_before(jiffies, timeout) && !priv->dp_transmit_idle) {
+		cond_wait_interruptible_timeout_irqrestore(
+			&priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT,
+			&priv->dp_port_lock, flags);
+		if (signal_pending(current))
+			return -EINTR;
+		spin_lock_irqsave(&priv->dp_port_lock, flags);
+	}
+	priv->dp_transmit_idle = 0;
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+	return 0;
+
+}
+
+
+static void digi_rx_throttle(struct tty_struct *tty)
+{
+	unsigned long flags;
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+
+	/* stop receiving characters by not resubmitting the read urb */
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	priv->dp_throttled = 1;
+	priv->dp_throttle_restart = 0;
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+}
+
+
+static void digi_rx_unthrottle(struct tty_struct *tty)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+
+	/* restart read chain */
+	if (priv->dp_throttle_restart)
+		ret = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+
+	/* turn throttle off */
+	priv->dp_throttled = 0;
+	priv->dp_throttle_restart = 0;
+
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
+	if (ret)
+		dev_err(&port->dev,
+			"%s: usb_submit_urb failed, ret=%d, port=%d\n",
+			__func__, ret, priv->dp_port_num);
+}
+
+
+static void digi_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned int iflag = tty->termios.c_iflag;
+	unsigned int cflag = tty->termios.c_cflag;
+	unsigned int old_iflag = old_termios->c_iflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	unsigned char buf[32];
+	unsigned int modem_signals;
+	int arg, ret;
+	int i = 0;
+	speed_t baud;
+
+	dev_dbg(dev,
+		"digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x\n",
+		priv->dp_port_num, iflag, old_iflag, cflag, old_cflag);
+
+	/* set baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (baud != tty_termios_baud_rate(old_termios)) {
+		arg = -1;
+
+		/* reassert DTR and (maybe) RTS on transition from B0 */
+		if ((old_cflag & CBAUD) == B0) {
+			/* don't set RTS if using hardware flow control */
+			/* and throttling input */
+			modem_signals = TIOCM_DTR;
+			if (!C_CRTSCTS(tty) || !tty_throttled(tty))
+				modem_signals |= TIOCM_RTS;
+			digi_set_modem_signals(port, modem_signals, 1);
+		}
+		switch (baud) {
+		/* drop DTR and RTS on transition to B0 */
+		case 0: digi_set_modem_signals(port, 0, 1); break;
+		case 50: arg = DIGI_BAUD_50; break;
+		case 75: arg = DIGI_BAUD_75; break;
+		case 110: arg = DIGI_BAUD_110; break;
+		case 150: arg = DIGI_BAUD_150; break;
+		case 200: arg = DIGI_BAUD_200; break;
+		case 300: arg = DIGI_BAUD_300; break;
+		case 600: arg = DIGI_BAUD_600; break;
+		case 1200: arg = DIGI_BAUD_1200; break;
+		case 1800: arg = DIGI_BAUD_1800; break;
+		case 2400: arg = DIGI_BAUD_2400; break;
+		case 4800: arg = DIGI_BAUD_4800; break;
+		case 9600: arg = DIGI_BAUD_9600; break;
+		case 19200: arg = DIGI_BAUD_19200; break;
+		case 38400: arg = DIGI_BAUD_38400; break;
+		case 57600: arg = DIGI_BAUD_57600; break;
+		case 115200: arg = DIGI_BAUD_115200; break;
+		case 230400: arg = DIGI_BAUD_230400; break;
+		case 460800: arg = DIGI_BAUD_460800; break;
+		default:
+			arg = DIGI_BAUD_9600;
+			baud = 9600;
+			break;
+		}
+		if (arg != -1) {
+			buf[i++] = DIGI_CMD_SET_BAUD_RATE;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = arg;
+			buf[i++] = 0;
+		}
+	}
+	/* set parity */
+	tty->termios.c_cflag &= ~CMSPAR;
+
+	if ((cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD))) {
+		if (cflag&PARENB) {
+			if (cflag&PARODD)
+				arg = DIGI_PARITY_ODD;
+			else
+				arg = DIGI_PARITY_EVEN;
+		} else {
+			arg = DIGI_PARITY_NONE;
+		}
+		buf[i++] = DIGI_CMD_SET_PARITY;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+	}
+	/* set word size */
+	if ((cflag&CSIZE) != (old_cflag&CSIZE)) {
+		arg = -1;
+		switch (cflag&CSIZE) {
+		case CS5: arg = DIGI_WORD_SIZE_5; break;
+		case CS6: arg = DIGI_WORD_SIZE_6; break;
+		case CS7: arg = DIGI_WORD_SIZE_7; break;
+		case CS8: arg = DIGI_WORD_SIZE_8; break;
+		default:
+			dev_dbg(dev,
+				"digi_set_termios: can't handle word size %d\n",
+				(cflag&CSIZE));
+			break;
+		}
+
+		if (arg != -1) {
+			buf[i++] = DIGI_CMD_SET_WORD_SIZE;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = arg;
+			buf[i++] = 0;
+		}
+
+	}
+
+	/* set stop bits */
+	if ((cflag&CSTOPB) != (old_cflag&CSTOPB)) {
+
+		if ((cflag&CSTOPB))
+			arg = DIGI_STOP_BITS_2;
+		else
+			arg = DIGI_STOP_BITS_1;
+
+		buf[i++] = DIGI_CMD_SET_STOP_BITS;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	/* set input flow control */
+	if ((iflag&IXOFF) != (old_iflag&IXOFF)
+	    || (cflag&CRTSCTS) != (old_cflag&CRTSCTS)) {
+		arg = 0;
+		if (iflag&IXOFF)
+			arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
+		else
+			arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
+
+		if (cflag&CRTSCTS) {
+			arg |= DIGI_INPUT_FLOW_CONTROL_RTS;
+
+			/* On USB-4 it is necessary to assert RTS prior */
+			/* to selecting RTS input flow control.  */
+			buf[i++] = DIGI_CMD_SET_RTS_SIGNAL;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = DIGI_RTS_ACTIVE;
+			buf[i++] = 0;
+
+		} else {
+			arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
+		}
+		buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+	}
+
+	/* set output flow control */
+	if ((iflag & IXON) != (old_iflag & IXON)
+	    || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+		arg = 0;
+		if (iflag & IXON)
+			arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
+		else
+			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
+
+		if (cflag & CRTSCTS) {
+			arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS;
+		} else {
+			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
+		}
+
+		buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+	}
+
+	/* set receive enable/disable */
+	if ((cflag & CREAD) != (old_cflag & CREAD)) {
+		if (cflag & CREAD)
+			arg = DIGI_ENABLE;
+		else
+			arg = DIGI_DISABLE;
+
+		buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+	}
+	ret = digi_write_oob_command(port, buf, i, 1);
+	if (ret != 0)
+		dev_dbg(dev, "digi_set_termios: write oob failed, ret=%d\n", ret);
+	tty_encode_baud_rate(tty, baud, baud);
+}
+
+
+static void digi_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned char buf[4];
+
+	buf[0] = DIGI_CMD_BREAK_CONTROL;
+	buf[1] = 2;				/* length */
+	buf[2] = break_state ? 1 : 0;
+	buf[3] = 0;				/* pad */
+	digi_write_inb_command(port, buf, 4, 0);
+}
+
+
+static int digi_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	val = priv->dp_modem_signals;
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+	return val;
+}
+
+
+static int digi_tiocmset(struct tty_struct *tty,
+					unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	val = (priv->dp_modem_signals & ~clear) | set;
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+	return digi_set_modem_signals(port, val, 1);
+}
+
+
+static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *buf, int count)
+{
+
+	int ret, data_len, new_len;
+	struct digi_port *priv = usb_get_serial_port_data(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());
+
+	/* copy user data (which can sleep) before getting spin lock */
+	count = min(count, port->bulk_out_size-2);
+	count = min(64, count);
+
+	/* be sure only one write proceeds at a time */
+	/* there are races on the port private buffer */
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+
+	/* wait for urb status clear to submit another urb */
+	if (priv->dp_write_urb_in_use) {
+		/* buffer data if count is 1 (probably put_char) if possible */
+		if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) {
+			priv->dp_out_buf[priv->dp_out_buf_len++] = *buf;
+			new_len = 1;
+		} else {
+			new_len = 0;
+		}
+		spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+		return new_len;
+	}
+
+	/* allow space for any buffered data and for new data, up to */
+	/* transfer buffer size - 2 (for command and length bytes) */
+	new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
+	data_len = new_len + priv->dp_out_buf_len;
+
+	if (data_len == 0) {
+		spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+		return 0;
+	}
+
+	port->write_urb->transfer_buffer_length = data_len+2;
+
+	*data++ = DIGI_CMD_SEND_DATA;
+	*data++ = data_len;
+
+	/* copy in buffered data first */
+	memcpy(data, priv->dp_out_buf, priv->dp_out_buf_len);
+	data += priv->dp_out_buf_len;
+
+	/* copy in new data */
+	memcpy(data, buf, new_len);
+
+	ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	if (ret == 0) {
+		priv->dp_write_urb_in_use = 1;
+		ret = new_len;
+		priv->dp_out_buf_len = 0;
+	}
+
+	/* return length of new data written, or error */
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+	if (ret < 0)
+		dev_err_console(port,
+			"%s: usb_submit_urb failed, ret=%d, port=%d\n",
+			__func__, ret, priv->dp_port_num);
+	dev_dbg(&port->dev, "digi_write: returning %d\n", ret);
+	return ret;
+
+}
+
+static void digi_write_bulk_callback(struct urb *urb)
+{
+
+	struct usb_serial_port *port = urb->context;
+	struct usb_serial *serial;
+	struct digi_port *priv;
+	struct digi_serial *serial_priv;
+	unsigned long flags;
+	int ret = 0;
+	int status = urb->status;
+
+	/* port and serial sanity check */
+	if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
+		pr_err("%s: port or port->private is NULL, status=%d\n",
+			__func__, status);
+		return;
+	}
+	serial = port->serial;
+	if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) {
+		dev_err(&port->dev,
+			"%s: serial or serial->private is NULL, status=%d\n",
+			__func__, status);
+		return;
+	}
+
+	/* handle oob callback */
+	if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
+		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);
+		spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+		return;
+	}
+
+	/* try to send any buffered data on this port */
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+	priv->dp_write_urb_in_use = 0;
+	if (priv->dp_out_buf_len > 0) {
+		*((unsigned char *)(port->write_urb->transfer_buffer))
+			= (unsigned char)DIGI_CMD_SEND_DATA;
+		*((unsigned char *)(port->write_urb->transfer_buffer) + 1)
+			= (unsigned char)priv->dp_out_buf_len;
+		port->write_urb->transfer_buffer_length =
+						priv->dp_out_buf_len + 2;
+		memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf,
+			priv->dp_out_buf_len);
+		ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (ret == 0) {
+			priv->dp_write_urb_in_use = 1;
+			priv->dp_out_buf_len = 0;
+		}
+	}
+	/* 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);
+}
+
+static int digi_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	int room;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+
+	if (priv->dp_write_urb_in_use)
+		room = 0;
+	else
+		room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
+
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+	dev_dbg(&port->dev, "digi_write_room: port=%d, room=%d\n", priv->dp_port_num, room);
+	return room;
+
+}
+
+static int digi_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+
+	if (priv->dp_write_urb_in_use) {
+		dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
+			priv->dp_port_num, port->bulk_out_size - 2);
+		/* return(port->bulk_out_size - 2); */
+		return 256;
+	} else {
+		dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
+			priv->dp_port_num, priv->dp_out_buf_len);
+		return priv->dp_out_buf_len;
+	}
+
+}
+
+static void digi_dtr_rts(struct usb_serial_port *port, int on)
+{
+	/* Adjust DTR and RTS */
+	digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1);
+}
+
+static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int ret;
+	unsigned char buf[32];
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	struct ktermios not_termios;
+
+	/* be sure the device is started up */
+	if (digi_startup_device(port->serial) != 0)
+		return -ENXIO;
+
+	/* read modem signals automatically whenever they change */
+	buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
+	buf[1] = priv->dp_port_num;
+	buf[2] = DIGI_ENABLE;
+	buf[3] = 0;
+
+	/* flush fifos */
+	buf[4] = DIGI_CMD_IFLUSH_FIFO;
+	buf[5] = priv->dp_port_num;
+	buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+	buf[7] = 0;
+
+	ret = digi_write_oob_command(port, buf, 8, 1);
+	if (ret != 0)
+		dev_dbg(&port->dev, "digi_open: write oob failed, ret=%d\n", ret);
+
+	/* set termios settings */
+	if (tty) {
+		not_termios.c_cflag = ~tty->termios.c_cflag;
+		not_termios.c_iflag = ~tty->termios.c_iflag;
+		digi_set_termios(tty, port, &not_termios);
+	}
+	return 0;
+}
+
+
+static void digi_close(struct usb_serial_port *port)
+{
+	DEFINE_WAIT(wait);
+	int ret;
+	unsigned char buf[32];
+	struct digi_port *priv = usb_get_serial_port_data(port);
+
+	mutex_lock(&port->serial->disc_mutex);
+	/* if disconnected, just clear flags */
+	if (port->serial->disconnected)
+		goto exit;
+
+	/* FIXME: Transmit idle belongs in the wait_unti_sent path */
+	digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
+
+	/* disable input flow control */
+	buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+	buf[1] = priv->dp_port_num;
+	buf[2] = DIGI_DISABLE;
+	buf[3] = 0;
+
+	/* disable output flow control */
+	buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+	buf[5] = priv->dp_port_num;
+	buf[6] = DIGI_DISABLE;
+	buf[7] = 0;
+
+	/* disable reading modem signals automatically */
+	buf[8] = DIGI_CMD_READ_INPUT_SIGNALS;
+	buf[9] = priv->dp_port_num;
+	buf[10] = DIGI_DISABLE;
+	buf[11] = 0;
+
+	/* disable receive */
+	buf[12] = DIGI_CMD_RECEIVE_ENABLE;
+	buf[13] = priv->dp_port_num;
+	buf[14] = DIGI_DISABLE;
+	buf[15] = 0;
+
+	/* flush fifos */
+	buf[16] = DIGI_CMD_IFLUSH_FIFO;
+	buf[17] = priv->dp_port_num;
+	buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+	buf[19] = 0;
+
+	ret = digi_write_oob_command(port, buf, 20, 0);
+	if (ret != 0)
+		dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d\n",
+									ret);
+	/* wait for final commands on oob port to complete */
+	prepare_to_wait(&priv->dp_flush_wait, &wait,
+			TASK_INTERRUPTIBLE);
+	schedule_timeout(DIGI_CLOSE_TIMEOUT);
+	finish_wait(&priv->dp_flush_wait, &wait);
+
+	/* shutdown any outstanding bulk writes */
+	usb_kill_urb(port->write_urb);
+exit:
+	spin_lock_irq(&priv->dp_port_lock);
+	priv->dp_write_urb_in_use = 0;
+	wake_up_interruptible(&priv->dp_close_wait);
+	spin_unlock_irq(&priv->dp_port_lock);
+	mutex_unlock(&port->serial->disc_mutex);
+}
+
+
+/*
+ *  Digi Startup Device
+ *
+ *  Starts reads on all ports.  Must be called AFTER startup, with
+ *  urbs initialized.  Returns 0 if successful, non-zero error otherwise.
+ */
+
+static int digi_startup_device(struct usb_serial *serial)
+{
+	int i, ret = 0;
+	struct digi_serial *serial_priv = usb_get_serial_data(serial);
+	struct usb_serial_port *port;
+
+	/* be sure this happens exactly once */
+	spin_lock(&serial_priv->ds_serial_lock);
+	if (serial_priv->ds_device_started) {
+		spin_unlock(&serial_priv->ds_serial_lock);
+		return 0;
+	}
+	serial_priv->ds_device_started = 1;
+	spin_unlock(&serial_priv->ds_serial_lock);
+
+	/* start reading from each bulk in endpoint for the device */
+	/* set USB_DISABLE_SPD flag for write bulk urbs */
+	for (i = 0; i < serial->type->num_ports + 1; i++) {
+		port = serial->port[i];
+		ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
+		if (ret != 0) {
+			dev_err(&port->dev,
+				"%s: usb_submit_urb failed, ret=%d, port=%d\n",
+				__func__, ret, i);
+			break;
+		}
+	}
+	return ret;
+}
+
+static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
+{
+	struct digi_port *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->dp_port_lock);
+	priv->dp_port_num = 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);
+	priv->dp_port = port;
+
+	init_waitqueue_head(&port->write_wait);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int digi_startup(struct usb_serial *serial)
+{
+	struct digi_serial *serial_priv;
+	int ret;
+
+	serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
+	if (!serial_priv)
+		return -ENOMEM;
+
+	spin_lock_init(&serial_priv->ds_serial_lock);
+	serial_priv->ds_oob_port_num = serial->type->num_ports;
+	serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
+
+	ret = digi_port_init(serial_priv->ds_oob_port,
+						serial_priv->ds_oob_port_num);
+	if (ret) {
+		kfree(serial_priv);
+		return ret;
+	}
+
+	usb_set_serial_data(serial, serial_priv);
+
+	return 0;
+}
+
+
+static void digi_disconnect(struct usb_serial *serial)
+{
+	int i;
+
+	/* stop reads and writes on all ports */
+	for (i = 0; i < serial->type->num_ports + 1; i++) {
+		usb_kill_urb(serial->port[i]->read_urb);
+		usb_kill_urb(serial->port[i]->write_urb);
+	}
+}
+
+
+static void digi_release(struct usb_serial *serial)
+{
+	struct digi_serial *serial_priv;
+	struct digi_port *priv;
+
+	serial_priv = usb_get_serial_data(serial);
+
+	priv = usb_get_serial_port_data(serial_priv->ds_oob_port);
+	kfree(priv);
+
+	kfree(serial_priv);
+}
+
+static int digi_port_probe(struct usb_serial_port *port)
+{
+	return digi_port_init(port, port->port_number);
+}
+
+static int digi_port_remove(struct usb_serial_port *port)
+{
+	struct digi_port *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static void digi_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct digi_port *priv;
+	struct digi_serial *serial_priv;
+	int ret;
+	int status = urb->status;
+
+	/* port sanity check, do not resubmit if port is not valid */
+	if (port == NULL)
+		return;
+	priv = usb_get_serial_port_data(port);
+	if (priv == NULL) {
+		dev_err(&port->dev, "%s: port->private is NULL, status=%d\n",
+			__func__, status);
+		return;
+	}
+	if (port->serial == NULL ||
+		(serial_priv = usb_get_serial_data(port->serial)) == NULL) {
+		dev_err(&port->dev, "%s: serial is bad or serial->private "
+			"is NULL, status=%d\n", __func__, status);
+		return;
+	}
+
+	/* do not resubmit urb if it has any status error */
+	if (status) {
+		dev_err(&port->dev,
+			"%s: nonzero read bulk status: status=%d, port=%d\n",
+			__func__, status, priv->dp_port_num);
+		return;
+	}
+
+	/* handle oob or inb callback, do not resubmit if error */
+	if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
+		if (digi_read_oob_callback(urb) != 0)
+			return;
+	} else {
+		if (digi_read_inb_callback(urb) != 0)
+			return;
+	}
+
+	/* continue read */
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret != 0 && ret != -EPERM) {
+		dev_err(&port->dev,
+			"%s: failed resubmitting urb, ret=%d, port=%d\n",
+			__func__, ret, priv->dp_port_num);
+	}
+
+}
+
+/*
+ *  Digi Read INB Callback
+ *
+ *  Digi Read INB Callback handles reads on the in band ports, sending
+ *  the data on to the tty subsystem.  When called we know port and
+ *  port->private are not NULL and port->serial has been validated.
+ *  It returns 0 if successful, 1 if successful but the port is
+ *  throttled, and -1 if the sanity checks failed.
+ */
+
+static int digi_read_inb_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned char *buf = urb->transfer_buffer;
+	unsigned long flags;
+	int opcode;
+	int len;
+	int port_status;
+	unsigned char *data;
+	int tty_flag, throttled;
+
+	/* short/multiple packet check */
+	if (urb->actual_length < 2) {
+		dev_warn(&port->dev, "short packet received\n");
+		return -1;
+	}
+
+	opcode = buf[0];
+	len = buf[1];
+
+	if (urb->actual_length != len + 2) {
+		dev_err(&port->dev, "malformed packet received: port=%d, opcode=%d, len=%d, actual_length=%u\n",
+			priv->dp_port_num, opcode, len, urb->actual_length);
+		return -1;
+	}
+
+	if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) {
+		dev_err(&port->dev, "malformed data packet received\n");
+		return -1;
+	}
+
+	spin_lock_irqsave(&priv->dp_port_lock, flags);
+
+	/* check for throttle; if set, do not resubmit read urb */
+	/* indicate the read chain needs to be restarted on unthrottle */
+	throttled = priv->dp_throttled;
+	if (throttled)
+		priv->dp_throttle_restart = 1;
+
+	/* receive data */
+	if (opcode == DIGI_CMD_RECEIVE_DATA) {
+		port_status = buf[2];
+		data = &buf[3];
+
+		/* get flag from port_status */
+		tty_flag = 0;
+
+		/* overrun is special, not associated with a char */
+		if (port_status & DIGI_OVERRUN_ERROR)
+			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+
+		/* break takes precedence over parity, */
+		/* which takes precedence over framing errors */
+		if (port_status & DIGI_BREAK_ERROR)
+			tty_flag = TTY_BREAK;
+		else if (port_status & DIGI_PARITY_ERROR)
+			tty_flag = TTY_PARITY;
+		else if (port_status & DIGI_FRAMING_ERROR)
+			tty_flag = TTY_FRAME;
+
+		/* data length is len-1 (one byte of len is port_status) */
+		--len;
+		if (len > 0) {
+			tty_insert_flip_string_fixed_flag(&port->port, data,
+					tty_flag, len);
+			tty_flip_buffer_push(&port->port);
+		}
+	}
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+
+	if (opcode == DIGI_CMD_RECEIVE_DISABLE)
+		dev_dbg(&port->dev, "%s: got RECEIVE_DISABLE\n", __func__);
+	else if (opcode != DIGI_CMD_RECEIVE_DATA)
+		dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode);
+
+	return throttled ? 1 : 0;
+
+}
+
+
+/*
+ *  Digi Read OOB Callback
+ *
+ *  Digi Read OOB Callback handles reads on the out of band port.
+ *  When called we know port and port->private are not NULL and
+ *  the port->serial is valid.  It returns 0 if successful, and
+ *  -1 if the sanity checks failed.
+ */
+
+static int digi_read_oob_callback(struct urb *urb)
+{
+
+	struct usb_serial_port *port = urb->context;
+	struct usb_serial *serial = port->serial;
+	struct tty_struct *tty;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	unsigned char *buf = urb->transfer_buffer;
+	int opcode, line, status, val;
+	unsigned long flags;
+	int i;
+	unsigned int rts;
+
+	if (urb->actual_length < 4)
+		return -1;
+
+	/* handle each oob command */
+	for (i = 0; i < urb->actual_length - 3; i += 4) {
+		opcode = buf[i];
+		line = buf[i + 1];
+		status = buf[i + 2];
+		val = buf[i + 3];
+
+		dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
+			opcode, line, status, val);
+
+		if (status != 0 || line >= serial->type->num_ports)
+			continue;
+
+		port = serial->port[line];
+
+		priv = usb_get_serial_port_data(port);
+		if (priv == NULL)
+			return -1;
+
+		tty = tty_port_tty_get(&port->port);
+
+		rts = 0;
+		if (tty)
+			rts = C_CRTSCTS(tty);
+
+		if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+			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);
+			} else {
+				priv->dp_modem_signals &= ~TIOCM_CTS;
+				/* port must be open to use tty struct */
+			}
+			if (val & DIGI_READ_INPUT_SIGNALS_DSR)
+				priv->dp_modem_signals |= TIOCM_DSR;
+			else
+				priv->dp_modem_signals &= ~TIOCM_DSR;
+			if (val & DIGI_READ_INPUT_SIGNALS_RI)
+				priv->dp_modem_signals |= TIOCM_RI;
+			else
+				priv->dp_modem_signals &= ~TIOCM_RI;
+			if (val & DIGI_READ_INPUT_SIGNALS_DCD)
+				priv->dp_modem_signals |= TIOCM_CD;
+			else
+				priv->dp_modem_signals &= ~TIOCM_CD;
+
+			spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+		} else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
+			spin_lock_irqsave(&priv->dp_port_lock, flags);
+			priv->dp_transmit_idle = 1;
+			wake_up_interruptible(&priv->dp_transmit_idle_wait);
+			spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+		} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
+			wake_up_interruptible(&priv->dp_flush_wait);
+		}
+		tty_kref_put(tty);
+	}
+	return 0;
+
+}
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
new file mode 100644
index 0000000..d680bec
--- /dev/null
+++ b/drivers/usb/serial/empeg.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Empeg empeg-car player driver
+ *
+ *	Copyright (C) 2000, 2001
+ *	    Gary Brubaker (xavyer@ix.netcom.com)
+ *
+ *	Copyright (C) 1999 - 2001
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Gary Brubaker <xavyer@ix.netcom.com>"
+#define DRIVER_DESC "USB Empeg Mark I/II Driver"
+
+#define EMPEG_VENDOR_ID			0x084f
+#define EMPEG_PRODUCT_ID		0x0001
+
+/* function prototypes for an empeg-car player */
+static int  empeg_startup(struct usb_serial *serial);
+static void empeg_init_termios(struct tty_struct *tty);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(EMPEG_VENDOR_ID, EMPEG_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver empeg_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"empeg",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.bulk_out_size =	256,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.attach =		empeg_startup,
+	.init_termios =		empeg_init_termios,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&empeg_device, NULL
+};
+
+static int empeg_startup(struct usb_serial *serial)
+{
+	int r;
+
+	if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
+		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
+			serial->dev->actconfig->desc.bConfigurationValue);
+		return -ENODEV;
+	}
+
+	r = usb_reset_configuration(serial->dev);
+
+	/* continue on with initialization */
+	return r;
+}
+
+static void empeg_init_termios(struct tty_struct *tty)
+{
+	struct ktermios *termios = &tty->termios;
+
+	/*
+	 * The empeg-car player wants these particular tty settings.
+	 * You could, for example, change the baud rate, however the
+	 * player only supports 115200 (currently), so there is really
+	 * no point in support for changes to the tty settings.
+	 * (at least for now)
+	 *
+	 * The default requirements for this device are:
+	 */
+	termios->c_iflag
+		&= ~(IGNBRK	/* disable ignore break */
+		| BRKINT	/* disable break causes interrupt */
+		| PARMRK	/* disable mark parity errors */
+		| ISTRIP	/* disable clear high bit of input characters */
+		| INLCR		/* disable translate NL to CR */
+		| IGNCR		/* disable ignore CR */
+		| ICRNL		/* disable translate CR to NL */
+		| IXON);	/* disable enable XON/XOFF flow control */
+
+	termios->c_oflag
+		&= ~OPOST;	/* disable postprocess output characters */
+
+	termios->c_lflag
+		&= ~(ECHO	/* disable echo input characters */
+		| ECHONL	/* disable echo new line */
+		| ICANON	/* disable erase, kill, werase, and rprnt special characters */
+		| ISIG		/* disable interrupt, quit, and suspend special characters */
+		| IEXTEN);	/* disable non-POSIX special characters */
+
+	termios->c_cflag
+		&= ~(CSIZE	/* no size */
+		| PARENB	/* disable parity bit */
+		| CBAUD);	/* clear current baud rate */
+
+	termios->c_cflag
+		|= CS8;		/* character size 8 bits */
+
+	tty_encode_baud_rate(tty, 115200, 115200);
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/ezusb_convert.pl b/drivers/usb/serial/ezusb_convert.pl
new file mode 100644
index 0000000..40d23f2
--- /dev/null
+++ b/drivers/usb/serial/ezusb_convert.pl
@@ -0,0 +1,51 @@
+#! /usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0
+
+
+# convert an Intel HEX file into a set of C records usable by the firmware
+# loading code in usb-serial.c (or others)
+
+# accepts the .hex file(s) on stdin, a basename (to name the initialized
+# array) as an argument, and prints the .h file to stdout. Typical usage:
+#  perl ezusb_convert.pl foo <foo.hex >fw_foo.h
+
+
+my $basename = $ARGV[0];
+die "no base name specified" unless $basename;
+
+while (<STDIN>) {
+    # ':' <len> <addr> <type> <len-data> <crc> '\r'
+    #  len, type, crc are 2-char hex, addr is 4-char hex. type is 00 for
+    # normal records, 01 for EOF
+    my($lenstring, $addrstring, $typestring, $reststring, $doscrap) =
+      /^:(\w\w)(\w\w\w\w)(\w\w)(\w+)(\r?)$/;
+    die "malformed line: $_" unless $reststring;
+    last if $typestring eq '01';
+    my($len) = hex($lenstring);
+    my($addr) = hex($addrstring);
+    my(@bytes) = unpack("C*", pack("H".(2*$len), $reststring));
+    #pop(@bytes); # last byte is a CRC
+    push(@records, [$addr, \@bytes]);
+}
+
+@sorted_records = sort { $a->[0] <=> $b->[0] } @records;
+
+print <<"EOF";
+/*
+ * ${basename}_fw.h
+ *
+ * Generated from ${basename}.s by ezusb_convert.pl
+ * This file is presumed to be under the same copyright as the source file
+ * from which it was derived.
+ */
+
+EOF
+
+print "static const struct ezusb_hex_record ${basename}_firmware[] = {\n";
+foreach $r (@sorted_records) {
+    printf("{ 0x%04x,\t%d,\t{", $r->[0], scalar(@{$r->[1]}));
+    print join(", ", map {sprintf('0x%02x', $_);} @{$r->[1]});
+    print "} },\n";
+}
+print "{ 0xffff,\t0,\t{0x00} }\n";
+print "};\n";
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
new file mode 100644
index 0000000..96036f8
--- /dev/null
+++ b/drivers/usb/serial/f81232.c
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Fintek F81232 USB to serial adaptor driver
+ *
+ * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
+ * Copyright (C) 2012 Linux Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x1934, 0x0706) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* Maximum baudrate for F81232 */
+#define F81232_MAX_BAUDRATE		115200
+
+/* USB Control EP parameter */
+#define F81232_REGISTER_REQUEST		0xa0
+#define F81232_GET_REGISTER		0xc0
+#define F81232_SET_REGISTER		0x40
+
+#define SERIAL_BASE_ADDRESS		0x0120
+#define RECEIVE_BUFFER_REGISTER		(0x00 + SERIAL_BASE_ADDRESS)
+#define INTERRUPT_ENABLE_REGISTER	(0x01 + SERIAL_BASE_ADDRESS)
+#define FIFO_CONTROL_REGISTER		(0x02 + SERIAL_BASE_ADDRESS)
+#define LINE_CONTROL_REGISTER		(0x03 + SERIAL_BASE_ADDRESS)
+#define MODEM_CONTROL_REGISTER		(0x04 + SERIAL_BASE_ADDRESS)
+#define MODEM_STATUS_REGISTER		(0x06 + SERIAL_BASE_ADDRESS)
+
+struct f81232_private {
+	struct mutex lock;
+	u8 modem_control;
+	u8 modem_status;
+	struct work_struct interrupt_work;
+	struct usb_serial_port *port;
+};
+
+static int calc_baud_divisor(speed_t baudrate)
+{
+	return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate);
+}
+
+static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val)
+{
+	int status;
+	u8 *tmp;
+	struct usb_device *dev = port->serial->dev;
+
+	tmp = kmalloc(sizeof(*val), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	status = usb_control_msg(dev,
+				usb_rcvctrlpipe(dev, 0),
+				F81232_REGISTER_REQUEST,
+				F81232_GET_REGISTER,
+				reg,
+				0,
+				tmp,
+				sizeof(*val),
+				USB_CTRL_GET_TIMEOUT);
+	if (status != sizeof(*val)) {
+		dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
+
+		if (status < 0)
+			status = usb_translate_errors(status);
+		else
+			status = -EIO;
+	} else {
+		status = 0;
+		*val = *tmp;
+	}
+
+	kfree(tmp);
+	return status;
+}
+
+static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val)
+{
+	int status;
+	u8 *tmp;
+	struct usb_device *dev = port->serial->dev;
+
+	tmp = kmalloc(sizeof(val), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	*tmp = val;
+
+	status = usb_control_msg(dev,
+				usb_sndctrlpipe(dev, 0),
+				F81232_REGISTER_REQUEST,
+				F81232_SET_REGISTER,
+				reg,
+				0,
+				tmp,
+				sizeof(val),
+				USB_CTRL_SET_TIMEOUT);
+	if (status != sizeof(val)) {
+		dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
+
+		if (status < 0)
+			status = usb_translate_errors(status);
+		else
+			status = -EIO;
+	} else {
+		status = 0;
+	}
+
+	kfree(tmp);
+	return status;
+}
+
+static void f81232_read_msr(struct usb_serial_port *port)
+{
+	int status;
+	u8 current_msr;
+	struct tty_struct *tty;
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+
+	mutex_lock(&priv->lock);
+	status = f81232_get_register(port, MODEM_STATUS_REGISTER,
+			&current_msr);
+	if (status) {
+		dev_err(&port->dev, "%s fail, status: %d\n", __func__, status);
+		mutex_unlock(&priv->lock);
+		return;
+	}
+
+	if (!(current_msr & UART_MSR_ANY_DELTA)) {
+		mutex_unlock(&priv->lock);
+		return;
+	}
+
+	priv->modem_status = current_msr;
+
+	if (current_msr & UART_MSR_DCTS)
+		port->icount.cts++;
+	if (current_msr & UART_MSR_DDSR)
+		port->icount.dsr++;
+	if (current_msr & UART_MSR_TERI)
+		port->icount.rng++;
+	if (current_msr & UART_MSR_DDCD) {
+		port->icount.dcd++;
+		tty = tty_port_tty_get(&port->port);
+		if (tty) {
+			usb_serial_handle_dcd_change(port, tty,
+					current_msr & UART_MSR_DCD);
+
+			tty_kref_put(tty);
+		}
+	}
+
+	wake_up_interruptible(&port->port.delta_msr_wait);
+	mutex_unlock(&priv->lock);
+}
+
+static int f81232_set_mctrl(struct usb_serial_port *port,
+					   unsigned int set, unsigned int clear)
+{
+	u8 val;
+	int status;
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0)
+		return 0;	/* no change */
+
+	/* 'set' takes precedence over 'clear' */
+	clear &= ~set;
+
+	/* force enable interrupt with OUT2 */
+	mutex_lock(&priv->lock);
+	val = UART_MCR_OUT2 | priv->modem_control;
+
+	if (clear & TIOCM_DTR)
+		val &= ~UART_MCR_DTR;
+
+	if (clear & TIOCM_RTS)
+		val &= ~UART_MCR_RTS;
+
+	if (set & TIOCM_DTR)
+		val |= UART_MCR_DTR;
+
+	if (set & TIOCM_RTS)
+		val |= UART_MCR_RTS;
+
+	dev_dbg(&port->dev, "%s new:%02x old:%02x\n", __func__,
+			val, priv->modem_control);
+
+	status = f81232_set_register(port, MODEM_CONTROL_REGISTER, val);
+	if (status) {
+		dev_err(&port->dev, "%s set MCR status < 0\n", __func__);
+		mutex_unlock(&priv->lock);
+		return status;
+	}
+
+	priv->modem_control = val;
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static void f81232_update_line_status(struct usb_serial_port *port,
+				      unsigned char *data,
+				      size_t actual_length)
+{
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+
+	if (!actual_length)
+		return;
+
+	switch (data[0] & 0x07) {
+	case 0x00: /* msr change */
+		dev_dbg(&port->dev, "IIR: MSR Change: %02x\n", data[0]);
+		schedule_work(&priv->interrupt_work);
+		break;
+	case 0x02: /* tx-empty */
+		break;
+	case 0x04: /* rx data available */
+		break;
+	case 0x06: /* lsr change */
+		/* we can forget it. the LSR will read from bulk-in */
+		dev_dbg(&port->dev, "IIR: LSR Change: %02x\n", data[0]);
+		break;
+	}
+}
+
+static void f81232_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port =  urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int actual_length = urb->actual_length;
+	int status = urb->status;
+	int retval;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__,
+			      urb->actual_length, urb->transfer_buffer);
+
+	f81232_update_line_status(port, data, actual_length);
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&urb->dev->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+}
+
+static void f81232_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	char tty_flag;
+	unsigned int i;
+	u8 lsr;
+
+	/*
+	 * When opening the port we get a 1-byte packet with the current LSR,
+	 * which we discard.
+	 */
+	if ((urb->actual_length < 2) || (urb->actual_length % 2))
+		return;
+
+	/* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */
+
+	for (i = 0; i < urb->actual_length; i += 2) {
+		tty_flag = TTY_NORMAL;
+		lsr = data[i];
+
+		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 + 1]))
+				continue;
+		}
+
+		tty_insert_flip_char(&port->port, data[i + 1], tty_flag);
+	}
+
+	tty_flip_buffer_push(&port->port);
+}
+
+static void f81232_break_ctl(struct tty_struct *tty, int break_state)
+{
+	/* FIXME - Stubbed out for now */
+
+	/*
+	 * break_state = -1 to turn on break, and 0 to turn off break
+	 * see drivers/char/tty_io.c to see it used.
+	 * last_set_data_urb_value NEVER has the break bit set in it.
+	 */
+}
+
+static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate)
+{
+	u8 lcr;
+	int divisor;
+	int status = 0;
+
+	divisor = calc_baud_divisor(baudrate);
+
+	status = f81232_get_register(port, LINE_CONTROL_REGISTER,
+			 &lcr); /* get LCR */
+	if (status) {
+		dev_err(&port->dev, "%s failed to get LCR: %d\n",
+			__func__, status);
+		return;
+	}
+
+	status = f81232_set_register(port, LINE_CONTROL_REGISTER,
+			 lcr | UART_LCR_DLAB); /* Enable DLAB */
+	if (status) {
+		dev_err(&port->dev, "%s failed to set DLAB: %d\n",
+			__func__, status);
+		return;
+	}
+
+	status = f81232_set_register(port, RECEIVE_BUFFER_REGISTER,
+			 divisor & 0x00ff); /* low */
+	if (status) {
+		dev_err(&port->dev, "%s failed to set baudrate MSB: %d\n",
+			__func__, status);
+		goto reapply_lcr;
+	}
+
+	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER,
+			 (divisor & 0xff00) >> 8); /* high */
+	if (status) {
+		dev_err(&port->dev, "%s failed to set baudrate LSB: %d\n",
+			__func__, status);
+	}
+
+reapply_lcr:
+	status = f81232_set_register(port, LINE_CONTROL_REGISTER,
+			lcr & ~UART_LCR_DLAB);
+	if (status) {
+		dev_err(&port->dev, "%s failed to set DLAB: %d\n",
+			__func__, status);
+	}
+}
+
+static int f81232_port_enable(struct usb_serial_port *port)
+{
+	u8 val;
+	int status;
+
+	/* fifo on, trigger8, clear TX/RX*/
+	val = UART_FCR_TRIGGER_8 | UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+			UART_FCR_CLEAR_XMIT;
+
+	status = f81232_set_register(port, FIFO_CONTROL_REGISTER, val);
+	if (status) {
+		dev_err(&port->dev, "%s failed to set FCR: %d\n",
+			__func__, status);
+		return status;
+	}
+
+	/* MSR Interrupt only, LSR will read from Bulk-in odd byte */
+	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER,
+			UART_IER_MSI);
+	if (status) {
+		dev_err(&port->dev, "%s failed to set IER: %d\n",
+			__func__, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static int f81232_port_disable(struct usb_serial_port *port)
+{
+	int status;
+
+	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, 0);
+	if (status) {
+		dev_err(&port->dev, "%s failed to set IER: %d\n",
+			__func__, status);
+		return status;
+	}
+
+	return 0;
+}
+
+static void f81232_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	u8 new_lcr = 0;
+	int status = 0;
+	speed_t baudrate;
+
+	/* Don't change anything if nothing has changed */
+	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+		return;
+
+	if (C_BAUD(tty) == B0)
+		f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+		f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+
+	baudrate = tty_get_baud_rate(tty);
+	if (baudrate > 0) {
+		if (baudrate > F81232_MAX_BAUDRATE) {
+			baudrate = F81232_MAX_BAUDRATE;
+			tty_encode_baud_rate(tty, baudrate, baudrate);
+		}
+		f81232_set_baudrate(port, baudrate);
+	}
+
+	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;
+	}
+
+	status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr);
+	if (status) {
+		dev_err(&port->dev, "%s failed to set LCR: %d\n",
+			__func__, status);
+	}
+}
+
+static int f81232_tiocmget(struct tty_struct *tty)
+{
+	int r;
+	struct usb_serial_port *port = tty->driver_data;
+	struct f81232_private *port_priv = usb_get_serial_port_data(port);
+	u8 mcr, msr;
+
+	/* force get current MSR changed state */
+	f81232_read_msr(port);
+
+	mutex_lock(&port_priv->lock);
+	mcr = port_priv->modem_control;
+	msr = port_priv->modem_status;
+	mutex_unlock(&port_priv->lock);
+
+	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 f81232_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	return f81232_set_mctrl(port, set, clear);
+}
+
+static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result;
+
+	result = f81232_port_enable(port);
+	if (result)
+		return result;
+
+	/* Setup termios */
+	if (tty)
+		f81232_set_termios(tty, port, NULL);
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result) {
+		dev_err(&port->dev, "%s - failed submitting interrupt urb,"
+			" error %d\n", __func__, result);
+		return result;
+	}
+
+	result = usb_serial_generic_open(tty, port);
+	if (result) {
+		usb_kill_urb(port->interrupt_in_urb);
+		return result;
+	}
+
+	return 0;
+}
+
+static void f81232_close(struct usb_serial_port *port)
+{
+	f81232_port_disable(port);
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static void f81232_dtr_rts(struct usb_serial_port *port, int on)
+{
+	if (on)
+		f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+	else
+		f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+static int f81232_carrier_raised(struct usb_serial_port *port)
+{
+	u8 msr;
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+
+	mutex_lock(&priv->lock);
+	msr = priv->modem_status;
+	mutex_unlock(&priv->lock);
+
+	if (msr & UART_MSR_DCD)
+		return 1;
+	return 0;
+}
+
+static int f81232_get_serial_info(struct usb_serial_port *port,
+		unsigned long arg)
+{
+	struct serial_struct ser;
+
+	memset(&ser, 0, sizeof(ser));
+
+	ser.type = PORT_16550A;
+	ser.line = port->minor;
+	ser.port = port->port_number;
+	ser.baud_base = F81232_MAX_BAUDRATE;
+
+	if (copy_to_user((void __user *)arg, &ser, sizeof(ser)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int f81232_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return f81232_get_serial_info(port, arg);
+	default:
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static void  f81232_interrupt_work(struct work_struct *work)
+{
+	struct f81232_private *priv =
+		container_of(work, struct f81232_private, interrupt_work);
+
+	f81232_read_msr(priv->port);
+}
+
+static int f81232_port_probe(struct usb_serial_port *port)
+{
+	struct f81232_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mutex_init(&priv->lock);
+	INIT_WORK(&priv->interrupt_work,  f81232_interrupt_work);
+
+	usb_set_serial_port_data(port, priv);
+
+	port->port.drain_delay = 256;
+	priv->port = port;
+
+	return 0;
+}
+
+static int f81232_port_remove(struct usb_serial_port *port)
+{
+	struct f81232_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct usb_serial_driver f81232_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"f81232",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.bulk_in_size =		256,
+	.bulk_out_size =	256,
+	.open =			f81232_open,
+	.close =		f81232_close,
+	.dtr_rts =		f81232_dtr_rts,
+	.carrier_raised =	f81232_carrier_raised,
+	.ioctl =		f81232_ioctl,
+	.break_ctl =		f81232_break_ctl,
+	.set_termios =		f81232_set_termios,
+	.tiocmget =		f81232_tiocmget,
+	.tiocmset =		f81232_tiocmset,
+	.tiocmiwait =		usb_serial_generic_tiocmiwait,
+	.process_read_urb =	f81232_process_read_urb,
+	.read_int_callback =	f81232_read_int_callback,
+	.port_probe =		f81232_port_probe,
+	.port_remove =		f81232_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&f81232_device,
+	NULL,
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
+MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
+MODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
new file mode 100644
index 0000000..4dfbff2
--- /dev/null
+++ b/drivers/usb/serial/f81534.c
@@ -0,0 +1,1602 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * F81532/F81534 USB to Serial Ports Bridge
+ *
+ * F81532 => 2 Serial Ports
+ * F81534 => 4 Serial Ports
+ *
+ * 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_INTERRUPT_ENABLE_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_LINE_STATUS_REG		(0x05 + F81534_UART_BASE_ADDRESS)
+#define F81534_MODEM_STATUS_REG		(0x06 + F81534_UART_BASE_ADDRESS)
+#define F81534_CLOCK_REG		(0x08 + 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_CONF_GPIO_OFFSET		4
+
+#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		2000
+#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_PORT_CONF_RS232		0
+#define F81534_PORT_CONF_RS485		BIT(0)
+#define F81534_PORT_CONF_RS485_INVERT	(BIT(0) | BIT(1))
+#define F81534_PORT_CONF_MODE_MASK	GENMASK(1, 0)
+#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
+
+/*
+ * F81532/534 Clock registers (offset +08h)
+ *
+ * Bit0:	UART Enable (always on)
+ * Bit2-1:	Clock source selector
+ *			00: 1.846MHz.
+ *			01: 18.46MHz.
+ *			10: 24MHz.
+ *			11: 14.77MHz.
+ * Bit4:	Auto direction(RTS) control (RTS pin Low when TX)
+ * Bit5:	Invert direction(RTS) when Bit4 enabled (RTS pin high when TX)
+ */
+
+#define F81534_UART_EN			BIT(0)
+#define F81534_CLK_1_846_MHZ		0
+#define F81534_CLK_18_46_MHZ		BIT(1)
+#define F81534_CLK_24_MHZ		BIT(2)
+#define F81534_CLK_14_77_MHZ		(BIT(1) | BIT(2))
+#define F81534_CLK_MASK			GENMASK(2, 1)
+#define F81534_CLK_TX_DELAY_1BIT	BIT(3)
+#define F81534_CLK_RS485_MODE		BIT(4)
+#define F81534_CLK_RS485_INVERT		BIT(5)
+
+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;
+	struct mutex lcr_mutex;
+	struct work_struct lsr_work;
+	struct usb_serial_port *port;
+	unsigned long tx_empty;
+	spinlock_t msr_lock;
+	u32 baud_base;
+	u8 shadow_mcr;
+	u8 shadow_lcr;
+	u8 shadow_msr;
+	u8 shadow_clk;
+	u8 phy_num;
+};
+
+struct f81534_pin_data {
+	const u16 reg_addr;
+	const u8 reg_mask;
+};
+
+struct f81534_port_out_pin {
+	struct f81534_pin_data pin[3];
+};
+
+/* Pin output value for M2/M1/M0(SD) */
+static const struct f81534_port_out_pin f81534_port_out_pins[] = {
+	 { { { 0x2ae8, BIT(7) }, { 0x2a90, BIT(5) }, { 0x2a90, BIT(4) } } },
+	 { { { 0x2ae8, BIT(6) }, { 0x2ae8, BIT(0) }, { 0x2ae8, BIT(3) } } },
+	 { { { 0x2a90, BIT(0) }, { 0x2ae8, BIT(2) }, { 0x2a80, BIT(6) } } },
+	 { { { 0x2a90, BIT(3) }, { 0x2a90, BIT(2) }, { 0x2a90, BIT(1) } } },
+};
+
+static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 };
+static u8 const clock_table[] = { F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ,
+				F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ };
+
+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_mask_register(struct usb_serial *serial, u16 reg,
+					u8 mask, u8 data)
+{
+	int status;
+	u8 tmp;
+
+	status = f81534_get_register(serial, reg, &tmp);
+	if (status)
+		return status;
+
+	tmp &= ~mask;
+	tmp |= (mask & data);
+
+	return f81534_set_register(serial, reg, tmp);
+}
+
+static int f81534_set_phy_port_register(struct usb_serial *serial, int phy,
+					u16 reg, u8 data)
+{
+	return f81534_set_register(serial, reg + F81534_UART_OFFSET * phy,
+					data);
+}
+
+static int f81534_get_phy_port_register(struct usb_serial *serial, int phy,
+					u16 reg, u8 *data)
+{
+	return f81534_get_register(serial, reg + F81534_UART_OFFSET * phy,
+					data);
+}
+
+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_find_clk(u32 baudrate)
+{
+	int idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) {
+		if (baudrate <= baudrate_table[idx] &&
+				baudrate_table[idx] % baudrate == 0)
+			return idx;
+	}
+
+	return -EINVAL;
+}
+
+static int f81534_set_port_config(struct usb_serial_port *port,
+		struct tty_struct *tty, u32 baudrate, u32 old_baudrate, u8 lcr)
+{
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	u32 divisor;
+	int status;
+	int i;
+	int idx;
+	u8 value;
+	u32 baud_list[] = {baudrate, old_baudrate, F81534_DEFAULT_BAUD_RATE};
+
+	for (i = 0; i < ARRAY_SIZE(baud_list); ++i) {
+		idx = f81534_find_clk(baud_list[i]);
+		if (idx >= 0) {
+			baudrate = baud_list[i];
+			tty_encode_baud_rate(tty, baudrate, baudrate);
+			break;
+		}
+	}
+
+	if (idx < 0)
+		return -EINVAL;
+
+	port_priv->baud_base = baudrate_table[idx];
+	port_priv->shadow_clk &= ~F81534_CLK_MASK;
+	port_priv->shadow_clk |= clock_table[idx];
+
+	status = f81534_set_port_register(port, F81534_CLOCK_REG,
+			port_priv->shadow_clk);
+	if (status) {
+		dev_err(&port->dev, "CLOCK_REG setting failed\n");
+		return status;
+	}
+
+	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_TRIGGER_8 | UART_FCR_ENABLE_FIFO; /* TL: 8 */
+
+	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, port_priv->baud_base);
+
+	mutex_lock(&port_priv->lcr_mutex);
+
+	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__);
+		goto out_unlock;
+	}
+
+	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__);
+		goto out_unlock;
+	}
+
+	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__);
+		goto out_unlock;
+	}
+
+	value = lcr | (port_priv->shadow_lcr & UART_LCR_SBC);
+	status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+						value);
+	if (status) {
+		dev_err(&port->dev, "%s: set LCR failed\n", __func__);
+		goto out_unlock;
+	}
+
+	port_priv->shadow_lcr = value;
+out_unlock:
+	mutex_unlock(&port_priv->lcr_mutex);
+
+	return status;
+}
+
+static void f81534_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+	int status;
+
+	mutex_lock(&port_priv->lcr_mutex);
+
+	if (break_state)
+		port_priv->shadow_lcr |= UART_LCR_SBC;
+	else
+		port_priv->shadow_lcr &= ~UART_LCR_SBC;
+
+	status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+					port_priv->shadow_lcr);
+	if (status)
+		dev_err(&port->dev, "set break failed: %d\n", status);
+
+	mutex_unlock(&port_priv->lcr_mutex);
+}
+
+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;
+}
+
+/*
+ * The F81532/534 will not report serial port to USB serial subsystem when
+ * H/W DCD/DSR/CTS/RI/RX pin connected to ground.
+ *
+ * To detect RX pin status, we'll enable MCR interal loopback, disable it and
+ * delayed for 60ms. It connected to ground If LSR register report UART_LSR_BI.
+ */
+static bool f81534_check_port_hw_disabled(struct usb_serial *serial, int phy)
+{
+	int status;
+	u8 old_mcr;
+	u8 msr;
+	u8 lsr;
+	u8 msr_mask;
+
+	msr_mask = UART_MSR_DCD | UART_MSR_RI | UART_MSR_DSR | UART_MSR_CTS;
+
+	status = f81534_get_phy_port_register(serial, phy,
+				F81534_MODEM_STATUS_REG, &msr);
+	if (status)
+		return false;
+
+	if ((msr & msr_mask) != msr_mask)
+		return false;
+
+	status = f81534_set_phy_port_register(serial, phy,
+				F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO |
+				UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	if (status)
+		return false;
+
+	status = f81534_get_phy_port_register(serial, phy,
+				F81534_MODEM_CONTROL_REG, &old_mcr);
+	if (status)
+		return false;
+
+	status = f81534_set_phy_port_register(serial, phy,
+				F81534_MODEM_CONTROL_REG, UART_MCR_LOOP);
+	if (status)
+		return false;
+
+	status = f81534_set_phy_port_register(serial, phy,
+				F81534_MODEM_CONTROL_REG, 0x0);
+	if (status)
+		return false;
+
+	msleep(60);
+
+	status = f81534_get_phy_port_register(serial, phy,
+				F81534_LINE_STATUS_REG, &lsr);
+	if (status)
+		return false;
+
+	status = f81534_set_phy_port_register(serial, phy,
+				F81534_MODEM_CONTROL_REG, old_mcr);
+	if (status)
+		return false;
+
+	if ((lsr & UART_LSR_BI) == UART_LSR_BI)
+		return true;
+
+	return false;
+}
+
+/*
+ * 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,
+					struct usb_serial_endpoints *epds)
+{
+	struct f81534_serial_private *serial_priv;
+	struct device *dev = &serial->interface->dev;
+	int size_bulk_in = usb_endpoint_maxp(epds->bulk_in[0]);
+	int size_bulk_out = usb_endpoint_maxp(epds->bulk_out[0]);
+	u8 num_port = 0;
+	int index = 0;
+	int status;
+	int i;
+
+	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;
+	}
+
+	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);
+
+	/* 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) {
+		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: get custom data failed: %d\n",
+					__func__, status);
+			return status;
+		}
+
+		dev_dbg(&serial->interface->dev,
+				"%s: read config from block: %d\n", __func__,
+				serial_priv->setting_idx);
+	} else {
+		/* Read default board setting */
+		status = f81534_read_flash(serial,
+				F81534_DEF_CONF_ADDRESS_START,
+				sizeof(serial_priv->conf_data),
+				serial_priv->conf_data);
+		if (status) {
+			dev_err(&serial->interface->dev,
+					"%s: read failed: %d\n", __func__,
+					status);
+			return status;
+		}
+
+		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 (f81534_check_port_hw_disabled(serial, i))
+			serial_priv->conf_data[i] |= F81534_PORT_UNAVAILABLE;
+
+		if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE)
+			continue;
+
+		++num_port;
+	}
+
+	if (!num_port) {
+		dev_warn(&serial->interface->dev,
+			"no config found, assuming 4 ports\n");
+		num_port = 4;		/* Nothing found, oldest version IC */
+	}
+
+	/* 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]);
+	}
+
+	/*
+	 * Setup bulk-out endpoint multiplexing. All ports share the same
+	 * bulk-out endpoint.
+	 */
+	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < F81534_NUM_PORT);
+
+	for (i = 1; i < num_port; ++i)
+		epds->bulk_out[i] = epds->bulk_out[0];
+
+	epds->num_bulk_out = num_port;
+
+	return num_port;
+}
+
+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;
+	u32 old_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 (old_termios)
+		old_baud = tty_termios_baud_rate(old_termios);
+	else
+		old_baud = F81534_DEFAULT_BAUD_RATE;
+
+	dev_dbg(&port->dev, "%s: baud: %d\n", __func__, baud);
+
+	status = f81534_set_port_config(port, tty, baud, old_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 = port_priv->baud_base;
+
+	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);
+			}
+
+			schedule_work(&port_priv->lsr_work);
+		}
+
+		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 void f81534_lsr_worker(struct work_struct *work)
+{
+	struct f81534_port_private *port_priv;
+	struct usb_serial_port *port;
+	int status;
+	u8 tmp;
+
+	port_priv = container_of(work, struct f81534_port_private, lsr_work);
+	port = port_priv->port;
+
+	status = f81534_get_port_register(port, F81534_LINE_STATUS_REG, &tmp);
+	if (status)
+		dev_warn(&port->dev, "read LSR failed: %d\n", status);
+}
+
+static int f81534_set_port_output_pin(struct usb_serial_port *port)
+{
+	struct f81534_serial_private *serial_priv;
+	struct f81534_port_private *port_priv;
+	struct usb_serial *serial;
+	const struct f81534_port_out_pin *pins;
+	int status;
+	int i;
+	u8 value;
+	u8 idx;
+
+	serial = port->serial;
+	serial_priv = usb_get_serial_data(serial);
+	port_priv = usb_get_serial_port_data(port);
+
+	idx = F81534_CONF_GPIO_OFFSET + port_priv->phy_num;
+	value = serial_priv->conf_data[idx];
+	pins = &f81534_port_out_pins[port_priv->phy_num];
+
+	for (i = 0; i < ARRAY_SIZE(pins->pin); ++i) {
+		status = f81534_set_mask_register(serial,
+				pins->pin[i].reg_addr, pins->pin[i].reg_mask,
+				value & BIT(i) ? pins->pin[i].reg_mask : 0);
+		if (status)
+			return status;
+	}
+
+	dev_dbg(&port->dev, "Output pin (M0/M1/M2): %d\n", value);
+	return 0;
+}
+
+static int f81534_port_probe(struct usb_serial_port *port)
+{
+	struct f81534_serial_private *serial_priv;
+	struct f81534_port_private *port_priv;
+	int ret;
+	u8 value;
+
+	serial_priv = usb_get_serial_data(port->serial);
+	port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL);
+	if (!port_priv)
+		return -ENOMEM;
+
+	/*
+	 * We'll make tx frame error when baud rate from 384~500kps. So we'll
+	 * delay all tx data frame with 1bit.
+	 */
+	port_priv->shadow_clk = F81534_UART_EN | F81534_CLK_TX_DELAY_1BIT;
+	spin_lock_init(&port_priv->msr_lock);
+	mutex_init(&port_priv->mcr_mutex);
+	mutex_init(&port_priv->lcr_mutex);
+	INIT_WORK(&port_priv->lsr_work, f81534_lsr_worker);
+
+	/* Assign logic-to-phy mapping */
+	ret = f81534_logic_to_phy_port(port->serial, port);
+	if (ret < 0)
+		return ret;
+
+	port_priv->phy_num = ret;
+	port_priv->port = port;
+	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);
+
+	/*
+	 * The F81532/534 will hang-up when enable LSR interrupt in IER and
+	 * occur data overrun. So we'll disable the LSR interrupt in probe()
+	 * and submit the LSR worker to clear LSR state when reported LSR error
+	 * bit with bulk-in data in f81534_process_per_serial_block().
+	 */
+	ret = f81534_set_port_register(port, F81534_INTERRUPT_ENABLE_REG,
+			UART_IER_RDI | UART_IER_THRI | UART_IER_MSI);
+	if (ret)
+		return ret;
+
+	value = serial_priv->conf_data[port_priv->phy_num];
+	switch (value & F81534_PORT_CONF_MODE_MASK) {
+	case F81534_PORT_CONF_RS485_INVERT:
+		port_priv->shadow_clk |= F81534_CLK_RS485_MODE |
+					F81534_CLK_RS485_INVERT;
+		dev_dbg(&port->dev, "RS485 invert mode\n");
+		break;
+	case F81534_PORT_CONF_RS485:
+		port_priv->shadow_clk |= F81534_CLK_RS485_MODE;
+		dev_dbg(&port->dev, "RS485 mode\n");
+		break;
+
+	default:
+	case F81534_PORT_CONF_RS232:
+		dev_dbg(&port->dev, "RS232 mode\n");
+		break;
+	}
+
+	return f81534_set_port_output_pin(port);
+}
+
+static int f81534_port_remove(struct usb_serial_port *port)
+{
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+	flush_work(&port_priv->lsr_work);
+	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,
+	.num_bulk_in =		1,
+	.num_bulk_out =		1,
+	.open =			f81534_open,
+	.close =		f81534_close,
+	.write =		f81534_write,
+	.tx_empty =		f81534_tx_empty,
+	.calc_num_ports =	f81534_calc_num_ports,
+	.port_probe =		f81534_port_probe,
+	.port_remove =		f81534_port_remove,
+	.break_ctl =		f81534_break_ctl,
+	.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
new file mode 100644
index 0000000..b5cef32
--- /dev/null
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -0,0 +1,2475 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB FTDI SIO driver
+ *
+ *	Copyright (C) 2009 - 2013
+ *	    Johan Hovold (jhovold@gmail.com)
+ *	Copyright (C) 1999 - 2001
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *          Bill Ryder (bryder@sgi.com)
+ *	Copyright (C) 2002
+ *	    Kuba Ober (kuba@mareimbrium.org)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * See http://ftdi-usb-sio.sourceforge.net for up to date testing info
+ *	and extra documentation
+ *
+ * Change entries from 2004 and earlier can be found in versions of this
+ * file in kernel versions prior to the 2.6.24 release.
+ *
+ */
+
+/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */
+/* Thanx to FTDI for so kindly providing details of the protocol required */
+/*   to talk to the device */
+/* Thanx to gkh and the rest of the usb dev group for all code I have
+   assimilated :-) */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/serial.h>
+#include <linux/usb/serial.h>
+#include "ftdi_sio.h"
+#include "ftdi_sio_ids.h"
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "USB FTDI Serial Converters Driver"
+
+
+struct ftdi_private {
+	enum ftdi_chip_type chip_type;
+				/* type of device, either SIO or FT8U232AM */
+	int baud_base;		/* baud base clock for divisor setting */
+	int custom_divisor;	/* custom_divisor kludge, this is for
+				   baud_base (different from what goes to the
+				   chip!) */
+	u16 last_set_data_value; /* the last data state set - needed for doing
+				  * a break
+				  */
+	int flags;		/* some ASYNC_xxxx flags are supported */
+	unsigned long last_dtr_rts;	/* saved modem control outputs */
+	char prev_status;        /* Used for TIOCMIWAIT */
+	char transmit_empty;	/* If transmitter is empty or not */
+	u16 interface;		/* FT2232C, FT2232H or FT4232H port interface
+				   (0 for FT232/245) */
+
+	speed_t force_baud;	/* if non-zero, force the baud rate to
+				   this value */
+	int force_rtscts;	/* if non-zero, force RTS-CTS to always
+				   be enabled */
+
+	unsigned int latency;		/* latency setting in use */
+	unsigned short max_packet_size;
+	struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */
+};
+
+/* struct ftdi_sio_quirk is used by devices requiring special attention. */
+struct ftdi_sio_quirk {
+	int (*probe)(struct usb_serial *);
+	/* Special settings for probed ports. */
+	void (*port_probe)(struct ftdi_private *);
+};
+
+static int   ftdi_jtag_probe(struct usb_serial *serial);
+static int   ftdi_NDI_device_setup(struct usb_serial *serial);
+static int   ftdi_stmclite_probe(struct usb_serial *serial);
+static int   ftdi_8u2232c_probe(struct usb_serial *serial);
+static void  ftdi_USB_UIRT_setup(struct ftdi_private *priv);
+static void  ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
+
+static const struct ftdi_sio_quirk ftdi_jtag_quirk = {
+	.probe	= ftdi_jtag_probe,
+};
+
+static const struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
+	.probe	= ftdi_NDI_device_setup,
+};
+
+static const struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
+	.port_probe = ftdi_USB_UIRT_setup,
+};
+
+static const struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
+	.port_probe = ftdi_HE_TIRA1_setup,
+};
+
+static const struct ftdi_sio_quirk ftdi_stmclite_quirk = {
+	.probe	= ftdi_stmclite_probe,
+};
+
+static const struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
+	.probe	= ftdi_8u2232c_probe,
+};
+
+/*
+ * The 8U232AM has the same API as the sio except for:
+ * - it can support MUCH higher baudrates; up to:
+ *   o 921600 for RS232 and 2000000 for RS422/485 at 48MHz
+ *   o 230400 at 12MHz
+ *   so .. 8U232AM's baudrate setting codes are different
+ * - it has a two byte status code.
+ * - it returns characters every 16ms (the FTDI does it every 40ms)
+ *
+ * the bcdDevice value is used to differentiate FT232BM and FT245BM from
+ * the earlier FT8U232AM and FT8U232BM.  For now, include all known VID/PID
+ * combinations in both tables.
+ * FIXME: perhaps bcdDevice can also identify 12MHz FT8U232AM devices,
+ * but I don't know if those ever went into mass production. [Ian Abbott]
+ */
+
+
+
+/*
+ * Device ID not listed? Test it using
+ * /sys/bus/usb-serial/drivers/ftdi_sio/new_id and send a patch or report.
+ */
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(FTDI_VID, FTDI_BRICK_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_BM_ATOM_NANO_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_3_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_4_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IPLUS2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DMX4ALL) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) ,
+		.driver_info = (kernel_ulong_t)&ftdi_8u2232c_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_232H_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_FTX_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_SNIFFER_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_BOOST_PID) },
+	{ USB_DEVICE(NEWPORT_VID, NEWPORT_AGILIS_PID) },
+	{ USB_DEVICE(NEWPORT_VID, NEWPORT_CONEX_CC_PID) },
+	{ USB_DEVICE(NEWPORT_VID, NEWPORT_CONEX_AGP_PID) },
+	{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
+	{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SPROG_II) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TAGSYS_LP101_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TAGSYS_P200X_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_LENZ_LIUSB_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_547_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_633_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_631_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_635_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) },
+	{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_3_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) },
+	{ USB_DEVICE(FTDI_VID, FTDI_VARDAAN_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0103_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0104_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0105_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0106_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0107_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0108_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0109_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0110_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0111_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0112_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0113_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0114_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0115_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0116_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0117_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0118_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0119_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0120_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0121_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0122_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0123_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0130_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0131_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0132_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0133_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0134_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0135_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0136_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0137_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0138_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0139_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0140_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0141_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0142_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0143_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0144_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0145_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0146_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0147_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0148_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0149_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0160_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0161_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0162_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0163_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0164_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0165_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0166_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0167_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0168_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0169_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0170_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0171_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0172_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0173_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0174_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0175_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0176_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0177_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0178_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0179_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0180_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0181_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0182_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0183_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0184_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0185_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0186_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0187_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0188_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0189_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0190_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0191_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0192_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0193_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0194_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0195_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0196_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0197_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0198_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0199_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AD_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BD_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CD_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DD_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01ED_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F0_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F1_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F2_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F3_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F4_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F5_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F6_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F7_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F8_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F9_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FA_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FB_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FC_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_1_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_2_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_3_PID) },
+	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_4_PID) },
+	{ USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) },
+	{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
+	{ USB_DEVICE(OCT_VID, OCT_DK201_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_USB_UIRT_quirk },
+	{ USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) },
+	{ USB_DEVICE(FTDI_VID, PROTEGO_R2X0) },
+	{ USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) },
+	{ USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E808_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E809_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80A_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80B_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80C_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80D_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80E_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80F_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E888_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E889_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88A_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88B_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88C_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88D_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88E_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88F_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UM100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UR100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2WI_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX3_PID) },
+	/*
+	 * ELV devices:
+	 */
+	{ USB_DEVICE(FTDI_ELV_VID, FTDI_ELV_WS300_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PALMSENS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_IVIUM_XSTAT_PID) },
+	{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
+	{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
+	{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
+	{ USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) },
+	{ USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) },
+	{ USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) },
+	{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
+	{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
+	{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
+	{ USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) },
+	{ USB_DEVICE(TTI_VID, TTI_QL355P_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
+	{ USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) },
+	{ USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) },
+	{ USB_DEVICE(MITSUBISHI_VID, MITSUBISHI_FXUSB_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USOPTL4_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USPTL4_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_2_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR2_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_485USB9F_2W_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_485USB9F_4W_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_232USB9M_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_485USBTB_2W_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_485USBTB_4W_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_TTL5USB9M_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_TTL3USB9M_PID) },
+	{ USB_DEVICE(BANDB_VID, BANDB_ZZ_PROG1_USB_PID) },
+	{ USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
+	{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_3_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_3_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_4_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) },
+	{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) },
+	{ USB_DEVICE(XSENS_VID, XSENS_AWINDA_DONGLE_PID) },
+	{ USB_DEVICE(XSENS_VID, XSENS_AWINDA_STATION_PID) },
+	{ USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) },
+	{ USB_DEVICE(XSENS_VID, XSENS_MTDEVBOARD_PID) },
+	{ USB_DEVICE(XSENS_VID, XSENS_MTW_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
+	{ USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_YS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_IC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_DB9_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_RS232_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y9_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) },
+	{ USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) },
+	{ USB_DEVICE(EVOLUTION_VID, EVO_HYBRID_PID) },
+	{ USB_DEVICE(EVOLUTION_VID, EVO_RCM4_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16IC_PID) },
+	{ USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) },
+	{ USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) },
+	{ USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TTUSB_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ECLO_COM_1WIRE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2C2_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2D_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2VT_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2VR_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVT_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVR_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVT_PID) },
+	{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVR_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) },
+	{ USB_DEVICE(TESTO_VID, TESTO_1_PID) },
+	{ USB_DEVICE(TESTO_VID, TESTO_3_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
+	{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+	{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
+	{ USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) },
+	{ USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_29A_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_29B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_29F_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_62B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S01_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_63_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_29C_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_81B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_82B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_K5D_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_K4Y_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_K5G_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S05_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_60_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_61_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_62_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_63B_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_64_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_65_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_92_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_92D_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_W5R_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_A5R_PID) },
+	{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_PW1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) },
+	{ USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
+	{ USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) },
+	{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) },
+	{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) },
+	{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) },
+	{ USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
+	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) },
+	{ USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
+
+	/* Papouch devices based on FTDI chip */
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_2_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_2_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_2_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485S_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485C_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_LEC_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB232_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_IRAMP_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK5_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO8x8_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x2_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO10x1_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO30x3_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO60x3_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x16_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO3x32_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK6_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_UPSUSB_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_MU_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_SIMUKEY_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMUX_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMSR_PID) },
+
+	{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) },
+	{ USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) },
+	{ USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) },
+	{ USB_DEVICE(ATMEL_VID, STK541_PID) },
+	{ USB_DEVICE(DE_VID, STB_PID) },
+	{ USB_DEVICE(DE_VID, WHT_PID) },
+	{ USB_DEVICE(ADI_VID, ADI_GNICE_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
+					USB_CLASS_VENDOR_SPEC,
+					USB_SUBCLASS_VENDOR_SPEC, 0x00) },
+	{ USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) },
+	{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
+	{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
+	{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
+	{ USB_DEVICE(FTDI_VID, PI_C865_PID) },
+	{ USB_DEVICE(FTDI_VID, PI_C857_PID) },
+	{ USB_DEVICE(PI_VID, PI_C866_PID) },
+	{ USB_DEVICE(PI_VID, PI_C663_PID) },
+	{ USB_DEVICE(PI_VID, PI_C725_PID) },
+	{ USB_DEVICE(PI_VID, PI_E517_PID) },
+	{ USB_DEVICE(PI_VID, PI_C863_PID) },
+	{ USB_DEVICE(PI_VID, PI_E861_PID) },
+	{ USB_DEVICE(PI_VID, PI_C867_PID) },
+	{ USB_DEVICE(PI_VID, PI_E609_PID) },
+	{ USB_DEVICE(PI_VID, PI_E709_PID) },
+	{ USB_DEVICE(PI_VID, PI_100F_PID) },
+	{ USB_DEVICE(PI_VID, PI_1011_PID) },
+	{ USB_DEVICE(PI_VID, PI_1012_PID) },
+	{ USB_DEVICE(PI_VID, PI_1013_PID) },
+	{ USB_DEVICE(PI_VID, PI_1014_PID) },
+	{ USB_DEVICE(PI_VID, PI_1015_PID) },
+	{ USB_DEVICE(PI_VID, PI_1016_PID) },
+	{ USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
+	{ USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
+	{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, TI_XDS100V2_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
+	{ USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) },
+	{ USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) },
+	{ USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) },
+	{ USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) },
+	{ USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
+	{ USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) },
+	{ USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) },
+	{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) },
+	{ USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) },
+	{ USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MIDI_TIMECODE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MINI_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MAXI_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MEDIA_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_WING_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_FHE_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) },
+	{ USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(ST_VID, ST_STMCLT_2232_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(ST_VID, ST_STMCLT_4232_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_RF_R106) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
+	/* Crucible Devices */
+	{ USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_Z3X_PID) },
+	/* Cressi Devices */
+	{ USB_DEVICE(FTDI_VID, FTDI_CRESSI_PID) },
+	/* Brainboxes Devices */
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_001_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_012_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_023_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_034_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_101_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_3_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_4_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_5_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_6_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_7_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_8_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_257_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_3_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_4_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_313_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_324_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_346_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_346_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_357_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_606_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_606_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_606_3_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_701_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_701_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_1_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_2_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_3_PID) },
+	{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_4_PID) },
+	/* ekey Devices */
+	{ USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) },
+	/* Infineon Devices */
+	{ USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC1798_PID, 1) },
+	{ USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC2X7_PID, 1) },
+	/* GE Healthcare devices */
+	{ USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) },
+	/* Active Research (Actisense) devices */
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_NDC_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_USG_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_NGT_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_NGW_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_D9AC_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_D9AD_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_D9AE_PID) },
+	{ USB_DEVICE(FTDI_VID, ACTISENSE_D9AF_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEAGAUGE_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASWITCH_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_NMEA2000_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ETHERNET_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_WIFI_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) },
+	{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) },
+	/* ICP DAS I-756xU devices */
+	{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7560U_PID) },
+	{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
+	{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
+	{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
+	{ USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
+	{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
+	{ USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+static const char *ftdi_chip_name[] = {
+	[SIO] = "SIO",	/* the serial part of FT8U100AX */
+	[FT8U232AM] = "FT8U232AM",
+	[FT232BM] = "FT232BM",
+	[FT2232C] = "FT2232C",
+	[FT232RL] = "FT232RL",
+	[FT2232H] = "FT2232H",
+	[FT4232H] = "FT4232H",
+	[FT232H]  = "FT232H",
+	[FTX]     = "FT-X"
+};
+
+
+/* Used for TIOCMIWAIT */
+#define FTDI_STATUS_B0_MASK	(FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
+#define FTDI_STATUS_B1_MASK	(FTDI_RS_BI)
+/* End TIOCMIWAIT */
+
+/* function prototypes for a FTDI serial converter */
+static int  ftdi_sio_probe(struct usb_serial *serial,
+					const struct usb_device_id *id);
+static int  ftdi_sio_port_probe(struct usb_serial_port *port);
+static int  ftdi_sio_port_remove(struct usb_serial_port *port);
+static int  ftdi_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
+static void ftdi_process_read_urb(struct urb *urb);
+static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size);
+static void ftdi_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static int  ftdi_tiocmget(struct tty_struct *tty);
+static int  ftdi_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear);
+static int  ftdi_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
+static bool ftdi_tx_empty(struct usb_serial_port *port);
+static int ftdi_get_modem_status(struct usb_serial_port *port,
+						unsigned char status[2]);
+
+static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
+static unsigned short int ftdi_232am_baud_to_divisor(int baud);
+static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
+static u32 ftdi_232bm_baud_to_divisor(int baud);
+static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
+static u32 ftdi_2232h_baud_to_divisor(int baud);
+
+static struct usb_serial_driver ftdi_sio_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"ftdi_sio",
+	},
+	.description =		"FTDI USB Serial Device",
+	.id_table =		id_table_combined,
+	.num_ports =		1,
+	.bulk_in_size =		512,
+	.bulk_out_size =	256,
+	.probe =		ftdi_sio_probe,
+	.port_probe =		ftdi_sio_port_probe,
+	.port_remove =		ftdi_sio_port_remove,
+	.open =			ftdi_open,
+	.dtr_rts =		ftdi_dtr_rts,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.process_read_urb =	ftdi_process_read_urb,
+	.prepare_write_buffer =	ftdi_prepare_write_buffer,
+	.tiocmget =		ftdi_tiocmget,
+	.tiocmset =		ftdi_tiocmset,
+	.tiocmiwait =		usb_serial_generic_tiocmiwait,
+	.get_icount =           usb_serial_generic_get_icount,
+	.ioctl =		ftdi_ioctl,
+	.set_termios =		ftdi_set_termios,
+	.break_ctl =		ftdi_break_ctl,
+	.tx_empty =		ftdi_tx_empty,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ftdi_sio_device, NULL
+};
+
+
+#define WDR_TIMEOUT 5000 /* default urb timeout */
+#define WDR_SHORT_TIMEOUT 1000	/* shorter urb timeout */
+
+/*
+ * ***************************************************************************
+ * Utility functions
+ * ***************************************************************************
+ */
+
+static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base)
+{
+	unsigned short int divisor;
+	/* divisor shifted 3 bits to the left */
+	int divisor3 = base / 2 / baud;
+	if ((divisor3 & 0x7) == 7)
+		divisor3++; /* round x.7/8 up to x+1 */
+	divisor = divisor3 >> 3;
+	divisor3 &= 0x7;
+	if (divisor3 == 1)
+		divisor |= 0xc000;
+	else if (divisor3 >= 4)
+		divisor |= 0x4000;
+	else if (divisor3 != 0)
+		divisor |= 0x8000;
+	else if (divisor == 1)
+		divisor = 0;	/* special case for maximum baud rate */
+	return divisor;
+}
+
+static unsigned short int ftdi_232am_baud_to_divisor(int baud)
+{
+	 return ftdi_232am_baud_base_to_divisor(baud, 48000000);
+}
+
+static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
+{
+	static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+	u32 divisor;
+	/* divisor shifted 3 bits to the left */
+	int divisor3 = base / 2 / baud;
+	divisor = divisor3 >> 3;
+	divisor |= (u32)divfrac[divisor3 & 0x7] << 14;
+	/* Deal with special cases for highest baud rates. */
+	if (divisor == 1)
+		divisor = 0;
+	else if (divisor == 0x4001)
+		divisor = 1;
+	return divisor;
+}
+
+static u32 ftdi_232bm_baud_to_divisor(int baud)
+{
+	 return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
+}
+
+static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
+{
+	static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+	u32 divisor;
+	int divisor3;
+
+	/* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+	divisor3 = base * 8 / (baud * 10);
+
+	divisor = divisor3 >> 3;
+	divisor |= (u32)divfrac[divisor3 & 0x7] << 14;
+	/* Deal with special cases for highest baud rates. */
+	if (divisor == 1)
+		divisor = 0;
+	else if (divisor == 0x4001)
+		divisor = 1;
+	/*
+	 * Set this bit to turn off a divide by 2.5 on baud rate generator
+	 * This enables baud rates up to 12Mbaud but cannot reach below 1200
+	 * baud with this bit set
+	 */
+	divisor |= 0x00020000;
+	return divisor;
+}
+
+static u32 ftdi_2232h_baud_to_divisor(int baud)
+{
+	 return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
+}
+
+#define set_mctrl(port, set)		update_mctrl((port), (set), 0)
+#define clear_mctrl(port, clear)	update_mctrl((port), 0, (clear))
+
+static int update_mctrl(struct usb_serial_port *port, unsigned int set,
+							unsigned int clear)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned value;
+	int rv;
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+		dev_dbg(dev, "%s - DTR|RTS not being set|cleared\n", __func__);
+		return 0;	/* no change */
+	}
+
+	clear &= ~set;	/* 'set' takes precedence over 'clear' */
+	value = 0;
+	if (clear & TIOCM_DTR)
+		value |= FTDI_SIO_SET_DTR_LOW;
+	if (clear & TIOCM_RTS)
+		value |= FTDI_SIO_SET_RTS_LOW;
+	if (set & TIOCM_DTR)
+		value |= FTDI_SIO_SET_DTR_HIGH;
+	if (set & TIOCM_RTS)
+		value |= FTDI_SIO_SET_RTS_HIGH;
+	rv = usb_control_msg(port->serial->dev,
+			       usb_sndctrlpipe(port->serial->dev, 0),
+			       FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+			       FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			       value, priv->interface,
+			       NULL, 0, WDR_TIMEOUT);
+	if (rv < 0) {
+		dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n",
+			__func__,
+			(set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
+			(set & TIOCM_RTS) ? "HIGH" : (clear & TIOCM_RTS) ? "LOW" : "unchanged");
+		rv = usb_translate_errors(rv);
+	} else {
+		dev_dbg(dev, "%s - DTR %s, RTS %s\n", __func__,
+			(set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
+			(set & TIOCM_RTS) ? "HIGH" : (clear & TIOCM_RTS) ? "LOW" : "unchanged");
+		/* FIXME: locking on last_dtr_rts */
+		priv->last_dtr_rts = (priv->last_dtr_rts & ~clear) | set;
+	}
+	return rv;
+}
+
+
+static u32 get_ftdi_divisor(struct tty_struct *tty,
+						struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	u32 div_value = 0;
+	int div_okay = 1;
+	int baud;
+
+	baud = tty_get_baud_rate(tty);
+	dev_dbg(dev, "%s - tty_get_baud_rate reports speed %d\n", __func__, baud);
+
+	/*
+	 * Observe deprecated async-compatible custom_divisor hack, update
+	 * baudrate if needed.
+	 */
+	if (baud == 38400 &&
+	    ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
+	     (priv->custom_divisor)) {
+		baud = priv->baud_base / priv->custom_divisor;
+		dev_dbg(dev, "%s - custom divisor %d sets baud rate to %d\n",
+			__func__, priv->custom_divisor, baud);
+	}
+
+	if (!baud)
+		baud = 9600;
+	switch (priv->chip_type) {
+	case SIO: /* SIO chip */
+		switch (baud) {
+		case 300: div_value = ftdi_sio_b300; break;
+		case 600: div_value = ftdi_sio_b600; break;
+		case 1200: div_value = ftdi_sio_b1200; break;
+		case 2400: div_value = ftdi_sio_b2400; break;
+		case 4800: div_value = ftdi_sio_b4800; break;
+		case 9600: div_value = ftdi_sio_b9600; break;
+		case 19200: div_value = ftdi_sio_b19200; break;
+		case 38400: div_value = ftdi_sio_b38400; break;
+		case 57600: div_value = ftdi_sio_b57600;  break;
+		case 115200: div_value = ftdi_sio_b115200; break;
+		} /* baud */
+		if (div_value == 0) {
+			dev_dbg(dev, "%s - Baudrate (%d) requested is not supported\n",
+				__func__,  baud);
+			div_value = ftdi_sio_b9600;
+			baud = 9600;
+			div_okay = 0;
+		}
+		break;
+	case FT8U232AM: /* 8U232AM chip */
+		if (baud <= 3000000) {
+			div_value = ftdi_232am_baud_to_divisor(baud);
+		} else {
+			dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
+			baud = 9600;
+			div_value = ftdi_232am_baud_to_divisor(9600);
+			div_okay = 0;
+		}
+		break;
+	case FT232BM: /* FT232BM chip */
+	case FT2232C: /* FT2232C chip */
+	case FT232RL: /* FT232RL chip */
+	case FTX:     /* FT-X series */
+		if (baud <= 3000000) {
+			u16 product_id = le16_to_cpu(
+				port->serial->dev->descriptor.idProduct);
+			if (((product_id == FTDI_NDI_HUC_PID)		||
+			     (product_id == FTDI_NDI_SPECTRA_SCU_PID)	||
+			     (product_id == FTDI_NDI_FUTURE_2_PID)	||
+			     (product_id == FTDI_NDI_FUTURE_3_PID)	||
+			     (product_id == FTDI_NDI_AURORA_SCU_PID))	&&
+			    (baud == 19200)) {
+				baud = 1200000;
+			}
+			div_value = ftdi_232bm_baud_to_divisor(baud);
+		} else {
+			dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
+			div_value = ftdi_232bm_baud_to_divisor(9600);
+			div_okay = 0;
+			baud = 9600;
+		}
+		break;
+	case FT2232H: /* FT2232H chip */
+	case FT4232H: /* FT4232H chip */
+	case FT232H:  /* FT232H chip */
+		if ((baud <= 12000000) && (baud >= 1200)) {
+			div_value = ftdi_2232h_baud_to_divisor(baud);
+		} else if (baud < 1200) {
+			div_value = ftdi_232bm_baud_to_divisor(baud);
+		} else {
+			dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
+			div_value = ftdi_232bm_baud_to_divisor(9600);
+			div_okay = 0;
+			baud = 9600;
+		}
+		break;
+	} /* priv->chip_type */
+
+	if (div_okay) {
+		dev_dbg(dev, "%s - Baud rate set to %d (divisor 0x%lX) on chip %s\n",
+			__func__, baud, (unsigned long)div_value,
+			ftdi_chip_name[priv->chip_type]);
+	}
+
+	tty_encode_baud_rate(tty, baud, baud);
+	return div_value;
+}
+
+static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	u16 value;
+	u16 index;
+	u32 index_value;
+	int rv;
+
+	index_value = get_ftdi_divisor(tty, port);
+	value = (u16)index_value;
+	index = (u16)(index_value >> 16);
+	if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) ||
+		(priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) {
+		/* Probably the BM type needs the MSB of the encoded fractional
+		 * divider also moved like for the chips above. Any infos? */
+		index = (u16)((index << 8) | priv->interface);
+	}
+
+	rv = usb_control_msg(port->serial->dev,
+			    usb_sndctrlpipe(port->serial->dev, 0),
+			    FTDI_SIO_SET_BAUDRATE_REQUEST,
+			    FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+			    value, index,
+			    NULL, 0, WDR_SHORT_TIMEOUT);
+	return rv;
+}
+
+static int write_latency_timer(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev = port->serial->dev;
+	int rv;
+	int l = priv->latency;
+
+	if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
+		return -EINVAL;
+
+	if (priv->flags & ASYNC_LOW_LATENCY)
+		l = 1;
+
+	dev_dbg(&port->dev, "%s: setting latency timer = %i\n", __func__, l);
+
+	rv = usb_control_msg(udev,
+			     usb_sndctrlpipe(udev, 0),
+			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+			     l, priv->interface,
+			     NULL, 0, WDR_TIMEOUT);
+	if (rv < 0)
+		dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
+	return rv;
+}
+
+static int _read_latency_timer(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev = port->serial->dev;
+	unsigned char *buf;
+	int rv;
+
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	rv = usb_control_msg(udev,
+			     usb_rcvctrlpipe(udev, 0),
+			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
+			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
+			     0, priv->interface,
+			     buf, 1, WDR_TIMEOUT);
+	if (rv < 1) {
+		if (rv >= 0)
+			rv = -EIO;
+	} else {
+		rv = buf[0];
+	}
+
+	kfree(buf);
+
+	return rv;
+}
+
+static int read_latency_timer(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	int rv;
+
+	if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
+		return -EINVAL;
+
+	rv = _read_latency_timer(port);
+	if (rv < 0) {
+		dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
+		return rv;
+	}
+
+	priv->latency = rv;
+
+	return 0;
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+				struct serial_struct __user *retinfo)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.flags = priv->flags;
+	tmp.baud_base = priv->baud_base;
+	tmp.custom_divisor = priv->custom_divisor;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct tty_struct *tty,
+	struct usb_serial_port *port, struct serial_struct __user *newinfo)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct serial_struct new_serial;
+	struct ftdi_private old_priv;
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+		return -EFAULT;
+
+	mutex_lock(&priv->cfg_lock);
+	old_priv = *priv;
+
+	/* Do error checking and permission checking */
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.flags ^ priv->flags) & ~ASYNC_USR_MASK) {
+			mutex_unlock(&priv->cfg_lock);
+			return -EPERM;
+		}
+		priv->flags = ((priv->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		priv->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (new_serial.baud_base != priv->baud_base) {
+		mutex_unlock(&priv->cfg_lock);
+		return -EINVAL;
+	}
+
+	/* Make the changes - these are privileged changes! */
+
+	priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
+					(new_serial.flags & ASYNC_FLAGS));
+	priv->custom_divisor = new_serial.custom_divisor;
+
+check_and_exit:
+	write_latency_timer(port);
+
+	if ((priv->flags ^ old_priv.flags) & ASYNC_SPD_MASK ||
+			((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
+			 priv->custom_divisor != old_priv.custom_divisor)) {
+
+		/* warn about deprecation unless clearing */
+		if (priv->flags & ASYNC_SPD_MASK)
+			dev_warn_ratelimited(&port->dev, "use of SPD flags is deprecated\n");
+
+		change_speed(tty, port);
+		mutex_unlock(&priv->cfg_lock);
+	}
+	else
+		mutex_unlock(&priv->cfg_lock);
+	return 0;
+}
+
+static int get_lsr_info(struct usb_serial_port *port,
+			unsigned int __user *retinfo)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	unsigned int result = 0;
+
+	if (priv->transmit_empty)
+		result = TIOCSER_TEMT;
+
+	if (copy_to_user(retinfo, &result, sizeof(unsigned int)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* Determine type of FTDI chip based on USB config and descriptor. */
+static void ftdi_determine_type(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	struct usb_device *udev = serial->dev;
+	unsigned version;
+	unsigned interfaces;
+
+	/* Assume it is not the original SIO device for now. */
+	priv->baud_base = 48000000 / 2;
+
+	version = le16_to_cpu(udev->descriptor.bcdDevice);
+	interfaces = udev->actconfig->desc.bNumInterfaces;
+	dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
+		version, interfaces);
+	if (interfaces > 1) {
+		int inter;
+
+		/* Multiple interfaces.*/
+		if (version == 0x0800) {
+			priv->chip_type = FT4232H;
+			/* Hi-speed - baud clock runs at 120MHz */
+			priv->baud_base = 120000000 / 2;
+		} else if (version == 0x0700) {
+			priv->chip_type = FT2232H;
+			/* Hi-speed - baud clock runs at 120MHz */
+			priv->baud_base = 120000000 / 2;
+		} else
+			priv->chip_type = FT2232C;
+
+		/* Determine interface code. */
+		inter = serial->interface->altsetting->desc.bInterfaceNumber;
+		if (inter == 0) {
+			priv->interface = INTERFACE_A;
+		} else  if (inter == 1) {
+			priv->interface = INTERFACE_B;
+		} else  if (inter == 2) {
+			priv->interface = INTERFACE_C;
+		} else  if (inter == 3) {
+			priv->interface = INTERFACE_D;
+		}
+		/* BM-type devices have a bug where bcdDevice gets set
+		 * to 0x200 when iSerialNumber is 0.  */
+		if (version < 0x500) {
+			dev_dbg(&port->dev,
+				"%s: something fishy - bcdDevice too low for multi-interface device\n",
+				__func__);
+		}
+	} else if (version < 0x200) {
+		/* Old device.  Assume it's the original SIO. */
+		priv->chip_type = SIO;
+		priv->baud_base = 12000000 / 16;
+	} else if (version < 0x400) {
+		/* Assume it's an FT8U232AM (or FT8U245AM) */
+		priv->chip_type = FT8U232AM;
+		/*
+		 * It might be a BM type because of the iSerialNumber bug.
+		 * If iSerialNumber==0 and the latency timer is readable,
+		 * assume it is BM type.
+		 */
+		if (udev->descriptor.iSerialNumber == 0 &&
+				_read_latency_timer(port) >= 0) {
+			dev_dbg(&port->dev,
+				"%s: has latency timer so not an AM type\n",
+				__func__);
+			priv->chip_type = FT232BM;
+		}
+	} else if (version < 0x600) {
+		/* Assume it's an FT232BM (or FT245BM) */
+		priv->chip_type = FT232BM;
+	} else if (version < 0x900) {
+		/* Assume it's an FT232RL */
+		priv->chip_type = FT232RL;
+	} else if (version < 0x1000) {
+		/* Assume it's an FT232H */
+		priv->chip_type = FT232H;
+	} else {
+		/* Assume it's an FT-X series device */
+		priv->chip_type = FTX;
+	}
+
+	dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
+}
+
+
+/*
+ * Determine the maximum packet size for the device. This depends on the chip
+ * type and the USB host capabilities. The value should be obtained from the
+ * device descriptor as the chip will use the appropriate values for the host.
+ */
+static void ftdi_set_max_packet_size(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_interface *interface = port->serial->interface;
+	struct usb_endpoint_descriptor *ep_desc;
+	unsigned num_endpoints;
+	unsigned i;
+
+	num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+	if (!num_endpoints)
+		return;
+
+	/*
+	 * NOTE: Some customers have programmed FT232R/FT245R devices
+	 * with an endpoint size of 0 - not good. In this case, we
+	 * want to override the endpoint descriptor setting and use a
+	 * value of 64 for wMaxPacketSize.
+	 */
+	for (i = 0; i < num_endpoints; i++) {
+		ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+		if (!ep_desc->wMaxPacketSize) {
+			ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
+			dev_warn(&port->dev, "Overriding wMaxPacketSize on endpoint %d\n",
+					usb_endpoint_num(ep_desc));
+		}
+	}
+
+	/* Set max packet size based on last descriptor. */
+	priv->max_packet_size = usb_endpoint_maxp(ep_desc);
+}
+
+
+/*
+ * ***************************************************************************
+ * Sysfs Attribute
+ * ***************************************************************************
+ */
+
+static ssize_t latency_timer_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	if (priv->flags & ASYNC_LOW_LATENCY)
+		return sprintf(buf, "1\n");
+	else
+		return sprintf(buf, "%i\n", priv->latency);
+}
+
+/* Write a new value of the latency timer, in units of milliseconds. */
+static ssize_t latency_timer_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	u8 v;
+	int rv;
+
+	if (kstrtou8(valbuf, 10, &v))
+		return -EINVAL;
+
+	priv->latency = v;
+	rv = write_latency_timer(port);
+	if (rv < 0)
+		return -EIO;
+	return count;
+}
+static DEVICE_ATTR_RW(latency_timer);
+
+/* Write an event character directly to the FTDI register.  The ASCII
+   value is in the low 8 bits, with the enable bit in the 9th bit. */
+static ssize_t event_char_store(struct device *dev,
+	struct device_attribute *attr, const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev = port->serial->dev;
+	unsigned int v;
+	int rv;
+
+	if (kstrtouint(valbuf, 0, &v) || v >= 0x200)
+		return -EINVAL;
+
+	dev_dbg(&port->dev, "%s: setting event char = 0x%03x\n", __func__, v);
+
+	rv = usb_control_msg(udev,
+			     usb_sndctrlpipe(udev, 0),
+			     FTDI_SIO_SET_EVENT_CHAR_REQUEST,
+			     FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
+			     v, priv->interface,
+			     NULL, 0, WDR_TIMEOUT);
+	if (rv < 0) {
+		dev_dbg(&port->dev, "Unable to write event character: %i\n", rv);
+		return -EIO;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(event_char);
+
+static int create_sysfs_attrs(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	int retval = 0;
+
+	/* XXX I've no idea if the original SIO supports the event_char
+	 * sysfs parameter, so I'm playing it safe.  */
+	if (priv->chip_type != SIO) {
+		dev_dbg(&port->dev, "sysfs attributes for %s\n", ftdi_chip_name[priv->chip_type]);
+		retval = device_create_file(&port->dev, &dev_attr_event_char);
+		if ((!retval) &&
+		    (priv->chip_type == FT232BM ||
+		     priv->chip_type == FT2232C ||
+		     priv->chip_type == FT232RL ||
+		     priv->chip_type == FT2232H ||
+		     priv->chip_type == FT4232H ||
+		     priv->chip_type == FT232H ||
+		     priv->chip_type == FTX)) {
+			retval = device_create_file(&port->dev,
+						    &dev_attr_latency_timer);
+		}
+	}
+	return retval;
+}
+
+static void remove_sysfs_attrs(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	/* XXX see create_sysfs_attrs */
+	if (priv->chip_type != SIO) {
+		device_remove_file(&port->dev, &dev_attr_event_char);
+		if (priv->chip_type == FT232BM ||
+		    priv->chip_type == FT2232C ||
+		    priv->chip_type == FT232RL ||
+		    priv->chip_type == FT2232H ||
+		    priv->chip_type == FT4232H ||
+		    priv->chip_type == FT232H ||
+		    priv->chip_type == FTX) {
+			device_remove_file(&port->dev, &dev_attr_latency_timer);
+		}
+	}
+
+}
+
+/*
+ * ***************************************************************************
+ * FTDI driver specific functions
+ * ***************************************************************************
+ */
+
+/* Probe function to check for special devices */
+static int ftdi_sio_probe(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	const struct ftdi_sio_quirk *quirk =
+				(struct ftdi_sio_quirk *)id->driver_info;
+
+	if (quirk && quirk->probe) {
+		int ret = quirk->probe(serial);
+		if (ret != 0)
+			return ret;
+	}
+
+	usb_set_serial_data(serial, (void *)id->driver_info);
+
+	return 0;
+}
+
+static int ftdi_sio_port_probe(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv;
+	const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+
+
+	priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mutex_init(&priv->cfg_lock);
+
+	if (quirk && quirk->port_probe)
+		quirk->port_probe(priv);
+
+	usb_set_serial_port_data(port, priv);
+
+	ftdi_determine_type(port);
+	ftdi_set_max_packet_size(port);
+	if (read_latency_timer(port) < 0)
+		priv->latency = 16;
+	write_latency_timer(port);
+	create_sysfs_attrs(port);
+	return 0;
+}
+
+/* Setup for the USB-UIRT device, which requires hardwired
+ * baudrate (38400 gets mapped to 312500) */
+/* Called from usbserial:serial_probe */
+static void ftdi_USB_UIRT_setup(struct ftdi_private *priv)
+{
+	priv->flags |= ASYNC_SPD_CUST;
+	priv->custom_divisor = 77;
+	priv->force_baud = 38400;
+}
+
+/* Setup for the HE-TIRA1 device, which requires hardwired
+ * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled.  */
+
+static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
+{
+	priv->flags |= ASYNC_SPD_CUST;
+	priv->custom_divisor = 240;
+	priv->force_baud = 38400;
+	priv->force_rtscts = 1;
+}
+
+/*
+ * Module parameter to control latency timer for NDI FTDI-based USB devices.
+ * If this value is not set in /etc/modprobe.d/ its value will be set
+ * to 1ms.
+ */
+static int ndi_latency_timer = 1;
+
+/* Setup for the NDI FTDI-based USB devices, which requires hardwired
+ * baudrate (19200 gets mapped to 1200000).
+ *
+ * Called from usbserial:serial_probe.
+ */
+static int ftdi_NDI_device_setup(struct usb_serial *serial)
+{
+	struct usb_device *udev = serial->dev;
+	int latency = ndi_latency_timer;
+
+	if (latency == 0)
+		latency = 1;
+	if (latency > 99)
+		latency = 99;
+
+	dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency);
+	dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency);
+
+	/* FIXME: errors are not returned */
+	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+				FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+				latency, 0, NULL, 0, WDR_TIMEOUT);
+	return 0;
+}
+
+/*
+ * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko
+ * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from
+ * userspace using openocd.
+ */
+static int ftdi_jtag_probe(struct usb_serial *serial)
+{
+	struct usb_device *udev = serial->dev;
+	struct usb_interface *interface = serial->interface;
+
+	if (interface == udev->actconfig->interface[0]) {
+		dev_info(&udev->dev,
+			 "Ignoring serial port reserved for JTAG\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ftdi_8u2232c_probe(struct usb_serial *serial)
+{
+	struct usb_device *udev = serial->dev;
+
+	if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems"))
+		return ftdi_jtag_probe(serial);
+
+	if (udev->product &&
+		(!strcmp(udev->product, "Arrow USB Blaster") ||
+		 !strcmp(udev->product, "BeagleBone/XDS100V2") ||
+		 !strcmp(udev->product, "SNAP Connect E10")))
+		return ftdi_jtag_probe(serial);
+
+	return 0;
+}
+
+/*
+ * First two ports on JTAG adaptors using an FT4232 such as STMicroelectronics's
+ * ST Micro Connect Lite are reserved for JTAG or other non-UART interfaces and
+ * can be accessed from userspace.
+ * The next two ports are enabled as UARTs by default, where port 2 is
+ * a conventional RS-232 UART.
+ */
+static int ftdi_stmclite_probe(struct usb_serial *serial)
+{
+	struct usb_device *udev = serial->dev;
+	struct usb_interface *interface = serial->interface;
+
+	if (interface == udev->actconfig->interface[0] ||
+	    interface == udev->actconfig->interface[1]) {
+		dev_info(&udev->dev, "Ignoring serial port reserved for JTAG\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ftdi_sio_port_remove(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	remove_sysfs_attrs(port);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	/* No error checking for this (will get errors later anyway) */
+	/* See ftdi_sio.h for description of what is reset */
+	usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
+			FTDI_SIO_RESET_SIO,
+			priv->interface, NULL, 0, WDR_TIMEOUT);
+
+	/* Termios defaults are set by usb_serial_init. We don't change
+	   port->tty->termios - this would lose speed settings, etc.
+	   This is same behaviour as serial.c/rs_open() - Kuba */
+
+	/* ftdi_set_termios  will send usb control messages */
+	if (tty)
+		ftdi_set_termios(tty, port, NULL);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	/* Disable flow control */
+	if (!on) {
+		if (usb_control_msg(port->serial->dev,
+			    usb_sndctrlpipe(port->serial->dev, 0),
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+			    0, priv->interface, NULL, 0,
+			    WDR_TIMEOUT) < 0) {
+			dev_err(&port->dev, "error from flowcontrol urb\n");
+		}
+	}
+	/* drop RTS and DTR */
+	if (on)
+		set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+	else
+		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+}
+
+/* The SIO requires the first byte to have:
+ *  B0 1
+ *  B1 0
+ *  B2..7 length of message excluding byte 0
+ *
+ * The new devices do not require this byte
+ */
+static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	struct ftdi_private *priv;
+	int count;
+	unsigned long flags;
+
+	priv = usb_get_serial_port_data(port);
+
+	if (priv->chip_type == SIO) {
+		unsigned char *buffer = dest;
+		int i, len, c;
+
+		count = 0;
+		spin_lock_irqsave(&port->lock, flags);
+		for (i = 0; i < size - 1; i += priv->max_packet_size) {
+			len = min_t(int, size - i, priv->max_packet_size) - 1;
+			c = kfifo_out(&port->write_fifo, &buffer[i + 1], len);
+			if (!c)
+				break;
+			port->icount.tx += c;
+			buffer[i] = (c << 2) + 1;
+			count += c + 1;
+		}
+		spin_unlock_irqrestore(&port->lock, flags);
+	} else {
+		count = kfifo_out_locked(&port->write_fifo, dest, size,
+								&port->lock);
+		port->icount.tx += count;
+	}
+
+	return count;
+}
+
+#define FTDI_RS_ERR_MASK (FTDI_RS_BI | FTDI_RS_PE | FTDI_RS_FE | FTDI_RS_OE)
+
+static int ftdi_process_packet(struct usb_serial_port *port,
+		struct ftdi_private *priv, char *packet, int len)
+{
+	int i;
+	char status;
+	char flag;
+	char *ch;
+
+	if (len < 2) {
+		dev_dbg(&port->dev, "malformed packet\n");
+		return 0;
+	}
+
+	/* Compare new line status to the old one, signal if different/
+	   N.B. packet may be processed more than once, but differences
+	   are only processed once.  */
+	status = packet[0] & FTDI_STATUS_B0_MASK;
+	if (status != priv->prev_status) {
+		char diff_status = status ^ priv->prev_status;
+
+		if (diff_status & FTDI_RS0_CTS)
+			port->icount.cts++;
+		if (diff_status & FTDI_RS0_DSR)
+			port->icount.dsr++;
+		if (diff_status & FTDI_RS0_RI)
+			port->icount.rng++;
+		if (diff_status & FTDI_RS0_RLSD) {
+			struct tty_struct *tty;
+
+			port->icount.dcd++;
+			tty = tty_port_tty_get(&port->port);
+			if (tty)
+				usb_serial_handle_dcd_change(port, tty,
+						status & FTDI_RS0_RLSD);
+			tty_kref_put(tty);
+		}
+
+		wake_up_interruptible(&port->port.delta_msr_wait);
+		priv->prev_status = status;
+	}
+
+	/* save if the transmitter is empty or not */
+	if (packet[1] & FTDI_RS_TEMT)
+		priv->transmit_empty = 1;
+	else
+		priv->transmit_empty = 0;
+
+	len -= 2;
+	if (!len)
+		return 0;	/* status only */
+
+	/*
+	 * Break and error status must only be processed for packets with
+	 * data payload to avoid over-reporting.
+	 */
+	flag = TTY_NORMAL;
+	if (packet[1] & FTDI_RS_ERR_MASK) {
+		/* Break takes precedence over parity, which takes precedence
+		 * over framing errors */
+		if (packet[1] & FTDI_RS_BI) {
+			flag = TTY_BREAK;
+			port->icount.brk++;
+			usb_serial_handle_break(port);
+		} else if (packet[1] & FTDI_RS_PE) {
+			flag = TTY_PARITY;
+			port->icount.parity++;
+		} else if (packet[1] & FTDI_RS_FE) {
+			flag = TTY_FRAME;
+			port->icount.frame++;
+		}
+		/* Overrun is special, not associated with a char */
+		if (packet[1] & FTDI_RS_OE) {
+			port->icount.overrun++;
+			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+		}
+	}
+
+	port->icount.rx += len;
+	ch = packet + 2;
+
+	if (port->port.console && port->sysrq) {
+		for (i = 0; i < len; i++, ch++) {
+			if (!usb_serial_handle_sysrq_char(port, *ch))
+				tty_insert_flip_char(&port->port, *ch, flag);
+		}
+	} else {
+		tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
+	}
+
+	return len;
+}
+
+static void ftdi_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	char *data = (char *)urb->transfer_buffer;
+	int i;
+	int len;
+	int count = 0;
+
+	for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
+		len = min_t(int, urb->actual_length - i, priv->max_packet_size);
+		count += ftdi_process_packet(port, priv, &data[i], len);
+	}
+
+	if (count)
+		tty_flip_buffer_push(&port->port);
+}
+
+static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	u16 value;
+
+	/* break_state = -1 to turn on break, and 0 to turn off break */
+	/* see drivers/char/tty_io.c to see it used */
+	/* last_set_data_value NEVER has the break bit set in it */
+
+	if (break_state)
+		value = priv->last_set_data_value | FTDI_SIO_SET_BREAK;
+	else
+		value = priv->last_set_data_value;
+
+	if (usb_control_msg(port->serial->dev,
+			usb_sndctrlpipe(port->serial->dev, 0),
+			FTDI_SIO_SET_DATA_REQUEST,
+			FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			value , priv->interface,
+			NULL, 0, WDR_TIMEOUT) < 0) {
+		dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
+			__func__, break_state);
+	}
+
+	dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__,
+		break_state, value);
+
+}
+
+static bool ftdi_tx_empty(struct usb_serial_port *port)
+{
+	unsigned char buf[2];
+	int ret;
+
+	ret = ftdi_get_modem_status(port, buf);
+	if (ret == 2) {
+		if (!(buf[1] & FTDI_RS_TEMT))
+			return false;
+	}
+
+	return true;
+}
+
+/* old_termios contains the original termios settings and tty->termios contains
+ * the new setting to be used
+ * WARNING: set_termios calls this with old_termios in kernel space
+ */
+static void ftdi_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct device *ddev = &port->dev;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct ktermios *termios = &tty->termios;
+	unsigned int cflag = termios->c_cflag;
+	u16 value, index;
+	int ret;
+
+	/* Force baud rate if this device requires it, unless it is set to
+	   B0. */
+	if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) {
+		dev_dbg(ddev, "%s: forcing baud rate for this device\n", __func__);
+		tty_encode_baud_rate(tty, priv->force_baud,
+					priv->force_baud);
+	}
+
+	/* Force RTS-CTS if this device requires it. */
+	if (priv->force_rtscts) {
+		dev_dbg(ddev, "%s: forcing rtscts for this device\n", __func__);
+		termios->c_cflag |= CRTSCTS;
+	}
+
+	/*
+	 * All FTDI UART chips are limited to CS7/8. We shouldn't pretend to
+	 * support CS5/6 and revert the CSIZE setting instead.
+	 *
+	 * CS5 however is used to control some smartcard readers which abuse
+	 * this limitation to switch modes. Original FTDI chips fall back to
+	 * eight data bits.
+	 *
+	 * TODO: Implement a quirk to only allow this with mentioned
+	 *       readers. One I know of (Argolis Smartreader V1)
+	 *       returns "USB smartcard server" as iInterface string.
+	 *       The vendor didn't bother with a custom VID/PID of
+	 *       course.
+	 */
+	if (C_CSIZE(tty) == CS6) {
+		dev_warn(ddev, "requested CSIZE setting not supported\n");
+
+		termios->c_cflag &= ~CSIZE;
+		if (old_termios)
+			termios->c_cflag |= old_termios->c_cflag & CSIZE;
+		else
+			termios->c_cflag |= CS8;
+	}
+
+	cflag = termios->c_cflag;
+
+	if (!old_termios)
+		goto no_skip;
+
+	if (old_termios->c_cflag == termios->c_cflag
+	    && old_termios->c_ispeed == termios->c_ispeed
+	    && old_termios->c_ospeed == termios->c_ospeed)
+		goto no_c_cflag_changes;
+
+	/* NOTE These routines can get interrupted by
+	   ftdi_sio_read_bulk_callback  - need to examine what this means -
+	   don't see any problems yet */
+
+	if ((old_termios->c_cflag & (CSIZE|PARODD|PARENB|CMSPAR|CSTOPB)) ==
+	    (termios->c_cflag & (CSIZE|PARODD|PARENB|CMSPAR|CSTOPB)))
+		goto no_data_parity_stop_changes;
+
+no_skip:
+	/* Set number of data bits, parity, stop bits */
+
+	value = 0;
+	value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+			FTDI_SIO_SET_DATA_STOP_BITS_1);
+	if (cflag & PARENB) {
+		if (cflag & CMSPAR)
+			value |= cflag & PARODD ?
+					FTDI_SIO_SET_DATA_PARITY_MARK :
+					FTDI_SIO_SET_DATA_PARITY_SPACE;
+		else
+			value |= cflag & PARODD ?
+					FTDI_SIO_SET_DATA_PARITY_ODD :
+					FTDI_SIO_SET_DATA_PARITY_EVEN;
+	} else {
+		value |= FTDI_SIO_SET_DATA_PARITY_NONE;
+	}
+	switch (cflag & CSIZE) {
+	case CS5:
+		dev_dbg(ddev, "Setting CS5 quirk\n");
+		break;
+	case CS7:
+		value |= 7;
+		dev_dbg(ddev, "Setting CS7\n");
+		break;
+	default:
+	case CS8:
+		value |= 8;
+		dev_dbg(ddev, "Setting CS8\n");
+		break;
+	}
+
+	/* This is needed by the break command since it uses the same command
+	   - but is or'ed with this value  */
+	priv->last_set_data_value = value;
+
+	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			    FTDI_SIO_SET_DATA_REQUEST,
+			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			    value , priv->interface,
+			    NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
+		dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
+			__func__);
+	}
+
+	/* Now do the baudrate */
+no_data_parity_stop_changes:
+	if ((cflag & CBAUD) == B0) {
+		/* Disable flow control */
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0, priv->interface,
+				    NULL, 0, WDR_TIMEOUT) < 0) {
+			dev_err(ddev, "%s error from disable flowcontrol urb\n",
+				__func__);
+		}
+		/* Drop RTS and DTR */
+		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+	} else {
+		/* set the baudrate determined before */
+		mutex_lock(&priv->cfg_lock);
+		if (change_speed(tty, port))
+			dev_err(ddev, "%s urb failed to set baudrate\n", __func__);
+		mutex_unlock(&priv->cfg_lock);
+		/* Ensure RTS and DTR are raised when baudrate changed from 0 */
+		if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+			set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+	}
+
+no_c_cflag_changes:
+	/* Set hardware-assisted flow control */
+	value = 0;
+
+	if (C_CRTSCTS(tty)) {
+		dev_dbg(&port->dev, "enabling rts/cts flow control\n");
+		index = FTDI_SIO_RTS_CTS_HS;
+	} else if (I_IXON(tty)) {
+		dev_dbg(&port->dev, "enabling xon/xoff flow control\n");
+		index = FTDI_SIO_XON_XOFF_HS;
+		value = STOP_CHAR(tty) << 8 | START_CHAR(tty);
+	} else {
+		dev_dbg(&port->dev, "disabling flow control\n");
+		index = FTDI_SIO_DISABLE_FLOW_CTRL;
+	}
+
+	index |= priv->interface;
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+			FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+			value, index, NULL, 0, WDR_TIMEOUT);
+	if (ret < 0)
+		dev_err(&port->dev, "failed to set flow control: %d\n", ret);
+}
+
+/*
+ * Get modem-control status.
+ *
+ * Returns the number of status bytes retrieved (device dependant), or
+ * negative error code.
+ */
+static int ftdi_get_modem_status(struct usb_serial_port *port,
+						unsigned char status[2])
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	unsigned char *buf;
+	int len;
+	int ret;
+
+	buf = kmalloc(2, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	/*
+	 * The 8U232AM returns a two byte value (the SIO a 1 byte value) in
+	 * the same format as the data returned from the in point.
+	 */
+	switch (priv->chip_type) {
+	case SIO:
+		len = 1;
+		break;
+	case FT8U232AM:
+	case FT232BM:
+	case FT2232C:
+	case FT232RL:
+	case FT2232H:
+	case FT4232H:
+	case FT232H:
+	case FTX:
+		len = 2;
+		break;
+	default:
+		ret = -EFAULT;
+		goto out;
+	}
+
+	ret = usb_control_msg(port->serial->dev,
+			usb_rcvctrlpipe(port->serial->dev, 0),
+			FTDI_SIO_GET_MODEM_STATUS_REQUEST,
+			FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+			0, priv->interface,
+			buf, len, WDR_TIMEOUT);
+
+	/* NOTE: We allow short responses and handle that below. */
+	if (ret < 1) {
+		dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+		if (ret >= 0)
+			ret = -EIO;
+		ret = usb_translate_errors(ret);
+		goto out;
+	}
+
+	status[0] = buf[0];
+	if (ret > 1)
+		status[1] = buf[1];
+	else
+		status[1] = 0;
+
+	dev_dbg(&port->dev, "%s - 0x%02x%02x\n", __func__, status[0],
+								status[1]);
+out:
+	kfree(buf);
+
+	return ret;
+}
+
+static int ftdi_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	unsigned char buf[2];
+	int ret;
+
+	ret = ftdi_get_modem_status(port, buf);
+	if (ret < 0)
+		return ret;
+
+	ret =	(buf[0] & FTDI_SIO_DSR_MASK  ? TIOCM_DSR : 0) |
+		(buf[0] & FTDI_SIO_CTS_MASK  ? TIOCM_CTS : 0) |
+		(buf[0] & FTDI_SIO_RI_MASK   ? TIOCM_RI  : 0) |
+		(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0) |
+		priv->last_dtr_rts;
+
+	return ret;
+}
+
+static int ftdi_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	return update_mctrl(port, set, clear);
+}
+
+static int ftdi_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	void __user *argp = (void __user *)arg;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port, argp);
+	case TIOCSSERIAL:
+		return set_serial_info(tty, port, argp);
+	case TIOCSERGETLSR:
+		return get_lsr_info(port, argp);
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override");
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
new file mode 100644
index 0000000..dcd0b6e
--- /dev/null
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -0,0 +1,565 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver definitions for the FTDI USB Single Port Serial Converter -
+ * known as FTDI_SIO (Serial Input/Output application of the chipset)
+ *
+ * For USB vendor/product IDs (VID/PID), please see ftdi_sio_ids.h
+ *
+ *
+ * The example I have is known as the USC-1000 which is available from
+ * http://www.dse.co.nz - cat no XH4214 It looks similar to this:
+ * http://www.dansdata.com/usbser.htm but I can't be sure There are other
+ * USC-1000s which don't look like my device though so beware!
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdichip.com) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the
+ * FTDI_SIO implementation.
+ *
+ */
+
+/* Commands */
+#define FTDI_SIO_RESET			0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL		1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL		2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE		3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA		4 /* Set the data characteristics of
+					     the port */
+#define FTDI_SIO_GET_MODEM_STATUS	5 /* Retrieve current value of modem
+					     status register */
+#define FTDI_SIO_SET_EVENT_CHAR		6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR		7 /* Set the error character */
+#define FTDI_SIO_SET_LATENCY_TIMER	9 /* Set the latency timer */
+#define FTDI_SIO_GET_LATENCY_TIMER	10 /* Get the latency timer */
+
+/* Interface indices for FT2232, FT2232H and FT4232H devices */
+#define INTERFACE_A		1
+#define INTERFACE_B		2
+#define INTERFACE_C		3
+#define INTERFACE_D		4
+
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_E2_READ
+ *   wValue:         0
+ *   wIndex:         Address of word to read
+ *   wLength:        2
+ *   Data:           Will return a word of data from E2Address
+ *
+ */
+
+/* Port Identifier Table */
+#define PIT_DEFAULT		0 /* SIOA */
+#define PIT_SIOA		1 /* SIOA */
+/* The device this driver is tested with one has only one port */
+#define PIT_SIOB		2 /* SIOB */
+#define PIT_PARALLEL		3 /* Parallel */
+
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET
+#define FTDI_SIO_RESET_REQUEST_TYPE 0x40
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_RESET
+ * wValue:         Control Value
+ *                   0 = Reset SIO
+ *                   1 = Purge RX buffer
+ *                   2 = Purge TX buffer
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           None
+ *
+ * The Reset SIO command has this effect:
+ *
+ *    Sets flow control set to 'none'
+ *    Event char = $0D
+ *    Event trigger = disabled
+ *    Purge RX buffer
+ *    Purge TX buffer
+ *    Clear DTR
+ *    Clear RTS
+ *    baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+   */
+
+/* FTDI_SIO_SET_BAUDRATE */
+#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_SET_BAUDRATE
+ * wValue:         BaudDivisor value - see below
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           None
+ * The BaudDivisor values are calculated as follows:
+ * - BaseClock is either 12000000 or 48000000 depending on the device.
+ *   FIXME: I wish I knew how to detect old chips to select proper base clock!
+ * - BaudDivisor is a fixed point number encoded in a funny way.
+ *   (--WRONG WAY OF THINKING--)
+ *   BaudDivisor is a fixed point number encoded with following bit weighs:
+ *   (-2)(-1)(13..0). It is a radical with a denominator of 4, so values
+ *   end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...).
+ *   (--THE REALITY--)
+ *   The both-bits-set has quite different meaning from 0.75 - the chip
+ *   designers have decided it to mean 0.125 instead of 0.75.
+ *   This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates
+ *   and Flow Control Consideration for USB to RS232".
+ * - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should
+ *   automagically re-encode the resulting value to take fractions into
+ *   consideration.
+ * As all values are integers, some bit twiddling is in order:
+ *   BaudDivisor = (BaseClock / 16 / BaudRate) |
+ *   (((BaseClock / 2 / BaudRate) & 4) ? 0x4000    // 0.5
+ *    : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000  // 0.25
+ *    : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000  // 0.125
+ *    : 0)
+ *
+ * For the FT232BM, a 17th divisor bit was introduced to encode the multiples
+ * of 0.125 missing from the FT8U232AM.  Bits 16 to 14 are coded as follows
+ * (the first four codes are the same as for the FT8U232AM, where bit 16 is
+ * always 0):
+ *   000 - add .000 to divisor
+ *   001 - add .500 to divisor
+ *   010 - add .250 to divisor
+ *   011 - add .125 to divisor
+ *   100 - add .375 to divisor
+ *   101 - add .625 to divisor
+ *   110 - add .750 to divisor
+ *   111 - add .875 to divisor
+ * Bits 15 to 0 of the 17-bit divisor are placed in the urb value.  Bit 16 is
+ * placed in bit 0 of the urb index.
+ *
+ * Note that there are a couple of special cases to support the highest baud
+ * rates.  If the calculated divisor value is 1, this needs to be replaced with
+ * 0.  Additionally for the FT232BM, if the calculated divisor value is 0x4001
+ * (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
+ * not supported by the FT8U232AM).
+ */
+
+enum ftdi_chip_type {
+	SIO = 1,
+	FT8U232AM = 2,
+	FT232BM = 3,
+	FT2232C = 4,
+	FT232RL = 5,
+	FT2232H = 6,
+	FT4232H = 7,
+	FT232H  = 8,
+	FTX     = 9,
+};
+
+enum ftdi_sio_baudrate {
+	ftdi_sio_b300 = 0,
+	ftdi_sio_b600 = 1,
+	ftdi_sio_b1200 = 2,
+	ftdi_sio_b2400 = 3,
+	ftdi_sio_b4800 = 4,
+	ftdi_sio_b9600 = 5,
+	ftdi_sio_b19200 = 6,
+	ftdi_sio_b38400 = 7,
+	ftdi_sio_b57600 = 8,
+	ftdi_sio_b115200 = 9
+};
+
+/*
+ * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor
+ * values are calculated internally.
+ */
+#define FTDI_SIO_SET_DATA_REQUEST	FTDI_SIO_SET_DATA
+#define FTDI_SIO_SET_DATA_REQUEST_TYPE	0x40
+#define FTDI_SIO_SET_DATA_PARITY_NONE	(0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD	(0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN	(0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK	(0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE	(0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1	(0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15	(0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2	(0x2 << 11)
+#define FTDI_SIO_SET_BREAK		(0x1 << 14)
+/* FTDI_SIO_SET_DATA */
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_SET_DATA
+ * wValue:         Data characteristics (see below)
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           No
+ *
+ * Data characteristics
+ *
+ *   B0..7   Number of data bits
+ *   B8..10  Parity
+ *           0 = None
+ *           1 = Odd
+ *           2 = Even
+ *           3 = Mark
+ *           4 = Space
+ *   B11..13 Stop Bits
+ *           0 = 1
+ *           1 = 1.5
+ *           2 = 2
+ *   B14
+ *           1 = TX ON (break)
+ *           0 = TX OFF (normal state)
+ *   B15 Reserved
+ *
+ */
+
+
+
+/* FTDI_SIO_MODEM_CTRL */
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL
+
+/*
+ * BmRequestType:   0100 0000B
+ * bRequest:        FTDI_SIO_MODEM_CTRL
+ * wValue:          ControlValue (see below)
+ * wIndex:          Port
+ * wLength:         0
+ * Data:            None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ */
+
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK  << 8) | 1)
+#define FTDI_SIO_SET_DTR_LOW  ((FTDI_SIO_SET_DTR_MASK  << 8) | 0)
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2)
+#define FTDI_SIO_SET_RTS_LOW  ((FTDI_SIO_SET_RTS_MASK << 8) | 0)
+
+/*
+ * ControlValue
+ * B0    DTR state
+ *          0 = reset
+ *          1 = set
+ * B1    RTS state
+ *          0 = reset
+ *          1 = set
+ * B2..7 Reserved
+ * B8    DTR state enable
+ *          0 = ignore
+ *          1 = use DTR state
+ * B9    RTS state enable
+ *          0 = ignore
+ *          1 = use RTS state
+ * B10..15 Reserved
+ */
+
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
+#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
+#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
+/*
+ *   BmRequestType:  0100 0000b
+ *   bRequest:       FTDI_SIO_SET_FLOW_CTRL
+ *   wValue:         Xoff/Xon
+ *   wIndex:         Protocol/Port - hIndex is protocol / lIndex is port
+ *   wLength:        0
+ *   Data:           None
+ *
+ * hIndex protocol is:
+ *   B0 Output handshaking using RTS/CTS
+ *       0 = disabled
+ *       1 = enabled
+ *   B1 Output handshaking using DTR/DSR
+ *       0 = disabled
+ *       1 = enabled
+ *   B2 Xon/Xoff handshaking
+ *       0 = disabled
+ *       1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+
+/*
+ * FTDI_SIO_GET_LATENCY_TIMER
+ *
+ * Set the timeout interval. The FTDI collects data from the slave
+ * device, transmitting it to the host when either A) 62 bytes are
+ * received, or B) the timeout interval has elapsed and the buffer
+ * contains at least 1 byte.  Setting this value to a small number
+ * can dramatically improve performance for applications which send
+ * small packets, since the default value is 16ms.
+ */
+#define  FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER
+#define  FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0
+
+/*
+ *  BmRequestType:   1100 0000b
+ *  bRequest:        FTDI_SIO_GET_LATENCY_TIMER
+ *  wValue:          0
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            latency (on return)
+ */
+
+/*
+ * FTDI_SIO_SET_LATENCY_TIMER
+ *
+ * Set the timeout interval. The FTDI collects data from the slave
+ * device, transmitting it to the host when either A) 62 bytes are
+ * received, or B) the timeout interval has elapsed and the buffer
+ * contains at least 1 byte.  Setting this value to a small number
+ * can dramatically improve performance for applications which send
+ * small packets, since the default value is 16ms.
+ */
+#define  FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
+#define  FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
+
+/*
+ *  BmRequestType:   0100 0000b
+ *  bRequest:        FTDI_SIO_SET_LATENCY_TIMER
+ *  wValue:          Latency (milliseconds)
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            None
+ *
+ * wValue:
+ *   B0..7   Latency timer
+ *   B8..15  0
+ *
+ */
+
+/*
+ * FTDI_SIO_SET_EVENT_CHAR
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+
+#define  FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR
+#define  FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40
+
+
+/*
+ *  BmRequestType:   0100 0000b
+ *  bRequest:        FTDI_SIO_SET_EVENT_CHAR
+ *  wValue:          EventChar
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            None
+ *
+ * wValue:
+ *   B0..7   Event Character
+ *   B8      Event Character Processing
+ *             0 = disabled
+ *             1 = enabled
+ *   B9..15  Reserved
+ *
+ */
+
+/* FTDI_SIO_SET_ERROR_CHAR */
+
+/*
+ * Set the parity error replacement character for the specified communications
+ * port
+ */
+
+/*
+ *  BmRequestType:  0100 0000b
+ *  bRequest:       FTDI_SIO_SET_EVENT_CHAR
+ *  wValue:         Error Char
+ *  wIndex:         Port
+ *  wLength:        0
+ *  Data:           None
+ *
+ *Error Char
+ *  B0..7  Error Character
+ *  B8     Error Character Processing
+ *           0 = disabled
+ *           1 = enabled
+ *  B9..15 Reserved
+ *
+ */
+
+/* FTDI_SIO_GET_MODEM_STATUS */
+/* Retrieve the current value of the modem status register */
+
+#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0
+#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK  0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+/*
+ *   BmRequestType:   1100 0000b
+ *   bRequest:        FTDI_SIO_GET_MODEM_STATUS
+ *   wValue:          zero
+ *   wIndex:          Port
+ *   wLength:         1
+ *   Data:            Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4    CTS
+ *         0 = inactive
+ *         1 = active
+ * B5    DSR
+ *         0 = inactive
+ *         1 = active
+ * B6    Ring Indicator (RI)
+ *         0 = inactive
+ *         1 = active
+ * B7    Receive Line Signal Detect (RLSD)
+ *         0 = inactive
+ *         1 = active
+ */
+
+
+
+/* Descriptors returned by the device
+ *
+ *  Device Descriptor
+ *
+ * Offset	Field		Size	Value	Description
+ * 0	bLength		1	0x12	Size of descriptor in bytes
+ * 1	bDescriptorType	1	0x01	DEVICE Descriptor Type
+ * 2	bcdUSB		2	0x0110	USB Spec Release Number
+ * 4	bDeviceClass	1	0x00	Class Code
+ * 5	bDeviceSubClass	1	0x00	SubClass Code
+ * 6	bDeviceProtocol	1	0x00	Protocol Code
+ * 7	bMaxPacketSize0 1	0x08	Maximum packet size for endpoint 0
+ * 8	idVendor	2	0x0403	Vendor ID
+ * 10	idProduct	2	0x8372	Product ID (FTDI_SIO_PID)
+ * 12	bcdDevice	2	0x0001	Device release number
+ * 14	iManufacturer	1	0x01	Index of man. string desc
+ * 15	iProduct	1	0x02	Index of prod string desc
+ * 16	iSerialNumber	1	0x02	Index of serial nmr string desc
+ * 17	bNumConfigurations 1    0x01	Number of possible configurations
+ *
+ * Configuration Descriptor
+ *
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x09	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x02	CONFIGURATION Descriptor Type
+ * 2	wTotalLength		2	0x0020	Total length of data
+ * 4	bNumInterfaces		1	0x01	Number of interfaces supported
+ * 5	bConfigurationValue	1	0x01	Argument for SetCOnfiguration() req
+ * 6	iConfiguration		1	0x02	Index of config string descriptor
+ * 7	bmAttributes		1	0x20	Config characteristics Remote Wakeup
+ * 8	MaxPower		1	0x1E	Max power consumption
+ *
+ * Interface Descriptor
+ *
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x09	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x04	INTERFACE Descriptor Type
+ * 2	bInterfaceNumber	1	0x00	Number of interface
+ * 3	bAlternateSetting	1	0x00	Value used to select alternate
+ * 4	bNumEndpoints		1	0x02	Number of endpoints
+ * 5	bInterfaceClass		1	0xFF	Class Code
+ * 6	bInterfaceSubClass	1	0xFF	Subclass Code
+ * 7	bInterfaceProtocol	1	0xFF	Protocol Code
+ * 8	iInterface		1	0x02	Index of interface string description
+ *
+ * IN Endpoint Descriptor
+ *
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x07	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x05	ENDPOINT descriptor type
+ * 2	bEndpointAddress	1	0x82	Address of endpoint
+ * 3	bmAttributes		1	0x02	Endpoint attributes - Bulk
+ * 4	bNumEndpoints		2	0x0040	maximum packet size
+ * 5	bInterval		1	0x00	Interval for polling endpoint
+ *
+ * OUT Endpoint Descriptor
+ *
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x07	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x05	ENDPOINT descriptor type
+ * 2	bEndpointAddress	1	0x02	Address of endpoint
+ * 3	bmAttributes		1	0x02	Endpoint attributes - Bulk
+ * 4	bNumEndpoints		2	0x0040	maximum packet size
+ * 5	bInterval		1	0x00	Interval for polling endpoint
+ *
+ * DATA FORMAT
+ *
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms
+ *
+ * Byte 0: Modem Status
+ *
+ * Offset	Description
+ * B0	Reserved - must be 1
+ * B1	Reserved - must be 0
+ * B2	Reserved - must be 0
+ * B3	Reserved - must be 0
+ * B4	Clear to Send (CTS)
+ * B5	Data Set Ready (DSR)
+ * B6	Ring Indicator (RI)
+ * B7	Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ *
+ * Offset	Description
+ * B0	Data Ready (DR)
+ * B1	Overrun Error (OE)
+ * B2	Parity Error (PE)
+ * B3	Framing Error (FE)
+ * B4	Break Interrupt (BI)
+ * B5	Transmitter Holding Register (THRE)
+ * B6	Transmitter Empty (TEMT)
+ * B7	Error in RCVR FIFO
+ *
+ */
+#define FTDI_RS0_CTS	(1 << 4)
+#define FTDI_RS0_DSR	(1 << 5)
+#define FTDI_RS0_RI	(1 << 6)
+#define FTDI_RS0_RLSD	(1 << 7)
+
+#define FTDI_RS_DR	1
+#define FTDI_RS_OE	(1<<1)
+#define FTDI_RS_PE	(1<<2)
+#define FTDI_RS_FE	(1<<3)
+#define FTDI_RS_BI	(1<<4)
+#define FTDI_RS_THRE	(1<<5)
+#define FTDI_RS_TEMT	(1<<6)
+#define FTDI_RS_FIFO	(1<<7)
+
+/*
+ * OUT Endpoint
+ *
+ * This device reserves the first bytes of data on this endpoint contain the
+ * length and port identifier of the message. For the FTDI USB Serial converter
+ * the port identifier is always 1.
+ *
+ * Byte 0: Line Status
+ *
+ * Offset	Description
+ * B0	Reserved - must be 1
+ * B1	Reserved - must be 0
+ * B2..7	Length of message - (not including Byte 0)
+ *
+ */
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
new file mode 100644
index 0000000..975d026
--- /dev/null
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -0,0 +1,1537 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * vendor/product IDs (VID/PID) of devices using FTDI USB serial converters.
+ * Please keep numerically sorted within individual areas, thanks!
+ *
+ * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais
+ * from Rudolf Gugler
+ *
+ */
+
+
+/**********************************/
+/***** devices using FTDI VID *****/
+/**********************************/
+
+
+#define FTDI_VID	0x0403	/* Vendor Id */
+
+
+/*** "original" FTDI device PIDs ***/
+
+#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
+#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
+#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
+#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
+#define FTDI_232H_PID  0x6014 /* Single channel hi-speed device */
+#define FTDI_FTX_PID   0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */
+#define FTDI_SIO_PID	0x8372	/* Product Id SIO application of 8U100AX */
+#define FTDI_232RL_PID  0xFBFA  /* Product ID for FT232RL */
+
+
+/*** third-party PIDs (using FTDI_VID) ***/
+
+/*
+ * Certain versions of the official Windows FTDI driver reprogrammed
+ * counterfeit FTDI devices to PID 0. Support these devices anyway.
+ */
+#define FTDI_BRICK_PID		0x0000
+
+#define FTDI_LUMEL_PD12_PID	0x6002
+
+/* Cyber Cortex AV by Fabulous Silicon (http://fabuloussilicon.com) */
+#define CYBER_CORTEX_AV_PID	0x8698
+
+/*
+ * Marvell OpenRD Base, Client
+ * http://www.open-rd.org
+ * OpenRD Base, Client use VID 0x0403
+ */
+#define MARVELL_OPENRD_PID	0x9e90
+
+/* www.candapter.com Ewert Energy Systems CANdapter device */
+#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */
+
+#define FTDI_BM_ATOM_NANO_PID	0xa559	/* Basic Micro ATOM Nano USB2Serial */
+
+/*
+ * Texas Instruments XDS100v2 JTAG / BeagleBone A3
+ * http://processors.wiki.ti.com/index.php/XDS100
+ * http://beagleboard.org/bone
+ */
+#define TI_XDS100V2_PID		0xa6d0
+
+#define FTDI_NXTCAM_PID		0xABB8 /* NXTCam for Mindstorms NXT */
+#define FTDI_EV3CON_PID		0xABB9 /* Mindstorms EV3 Console Adapter */
+
+/* US Interface Navigator (http://www.usinterface.com/) */
+#define FTDI_USINT_CAT_PID	0xb810	/* Navigator CAT and 2nd PTT lines */
+#define FTDI_USINT_WKEY_PID	0xb811	/* Navigator WKEY and FSK lines */
+#define FTDI_USINT_RS232_PID	0xb812	/* Navigator RS232 and CONFIG lines */
+
+/* OOCDlink by Joern Kaipf <joernk@web.de>
+ * (http://www.joernonline.de/) */
+#define FTDI_OOCDLINK_PID	0xbaf8	/* Amontec JTAGkey */
+
+/* Luminary Micro Stellaris Boards, VID = FTDI_VID */
+/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */
+#define LMI_LM3S_DEVEL_BOARD_PID	0xbcd8
+#define LMI_LM3S_EVAL_BOARD_PID		0xbcd9
+#define LMI_LM3S_ICDI_BOARD_PID		0xbcda
+
+#define FTDI_TURTELIZER_PID	0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */
+
+/* OpenDCC (www.opendcc.de) product id */
+#define FTDI_OPENDCC_PID	0xBFD8
+#define FTDI_OPENDCC_SNIFFER_PID	0xBFD9
+#define FTDI_OPENDCC_THROTTLE_PID	0xBFDA
+#define FTDI_OPENDCC_GATEWAY_PID	0xBFDB
+#define FTDI_OPENDCC_GBM_PID	0xBFDC
+#define FTDI_OPENDCC_GBM_BOOST_PID	0xBFDD
+
+/* NZR SEM 16+ USB (http://www.nzr.de) */
+#define FTDI_NZR_SEM_USB_PID	0xC1E0	/* NZR SEM-LOG16+ */
+
+/*
+ * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
+ */
+#define FTDI_RRCIRKITS_LOCOBUFFER_PID	0xc7d0	/* LocoBuffer USB */
+
+/* DMX4ALL DMX Interfaces */
+#define FTDI_DMX4ALL 0xC850
+
+/*
+ * ASK.fr devices
+ */
+#define FTDI_ASK_RDR400_PID	0xC991	/* ASK RDR 400 series card reader */
+
+/* www.starting-point-systems.com µChameleon device */
+#define FTDI_MICRO_CHAMELEON_PID	0xCAA0	/* Product Id */
+
+/*
+ * Tactrix OpenPort (ECU) devices.
+ * OpenPort 1.3M submitted by Donour Sizemore.
+ * OpenPort 1.3S and 1.3U submitted by Ian Abbott.
+ */
+#define FTDI_TACTRIX_OPENPORT_13M_PID	0xCC48	/* OpenPort 1.3 Mitsubishi */
+#define FTDI_TACTRIX_OPENPORT_13S_PID	0xCC49	/* OpenPort 1.3 Subaru */
+#define FTDI_TACTRIX_OPENPORT_13U_PID	0xCC4A	/* OpenPort 1.3 Universal */
+
+#define FTDI_DISTORTEC_JTAG_LOCK_PICK_PID	0xCFF8
+
+/* SCS HF Radio Modems PID's (http://www.scs-ptc.com) */
+/* the VID is the standard ftdi vid (FTDI_VID) */
+#define FTDI_SCS_DEVICE_0_PID 0xD010    /* SCS PTC-IIusb */
+#define FTDI_SCS_DEVICE_1_PID 0xD011    /* SCS Tracker / DSP TNC */
+#define FTDI_SCS_DEVICE_2_PID 0xD012
+#define FTDI_SCS_DEVICE_3_PID 0xD013
+#define FTDI_SCS_DEVICE_4_PID 0xD014
+#define FTDI_SCS_DEVICE_5_PID 0xD015
+#define FTDI_SCS_DEVICE_6_PID 0xD016
+#define FTDI_SCS_DEVICE_7_PID 0xD017
+
+/* iPlus device */
+#define FTDI_IPLUS_PID 0xD070 /* Product Id */
+#define FTDI_IPLUS2_PID 0xD071 /* Product Id */
+
+/*
+ * Gamma Scout (http://gamma-scout.com/). Submitted by rsc@runtux.com.
+ */
+#define FTDI_GAMMA_SCOUT_PID		0xD678	/* Gamma Scout online */
+
+/* Propox devices */
+#define FTDI_PROPOX_JTAGCABLEII_PID	0xD738
+#define FTDI_PROPOX_ISPCABLEIII_PID	0xD739
+
+/* Lenz LI-USB Computer Interface. */
+#define FTDI_LENZ_LIUSB_PID	0xD780
+
+/* Vardaan Enterprises Serial Interface VEUSB422R3 */
+#define FTDI_VARDAAN_PID	0xF070
+
+/*
+ * Xsens Technologies BV products (http://www.xsens.com).
+ */
+#define XSENS_VID		0x2639
+#define XSENS_AWINDA_STATION_PID 0x0101
+#define XSENS_AWINDA_DONGLE_PID 0x0102
+#define XSENS_MTW_PID		0x0200	/* Xsens MTw */
+#define XSENS_MTDEVBOARD_PID	0x0300	/* Motion Tracker Development Board */
+#define XSENS_CONVERTER_PID	0xD00D	/* Xsens USB-serial converter */
+
+/* Xsens devices using FTDI VID */
+#define XSENS_CONVERTER_0_PID	0xD388	/* Xsens USB converter */
+#define XSENS_CONVERTER_1_PID	0xD389	/* Xsens Wireless Receiver */
+#define XSENS_CONVERTER_2_PID	0xD38A
+#define XSENS_CONVERTER_3_PID	0xD38B	/* Xsens USB-serial converter */
+#define XSENS_CONVERTER_4_PID	0xD38C	/* Xsens Wireless Receiver */
+#define XSENS_CONVERTER_5_PID	0xD38D	/* Xsens Awinda Station */
+#define XSENS_CONVERTER_6_PID	0xD38E
+#define XSENS_CONVERTER_7_PID	0xD38F
+
+/**
+ * Zolix (www.zolix.com.cb) product ids
+ */
+#define FTDI_OMNI1509			0xD491	/* Omni1509 embedded USB-serial */
+
+/*
+ * NDI (www.ndigital.com) product ids
+ */
+#define FTDI_NDI_HUC_PID		0xDA70	/* NDI Host USB Converter */
+#define FTDI_NDI_SPECTRA_SCU_PID	0xDA71	/* NDI Spectra SCU */
+#define FTDI_NDI_FUTURE_2_PID		0xDA72	/* NDI future device #2 */
+#define FTDI_NDI_FUTURE_3_PID		0xDA73	/* NDI future device #3 */
+#define FTDI_NDI_AURORA_SCU_PID		0xDA74	/* NDI Aurora SCU */
+
+/*
+ * ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs
+ */
+#define FTDI_CHAMSYS_24_MASTER_WING_PID        0xDAF8
+#define FTDI_CHAMSYS_PC_WING_PID       0xDAF9
+#define FTDI_CHAMSYS_USB_DMX_PID       0xDAFA
+#define FTDI_CHAMSYS_MIDI_TIMECODE_PID 0xDAFB
+#define FTDI_CHAMSYS_MINI_WING_PID     0xDAFC
+#define FTDI_CHAMSYS_MAXI_WING_PID     0xDAFD
+#define FTDI_CHAMSYS_MEDIA_WING_PID    0xDAFE
+#define FTDI_CHAMSYS_WING_PID  0xDAFF
+
+/*
+ * Westrex International devices submitted by Cory Lee
+ */
+#define FTDI_WESTREX_MODEL_777_PID	0xDC00	/* Model 777 */
+#define FTDI_WESTREX_MODEL_8900F_PID	0xDC01	/* Model 8900F */
+
+/*
+ * ACG Identification Technologies GmbH products (http://www.acg.de/).
+ * Submitted by anton -at- goto10 -dot- org.
+ */
+#define FTDI_ACG_HFDUAL_PID		0xDD20	/* HF Dual ISO Reader (RFID) */
+
+/*
+ * Definitions for Artemis astronomical USB based cameras
+ * Check it at http://www.artemisccd.co.uk/
+ */
+#define FTDI_ARTEMIS_PID	0xDF28	/* All Artemis Cameras */
+
+/*
+ * Definitions for ATIK Instruments astronomical USB based cameras
+ * Check it at http://www.atik-instruments.com/
+ */
+#define FTDI_ATIK_ATK16_PID	0xDF30	/* ATIK ATK-16 Grayscale Camera */
+#define FTDI_ATIK_ATK16C_PID	0xDF32	/* ATIK ATK-16C Colour Camera */
+#define FTDI_ATIK_ATK16HR_PID	0xDF31	/* ATIK ATK-16HR Grayscale Camera */
+#define FTDI_ATIK_ATK16HRC_PID	0xDF33	/* ATIK ATK-16HRC Colour Camera */
+#define FTDI_ATIK_ATK16IC_PID   0xDF35  /* ATIK ATK-16IC Grayscale Camera */
+
+/*
+ * Yost Engineering, Inc. products (www.yostengineering.com).
+ * PID 0xE050 submitted by Aaron Prose.
+ */
+#define FTDI_YEI_SERVOCENTER31_PID	0xE050	/* YEI ServoCenter3.1 USB */
+
+/*
+ * ELV USB devices submitted by Christian Abt of ELV (www.elv.de).
+ * Almost all of these devices use FTDI's vendor ID (0x0403).
+ * Further IDs taken from ELV Windows .inf file.
+ *
+ * The previously included PID for the UO 100 module was incorrect.
+ * In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58).
+ *
+ * Armin Laeuger originally sent the PID for the UM 100 module.
+ */
+#define FTDI_ELV_VID	0x1B1F	/* ELV AG */
+#define FTDI_ELV_WS300_PID	0xC006	/* eQ3 WS 300 PC II */
+#define FTDI_ELV_USR_PID	0xE000	/* ELV Universal-Sound-Recorder */
+#define FTDI_ELV_MSM1_PID	0xE001	/* ELV Mini-Sound-Modul */
+#define FTDI_ELV_KL100_PID	0xE002	/* ELV Kfz-Leistungsmesser KL 100 */
+#define FTDI_ELV_WS550_PID	0xE004	/* WS 550 */
+#define FTDI_ELV_EC3000_PID	0xE006	/* ENERGY CONTROL 3000 USB */
+#define FTDI_ELV_WS888_PID	0xE008	/* WS 888 */
+#define FTDI_ELV_TWS550_PID	0xE009	/* Technoline WS 550 */
+#define FTDI_ELV_FEM_PID	0xE00A	/* Funk Energie Monitor */
+#define FTDI_ELV_FHZ1300PC_PID	0xE0E8	/* FHZ 1300 PC */
+#define FTDI_ELV_WS500_PID	0xE0E9	/* PC-Wetterstation (WS 500) */
+#define FTDI_ELV_HS485_PID	0xE0EA	/* USB to RS-485 adapter */
+#define FTDI_ELV_UMS100_PID	0xE0EB	/* ELV USB Master-Slave Schaltsteckdose UMS 100 */
+#define FTDI_ELV_TFD128_PID	0xE0EC	/* ELV Temperatur-Feuchte-Datenlogger TFD 128 */
+#define FTDI_ELV_FM3RX_PID	0xE0ED	/* ELV Messwertuebertragung FM3 RX */
+#define FTDI_ELV_WS777_PID	0xE0EE	/* Conrad WS 777 */
+#define FTDI_ELV_EM1010PC_PID	0xE0EF	/* Energy monitor EM 1010 PC */
+#define FTDI_ELV_CSI8_PID	0xE0F0	/* Computer-Schalt-Interface (CSI 8) */
+#define FTDI_ELV_EM1000DL_PID	0xE0F1	/* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
+#define FTDI_ELV_PCK100_PID	0xE0F2	/* PC-Kabeltester (PCK 100) */
+#define FTDI_ELV_RFP500_PID	0xE0F3	/* HF-Leistungsmesser (RFP 500) */
+#define FTDI_ELV_FS20SIG_PID	0xE0F4	/* Signalgeber (FS 20 SIG) */
+#define FTDI_ELV_UTP8_PID	0xE0F5	/* ELV UTP 8 */
+#define FTDI_ELV_WS300PC_PID	0xE0F6	/* PC-Wetterstation (WS 300 PC) */
+#define FTDI_ELV_WS444PC_PID	0xE0F7	/* Conrad WS 444 PC */
+#define FTDI_PHI_FISCO_PID      0xE40B  /* PHI Fisco USB to Serial cable */
+#define FTDI_ELV_UAD8_PID	0xF068	/* USB-AD-Wandler (UAD 8) */
+#define FTDI_ELV_UDA7_PID	0xF069	/* USB-DA-Wandler (UDA 7) */
+#define FTDI_ELV_USI2_PID	0xF06A	/* USB-Schrittmotoren-Interface (USI 2) */
+#define FTDI_ELV_T1100_PID	0xF06B	/* Thermometer (T 1100) */
+#define FTDI_ELV_PCD200_PID	0xF06C	/* PC-Datenlogger (PCD 200) */
+#define FTDI_ELV_ULA200_PID	0xF06D	/* USB-LCD-Ansteuerung (ULA 200) */
+#define FTDI_ELV_ALC8500_PID	0xF06E	/* ALC 8500 Expert */
+#define FTDI_ELV_FHZ1000PC_PID	0xF06F	/* FHZ 1000 PC */
+#define FTDI_ELV_UR100_PID	0xFB58	/* USB-RS232-Umsetzer (UR 100) */
+#define FTDI_ELV_UM100_PID	0xFB5A	/* USB-Modul UM 100 */
+#define FTDI_ELV_UO100_PID	0xFB5B	/* USB-Modul UO 100 */
+/* Additional ELV PIDs that default to using the FTDI D2XX drivers on
+ * MS Windows, rather than the FTDI Virtual Com Port drivers.
+ * Maybe these will be easier to use with the libftdi/libusb user-space
+ * drivers, or possibly the Comedi drivers in some cases. */
+#define FTDI_ELV_CLI7000_PID	0xFB59	/* Computer-Light-Interface (CLI 7000) */
+#define FTDI_ELV_PPS7330_PID	0xFB5C	/* Processor-Power-Supply (PPS 7330) */
+#define FTDI_ELV_TFM100_PID	0xFB5D	/* Temperatur-Feuchte-Messgeraet (TFM 100) */
+#define FTDI_ELV_UDF77_PID	0xFB5E	/* USB DCF Funkuhr (UDF 77) */
+#define FTDI_ELV_UIO88_PID	0xFB5F	/* USB-I/O Interface (UIO 88) */
+
+/*
+ * EVER Eco Pro UPS (http://www.ever.com.pl/)
+ */
+
+#define	EVER_ECO_PRO_CDS	0xe520	/* RS-232 converter */
+
+/*
+ * Active Robots product ids.
+ */
+#define FTDI_ACTIVE_ROBOTS_PID	0xE548	/* USB comms board */
+
+/* Pyramid Computer GmbH */
+#define FTDI_PYRAMID_PID	0xE6C8	/* Pyramid Appliance Display */
+
+/* www.elsterelectricity.com Elster Unicom III Optical Probe */
+#define FTDI_ELSTER_UNICOM_PID		0xE700 /* Product Id */
+
+/*
+ * Gude Analog- und Digitalsysteme GmbH
+ */
+#define FTDI_GUDEADS_E808_PID    0xE808
+#define FTDI_GUDEADS_E809_PID    0xE809
+#define FTDI_GUDEADS_E80A_PID    0xE80A
+#define FTDI_GUDEADS_E80B_PID    0xE80B
+#define FTDI_GUDEADS_E80C_PID    0xE80C
+#define FTDI_GUDEADS_E80D_PID    0xE80D
+#define FTDI_GUDEADS_E80E_PID    0xE80E
+#define FTDI_GUDEADS_E80F_PID    0xE80F
+#define FTDI_GUDEADS_E888_PID    0xE888  /* Expert ISDN Control USB */
+#define FTDI_GUDEADS_E889_PID    0xE889  /* USB RS-232 OptoBridge */
+#define FTDI_GUDEADS_E88A_PID    0xE88A
+#define FTDI_GUDEADS_E88B_PID    0xE88B
+#define FTDI_GUDEADS_E88C_PID    0xE88C
+#define FTDI_GUDEADS_E88D_PID    0xE88D
+#define FTDI_GUDEADS_E88E_PID    0xE88E
+#define FTDI_GUDEADS_E88F_PID    0xE88F
+
+/*
+ * Eclo (http://www.eclo.pt/) product IDs.
+ * PID 0xEA90 submitted by Martin Grill.
+ */
+#define FTDI_ECLO_COM_1WIRE_PID	0xEA90	/* COM to 1-Wire USB adaptor */
+
+/* TNC-X USB-to-packet-radio adapter, versions prior to 3.0 (DLP module) */
+#define FTDI_TNC_X_PID		0xEBE0
+
+/*
+ * Teratronik product ids.
+ * Submitted by O. Wölfelschneider.
+ */
+#define FTDI_TERATRONIK_VCP_PID	 0xEC88	/* Teratronik device (preferring VCP driver on windows) */
+#define FTDI_TERATRONIK_D2XX_PID 0xEC89	/* Teratronik device (preferring D2XX driver on windows) */
+
+/* Rig Expert Ukraine devices */
+#define FTDI_REU_TINY_PID		0xED22	/* RigExpert Tiny */
+
+/*
+ * Hameg HO820 and HO870 interface (using VID 0x0403)
+ */
+#define HAMEG_HO820_PID			0xed74
+#define HAMEG_HO730_PID			0xed73
+#define HAMEG_HO720_PID			0xed72
+#define HAMEG_HO870_PID			0xed71
+
+/*
+ *  MaxStream devices	www.maxstream.net
+ */
+#define FTDI_MAXSTREAM_PID	0xEE18	/* Xbee PKG-U Module */
+
+/*
+ * microHAM product IDs (http://www.microham.com).
+ * Submitted by Justin Burket (KL1RL) <zorton@jtan.com>
+ * and Mike Studer (K6EEP) <k6eep@hamsoftware.org>.
+ * Ian Abbott <abbotti@mev.co.uk> added a few more from the driver INF file.
+ */
+#define FTDI_MHAM_KW_PID	0xEEE8	/* USB-KW interface */
+#define FTDI_MHAM_YS_PID	0xEEE9	/* USB-YS interface */
+#define FTDI_MHAM_Y6_PID	0xEEEA	/* USB-Y6 interface */
+#define FTDI_MHAM_Y8_PID	0xEEEB	/* USB-Y8 interface */
+#define FTDI_MHAM_IC_PID	0xEEEC	/* USB-IC interface */
+#define FTDI_MHAM_DB9_PID	0xEEED	/* USB-DB9 interface */
+#define FTDI_MHAM_RS232_PID	0xEEEE	/* USB-RS232 interface */
+#define FTDI_MHAM_Y9_PID	0xEEEF	/* USB-Y9 interface */
+
+/* Domintell products  http://www.domintell.com */
+#define FTDI_DOMINTELL_DGQG_PID	0xEF50	/* Master */
+#define FTDI_DOMINTELL_DUSB_PID	0xEF51	/* DUSB01 module */
+
+/*
+ * The following are the values for the Perle Systems
+ * UltraPort USB serial converters
+ */
+#define FTDI_PERLE_ULTRAPORT_PID 0xF0C0	/* Perle UltraPort Product Id */
+
+/* Sprog II (Andrew Crosland's SprogII DCC interface) */
+#define FTDI_SPROG_II		0xF0C8
+
+/*
+ * Two of the Tagsys RFID Readers
+ */
+#define FTDI_TAGSYS_LP101_PID	0xF0E9	/* Tagsys L-P101 RFID*/
+#define FTDI_TAGSYS_P200X_PID	0xF0EE	/* Tagsys Medio P200x RFID*/
+
+/* an infrared receiver for user access control with IR tags */
+#define FTDI_PIEGROUP_PID	0xF208	/* Product Id */
+
+/* ACT Solutions HomePro ZWave interface
+   (http://www.act-solutions.com/HomePro-Product-Matrix.html) */
+#define FTDI_ACTZWAVE_PID	0xF2D0
+
+/*
+ * 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485,
+ * USB-TTY aktiv, USB-TTY passiv.  Some PIDs are used by several devices
+ * and I'm not entirely sure which are used by which.
+ */
+#define FTDI_4N_GALAXY_DE_1_PID	0xF3C0
+#define FTDI_4N_GALAXY_DE_2_PID	0xF3C1
+#define FTDI_4N_GALAXY_DE_3_PID	0xF3C2
+
+/*
+ * Ivium Technologies product IDs
+ */
+#define FTDI_PALMSENS_PID	0xf440
+#define FTDI_IVIUM_XSTAT_PID	0xf441
+
+/*
+ * Linx Technologies product ids
+ */
+#define LINX_SDMUSBQSS_PID	0xF448	/* Linx SDM-USB-QS-S */
+#define LINX_MASTERDEVEL2_PID   0xF449	/* Linx Master Development 2.0 */
+#define LINX_FUTURE_0_PID   0xF44A	/* Linx future device */
+#define LINX_FUTURE_1_PID   0xF44B	/* Linx future device */
+#define LINX_FUTURE_2_PID   0xF44C	/* Linx future device */
+
+/*
+ * Oceanic product ids
+ */
+#define FTDI_OCEANIC_PID	0xF460  /* Oceanic dive instrument */
+
+/*
+ * SUUNTO product ids
+ */
+#define FTDI_SUUNTO_SPORTS_PID	0xF680	/* Suunto Sports instrument */
+
+/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */
+/* http://www.usbuirt.com/ */
+#define FTDI_USB_UIRT_PID	0xF850	/* Product Id */
+
+/* CCS Inc. ICDU/ICDU40 product ID -
+ * the FT232BM is used in an in-circuit-debugger unit for PIC16's/PIC18's */
+#define FTDI_CCSICDU20_0_PID    0xF9D0
+#define FTDI_CCSICDU40_1_PID    0xF9D1
+#define FTDI_CCSMACHX_2_PID     0xF9D2
+#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3
+#define FTDI_CCSICDU64_4_PID    0xF9D4
+#define FTDI_CCSPRIME8_5_PID    0xF9D5
+
+/*
+ * The following are the values for the Matrix Orbital LCD displays,
+ * which are the FT232BM ( similar to the 8U232AM )
+ */
+#define FTDI_MTXORB_0_PID      0xFA00  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_1_PID      0xFA01  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_2_PID      0xFA02  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_3_PID      0xFA03  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_4_PID      0xFA04  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_5_PID      0xFA05  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_6_PID      0xFA06  /* Matrix Orbital Product Id */
+
+/*
+ * Home Electronics (www.home-electro.com) USB gadgets
+ */
+#define FTDI_HE_TIRA1_PID	0xFA78	/* Tira-1 IR transceiver */
+
+/* Inside Accesso contactless reader (http://www.insidecontactless.com/) */
+#define INSIDE_ACCESSO		0xFAD0
+
+/*
+ * ThorLabs USB motor drivers
+ */
+#define FTDI_THORLABS_PID		0xfaf0 /* ThorLabs USB motor drivers */
+
+/*
+ * Protego product ids
+ */
+#define PROTEGO_SPECIAL_1	0xFC70	/* special/unknown device */
+#define PROTEGO_R2X0		0xFC71	/* R200-USB TRNG unit (R210, R220, and R230) */
+#define PROTEGO_SPECIAL_3	0xFC72	/* special/unknown device */
+#define PROTEGO_SPECIAL_4	0xFC73	/* special/unknown device */
+
+/*
+ * Sony Ericsson product ids
+ */
+#define FTDI_DSS20_PID		0xFC82	/* DSS-20 Sync Station for Sony Ericsson P800 */
+#define FTDI_URBAN_0_PID	0xFC8A	/* Sony Ericsson Urban, uart #0 */
+#define FTDI_URBAN_1_PID	0xFC8B	/* Sony Ericsson Urban, uart #1 */
+
+/* www.irtrans.de device */
+#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */
+
+/*
+ * RM Michaelides CANview USB (http://www.rmcan.com) (FTDI_VID)
+ * CAN fieldbus interface adapter, added by port GmbH www.port.de)
+ * Ian Abbott changed the macro names for consistency.
+ */
+#define FTDI_RM_CANVIEW_PID	0xfd60	/* Product Id */
+/* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */
+#define FTDI_TTUSB_PID 0xFF20 /* Product Id */
+
+#define FTDI_USBX_707_PID 0xF857	/* ADSTech IR Blaster USBX-707 (FTDI_VID) */
+
+#define FTDI_RELAIS_PID	0xFA10  /* Relais device from Rudolf Gugler */
+
+/*
+ * PCDJ use ftdi based dj-controllers. The following PID is
+ * for their DAC-2 device http://www.pcdjhardware.com/DAC2.asp
+ * (the VID is the standard ftdi vid (FTDI_VID), PID sent by Wouter Paesen)
+ */
+#define FTDI_PCDJ_DAC2_PID 0xFA88
+
+#define FTDI_R2000KU_TRUE_RNG	0xFB80  /* R2000KU TRUE RNG (FTDI_VID) */
+
+/*
+ * DIEBOLD BCS SE923 (FTDI_VID)
+ */
+#define DIEBOLD_BCS_SE923_PID	0xfb99
+
+/* www.crystalfontz.com devices
+ * - thanx for providing free devices for evaluation !
+ * they use the ftdi chipset for the USB interface
+ * and the vendor id is the same
+ */
+#define FTDI_XF_632_PID 0xFC08	/* 632: 16x2 Character Display */
+#define FTDI_XF_634_PID 0xFC09	/* 634: 20x4 Character Display */
+#define FTDI_XF_547_PID 0xFC0A	/* 547: Two line Display */
+#define FTDI_XF_633_PID 0xFC0B	/* 633: 16x2 Character Display with Keys */
+#define FTDI_XF_631_PID 0xFC0C	/* 631: 20x2 Character Display */
+#define FTDI_XF_635_PID 0xFC0D	/* 635: 20x4 Character Display */
+#define FTDI_XF_640_PID 0xFC0E	/* 640: Two line Display */
+#define FTDI_XF_642_PID 0xFC0F	/* 642: Two line Display */
+
+/*
+ * Video Networks Limited / Homechoice in the UK use an ftdi-based device
+ * for their 1Mb broadband internet service.  The following PID is exhibited
+ * by the usb device supplied (the VID is the standard ftdi vid (FTDI_VID)
+ */
+#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */
+
+/* AlphaMicro Components AMC-232USB01 device (FTDI_VID) */
+#define FTDI_AMC232_PID 0xFF00 /* Product Id */
+
+/*
+ * IBS elektronik product ids (FTDI_VID)
+ * Submitted by Thomas Schleusener
+ */
+#define FTDI_IBS_US485_PID	0xff38  /* IBS US485 (USB<-->RS422/485 interface) */
+#define FTDI_IBS_PICPRO_PID	0xff39  /* IBS PIC-Programmer */
+#define FTDI_IBS_PCMCIA_PID	0xff3a  /* IBS Card reader for PCMCIA SRAM-cards */
+#define FTDI_IBS_PK1_PID	0xff3b  /* IBS PK1 - Particel counter */
+#define FTDI_IBS_RS232MON_PID	0xff3c  /* IBS RS232 - Monitor */
+#define FTDI_IBS_APP70_PID	0xff3d  /* APP 70 (dust monitoring system) */
+#define FTDI_IBS_PEDO_PID	0xff3e  /* IBS PEDO-Modem (RF modem 868.35 MHz) */
+#define FTDI_IBS_PROD_PID	0xff3f  /* future device */
+/* www.canusb.com Lawicel CANUSB device (FTDI_VID) */
+#define FTDI_CANUSB_PID 0xFFA8 /* Product Id */
+
+/*
+ * TavIR AVR product ids (FTDI_VID)
+ */
+#define FTDI_TAVIR_STK500_PID	0xFA33	/* STK500 AVR programmer */
+
+/*
+ * TIAO product ids (FTDI_VID)
+ * http://www.tiaowiki.com/w/Main_Page
+ */
+#define FTDI_TIAO_UMPA_PID	0x8a98	/* TIAO/DIYGADGET USB Multi-Protocol Adapter */
+
+/*
+ * NovaTech product ids (FTDI_VID)
+ */
+#define FTDI_NT_ORIONLXM_PID	0x7c90	/* OrionLXm Substation Automation Platform */
+
+/*
+ * Synapse Wireless product ids (FTDI_VID)
+ * http://www.synapse-wireless.com
+ */
+#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */
+
+/*
+ * CustomWare / ShipModul NMEA multiplexers product ids (FTDI_VID)
+ */
+#define FTDI_CUSTOMWARE_MINIPLEX_PID	0xfd48	/* MiniPlex first generation NMEA Multiplexer */
+#define FTDI_CUSTOMWARE_MINIPLEX2_PID	0xfd49	/* MiniPlex-USB and MiniPlex-2 series */
+#define FTDI_CUSTOMWARE_MINIPLEX2WI_PID	0xfd4a	/* MiniPlex-2Wi */
+#define FTDI_CUSTOMWARE_MINIPLEX3_PID	0xfd4b	/* MiniPlex-3 series */
+
+
+/********************************/
+/** third-party VID/PID combos **/
+/********************************/
+
+
+
+/*
+ * Atmel STK541
+ */
+#define ATMEL_VID		0x03eb /* Vendor ID */
+#define STK541_PID		0x2109 /* Zigbee Controller */
+
+/*
+ * Texas Instruments
+ */
+#define TI_VID			0x0451
+#define TI_CC3200_LAUNCHPAD_PID	0xC32A /* SimpleLink Wi-Fi CC3200 LaunchPad */
+
+/*
+ * Blackfin gnICE JTAG
+ * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice
+ */
+#define ADI_VID			0x0456
+#define ADI_GNICE_PID		0xF000
+#define ADI_GNICEPLUS_PID	0xF001
+
+/*
+ * Cypress WICED USB UART
+ */
+#define CYPRESS_VID			0x04B4
+#define CYPRESS_WICED_BT_USB_PID	0x009B
+#define CYPRESS_WICED_WL_USB_PID	0xF900
+
+/*
+ * Microchip Technology, Inc.
+ *
+ * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
+ * used by single function CDC ACM class based firmware demo
+ * applications.  The VID/PID has also been used in firmware
+ * emulating FTDI serial chips by:
+ * Hornby Elite - Digital Command Control Console
+ * http://www.hornby.com/hornby-dcc/controllers/
+ */
+#define MICROCHIP_VID		0x04D8
+#define MICROCHIP_USB_BOARD_PID	0x000A /* CDC RS-232 Emulation Demo */
+
+/*
+ * RATOC REX-USB60F
+ */
+#define RATOC_VENDOR_ID		0x0584
+#define RATOC_PRODUCT_ID_USB60F	0xb020
+#define RATOC_PRODUCT_ID_SCU18	0xb03a
+
+/*
+ * Infineon Technologies
+ */
+#define INFINEON_VID		        0x058b
+#define INFINEON_TRIBOARD_TC1798_PID	0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_TRIBOARD_TC2X7_PID	0x0043 /* DAS JTAG TriBoard TC2X7 V1.0 */
+
+/*
+ * Acton Research Corp.
+ */
+#define ACTON_VID		0x0647	/* Vendor ID */
+#define ACTON_SPECTRAPRO_PID	0x0100
+
+/*
+ * Contec products (http://www.contec.com)
+ * Submitted by Daniel Sangorrin
+ */
+#define CONTEC_VID		0x06CE	/* Vendor ID */
+#define CONTEC_COM1USBH_PID	0x8311	/* COM-1(USB)H */
+
+/*
+ * Mitsubishi Electric Corp. (http://www.meau.com)
+ * Submitted by Konstantin Holoborodko
+ */
+#define MITSUBISHI_VID		0x06D3
+#define MITSUBISHI_FXUSB_PID	0x0284 /* USB/RS422 converters: FX-USB-AW/-BD */
+
+/*
+ * Definitions for B&B Electronics products.
+ */
+#define BANDB_VID		0x0856	/* B&B Electronics Vendor ID */
+#define BANDB_USOTL4_PID	0xAC01	/* USOTL4 Isolated RS-485 Converter */
+#define BANDB_USTL4_PID		0xAC02	/* USTL4 RS-485 Converter */
+#define BANDB_USO9ML2_PID	0xAC03	/* USO9ML2 Isolated RS-232 Converter */
+#define BANDB_USOPTL4_PID	0xAC11
+#define BANDB_USPTL4_PID	0xAC12
+#define BANDB_USO9ML2DR_2_PID	0xAC16
+#define BANDB_USO9ML2DR_PID	0xAC17
+#define BANDB_USOPTL4DR2_PID	0xAC18	/* USOPTL4R-2 2-port Isolated RS-232 Converter */
+#define BANDB_USOPTL4DR_PID	0xAC19
+#define BANDB_485USB9F_2W_PID	0xAC25
+#define BANDB_485USB9F_4W_PID	0xAC26
+#define BANDB_232USB9M_PID	0xAC27
+#define BANDB_485USBTB_2W_PID	0xAC33
+#define BANDB_485USBTB_4W_PID	0xAC34
+#define BANDB_TTL5USB9M_PID	0xAC49
+#define BANDB_TTL3USB9M_PID	0xAC50
+#define BANDB_ZZ_PROG1_USB_PID	0xBA02
+
+/*
+ * Intrepid Control Systems (http://www.intrepidcs.com/) ValueCAN and NeoVI
+ */
+#define INTREPID_VID		0x093C
+#define INTREPID_VALUECAN_PID	0x0601
+#define INTREPID_NEOVI_PID	0x0701
+
+/*
+ * WICED USB UART
+ */
+#define WICED_VID		0x0A5C
+#define WICED_USB20706V2_PID	0x6422
+
+/*
+ * Definitions for ID TECH (www.idt-net.com) devices
+ */
+#define IDTECH_VID		0x0ACD	/* ID TECH Vendor ID */
+#define IDTECH_IDT1221U_PID	0x0300	/* IDT1221U USB to RS-232 adapter */
+
+/*
+ * Definitions for Omnidirectional Control Technology, Inc. devices
+ */
+#define OCT_VID			0x0B39	/* OCT vendor ID */
+/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
+/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */
+/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */
+#define OCT_DK201_PID		0x0103	/* OCT DK201 USB docking station */
+#define OCT_US101_PID		0x0421	/* OCT US101 USB to RS-232 */
+
+/*
+ * Definitions for Icom Inc. devices
+ */
+#define ICOM_VID		0x0C26 /* Icom vendor ID */
+/* Note: ID-1 is a communications tranceiver for HAM-radio operators */
+#define ICOM_ID_1_PID		0x0004 /* ID-1 USB to RS-232 */
+/* Note: OPC is an Optional cable to connect an Icom Tranceiver */
+#define ICOM_OPC_U_UC_PID	0x0018 /* OPC-478UC, OPC-1122U cloning cable */
+/* Note: ID-RP* devices are Icom Repeater Devices for HAM-radio */
+#define ICOM_ID_RP2C1_PID	0x0009 /* ID-RP2C Asset 1 to RS-232 */
+#define ICOM_ID_RP2C2_PID	0x000A /* ID-RP2C Asset 2 to RS-232 */
+#define ICOM_ID_RP2D_PID	0x000B /* ID-RP2D configuration port*/
+#define ICOM_ID_RP2VT_PID	0x000C /* ID-RP2V Transmit config port */
+#define ICOM_ID_RP2VR_PID	0x000D /* ID-RP2V Receive config port */
+#define ICOM_ID_RP4KVT_PID	0x0010 /* ID-RP4000V Transmit config port */
+#define ICOM_ID_RP4KVR_PID	0x0011 /* ID-RP4000V Receive config port */
+#define ICOM_ID_RP2KVT_PID	0x0012 /* ID-RP2000V Transmit config port */
+#define ICOM_ID_RP2KVR_PID	0x0013 /* ID-RP2000V Receive config port */
+
+/*
+ * GN Otometrics (http://www.otometrics.com)
+ * Submitted by Ville Sundberg.
+ */
+#define GN_OTOMETRICS_VID	0x0c33	/* Vendor ID */
+#define AURICAL_USB_PID		0x0010	/* Aurical USB Audiometer */
+
+/*
+ * The following are the values for the Sealevel SeaLINK+ adapters.
+ * (Original list sent by Tuan Hoang.  Ian Abbott renamed the macros and
+ * removed some PIDs that don't seem to match any existing products.)
+ */
+#define SEALEVEL_VID		0x0c52	/* Sealevel Vendor ID */
+#define SEALEVEL_2101_PID	0x2101	/* SeaLINK+232 (2101/2105) */
+#define SEALEVEL_2102_PID	0x2102	/* SeaLINK+485 (2102) */
+#define SEALEVEL_2103_PID	0x2103	/* SeaLINK+232I (2103) */
+#define SEALEVEL_2104_PID	0x2104	/* SeaLINK+485I (2104) */
+#define SEALEVEL_2106_PID	0x9020	/* SeaLINK+422 (2106) */
+#define SEALEVEL_2201_1_PID	0x2211	/* SeaPORT+2/232 (2201) Port 1 */
+#define SEALEVEL_2201_2_PID	0x2221	/* SeaPORT+2/232 (2201) Port 2 */
+#define SEALEVEL_2202_1_PID	0x2212	/* SeaPORT+2/485 (2202) Port 1 */
+#define SEALEVEL_2202_2_PID	0x2222	/* SeaPORT+2/485 (2202) Port 2 */
+#define SEALEVEL_2203_1_PID	0x2213	/* SeaPORT+2 (2203) Port 1 */
+#define SEALEVEL_2203_2_PID	0x2223	/* SeaPORT+2 (2203) Port 2 */
+#define SEALEVEL_2401_1_PID	0x2411	/* SeaPORT+4/232 (2401) Port 1 */
+#define SEALEVEL_2401_2_PID	0x2421	/* SeaPORT+4/232 (2401) Port 2 */
+#define SEALEVEL_2401_3_PID	0x2431	/* SeaPORT+4/232 (2401) Port 3 */
+#define SEALEVEL_2401_4_PID	0x2441	/* SeaPORT+4/232 (2401) Port 4 */
+#define SEALEVEL_2402_1_PID	0x2412	/* SeaPORT+4/485 (2402) Port 1 */
+#define SEALEVEL_2402_2_PID	0x2422	/* SeaPORT+4/485 (2402) Port 2 */
+#define SEALEVEL_2402_3_PID	0x2432	/* SeaPORT+4/485 (2402) Port 3 */
+#define SEALEVEL_2402_4_PID	0x2442	/* SeaPORT+4/485 (2402) Port 4 */
+#define SEALEVEL_2403_1_PID	0x2413	/* SeaPORT+4 (2403) Port 1 */
+#define SEALEVEL_2403_2_PID	0x2423	/* SeaPORT+4 (2403) Port 2 */
+#define SEALEVEL_2403_3_PID	0x2433	/* SeaPORT+4 (2403) Port 3 */
+#define SEALEVEL_2403_4_PID	0x2443	/* SeaPORT+4 (2403) Port 4 */
+#define SEALEVEL_2801_1_PID	0X2811	/* SeaLINK+8/232 (2801) Port 1 */
+#define SEALEVEL_2801_2_PID	0X2821	/* SeaLINK+8/232 (2801) Port 2 */
+#define SEALEVEL_2801_3_PID	0X2831	/* SeaLINK+8/232 (2801) Port 3 */
+#define SEALEVEL_2801_4_PID	0X2841	/* SeaLINK+8/232 (2801) Port 4 */
+#define SEALEVEL_2801_5_PID	0X2851	/* SeaLINK+8/232 (2801) Port 5 */
+#define SEALEVEL_2801_6_PID	0X2861	/* SeaLINK+8/232 (2801) Port 6 */
+#define SEALEVEL_2801_7_PID	0X2871	/* SeaLINK+8/232 (2801) Port 7 */
+#define SEALEVEL_2801_8_PID	0X2881	/* SeaLINK+8/232 (2801) Port 8 */
+#define SEALEVEL_2802_1_PID	0X2812	/* SeaLINK+8/485 (2802) Port 1 */
+#define SEALEVEL_2802_2_PID	0X2822	/* SeaLINK+8/485 (2802) Port 2 */
+#define SEALEVEL_2802_3_PID	0X2832	/* SeaLINK+8/485 (2802) Port 3 */
+#define SEALEVEL_2802_4_PID	0X2842	/* SeaLINK+8/485 (2802) Port 4 */
+#define SEALEVEL_2802_5_PID	0X2852	/* SeaLINK+8/485 (2802) Port 5 */
+#define SEALEVEL_2802_6_PID	0X2862	/* SeaLINK+8/485 (2802) Port 6 */
+#define SEALEVEL_2802_7_PID	0X2872	/* SeaLINK+8/485 (2802) Port 7 */
+#define SEALEVEL_2802_8_PID	0X2882	/* SeaLINK+8/485 (2802) Port 8 */
+#define SEALEVEL_2803_1_PID	0X2813	/* SeaLINK+8 (2803) Port 1 */
+#define SEALEVEL_2803_2_PID	0X2823	/* SeaLINK+8 (2803) Port 2 */
+#define SEALEVEL_2803_3_PID	0X2833	/* SeaLINK+8 (2803) Port 3 */
+#define SEALEVEL_2803_4_PID	0X2843	/* SeaLINK+8 (2803) Port 4 */
+#define SEALEVEL_2803_5_PID	0X2853	/* SeaLINK+8 (2803) Port 5 */
+#define SEALEVEL_2803_6_PID	0X2863	/* SeaLINK+8 (2803) Port 6 */
+#define SEALEVEL_2803_7_PID	0X2873	/* SeaLINK+8 (2803) Port 7 */
+#define SEALEVEL_2803_8_PID	0X2883	/* SeaLINK+8 (2803) Port 8 */
+#define SEALEVEL_2803R_1_PID	0Xa02a	/* SeaLINK+8 (2803-ROHS) Port 1+2 */
+#define SEALEVEL_2803R_2_PID	0Xa02b	/* SeaLINK+8 (2803-ROHS) Port 3+4 */
+#define SEALEVEL_2803R_3_PID	0Xa02c	/* SeaLINK+8 (2803-ROHS) Port 5+6 */
+#define SEALEVEL_2803R_4_PID	0Xa02d	/* SeaLINK+8 (2803-ROHS) Port 7+8 */
+
+/*
+ * JETI SPECTROMETER SPECBOS 1201
+ * http://www.jeti.com/cms/index.php/instruments/other-instruments/specbos-2101
+ */
+#define JETI_VID		0x0c6c
+#define JETI_SPC1201_PID	0x04b2
+
+/*
+ * FTDI USB UART chips used in construction projects from the
+ * Elektor Electronics magazine (http://www.elektor.com/)
+ */
+#define ELEKTOR_VID		0x0C7D
+#define ELEKTOR_FT323R_PID	0x0005	/* RFID-Reader, issue 09-2006 */
+
+/*
+ * Posiflex inc retail equipment (http://www.posiflex.com.tw)
+ */
+#define POSIFLEX_VID		0x0d3a  /* Vendor ID */
+#define POSIFLEX_PP7000_PID	0x0300  /* PP-7000II thermal printer */
+
+/*
+ * The following are the values for two KOBIL chipcard terminals.
+ */
+#define KOBIL_VID		0x0d46	/* KOBIL Vendor ID */
+#define KOBIL_CONV_B1_PID	0x2020	/* KOBIL Konverter for B1 */
+#define KOBIL_CONV_KAAN_PID	0x2021	/* KOBIL_Konverter for KAAN */
+
+#define FTDI_NF_RIC_VID	0x0DCD	/* Vendor Id */
+#define FTDI_NF_RIC_PID	0x0001	/* Product Id */
+
+/*
+ * Falcom Wireless Communications GmbH
+ */
+#define FALCOM_VID		0x0F94	/* Vendor Id */
+#define FALCOM_TWIST_PID	0x0001	/* Falcom Twist USB GPRS modem */
+#define FALCOM_SAMBA_PID	0x0005	/* Falcom Samba USB GPRS modem */
+
+/* Larsen and Brusgaard AltiTrack/USBtrack */
+#define LARSENBRUSGAARD_VID		0x0FD8
+#define LB_ALTITRACK_PID		0x0001
+
+/*
+ * TTi (Thurlby Thandar Instruments)
+ */
+#define TTI_VID			0x103E	/* Vendor Id */
+#define TTI_QL355P_PID		0x03E8	/* TTi QL355P power supply */
+
+/*
+ * Newport Cooperation (www.newport.com)
+ */
+#define NEWPORT_VID			0x104D
+#define NEWPORT_AGILIS_PID		0x3000
+#define NEWPORT_CONEX_CC_PID		0x3002
+#define NEWPORT_CONEX_AGP_PID		0x3006
+
+/* Interbiometrics USB I/O Board */
+/* Developed for Interbiometrics by Rudolf Gugler */
+#define INTERBIOMETRICS_VID              0x1209
+#define INTERBIOMETRICS_IOBOARD_PID      0x1002
+#define INTERBIOMETRICS_MINI_IOBOARD_PID 0x1006
+
+/*
+ * Testo products (http://www.testo.com/)
+ * Submitted by Colin Leroy
+ */
+#define TESTO_VID			0x128D
+#define TESTO_1_PID			0x0001
+#define TESTO_3_PID			0x0003
+
+/*
+ * Mobility Electronics products.
+ */
+#define MOBILITY_VID			0x1342
+#define MOBILITY_USB_SERIAL_PID		0x0202	/* EasiDock USB 200 serial */
+
+/*
+ * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3
+ * Submitted by Harald Welte <laforge@openmoko.org>
+ */
+#define	FIC_VID			0x1457
+#define	FIC_NEO1973_DEBUG_PID	0x5118
+
+/*
+ * Actel / Microsemi
+ */
+#define ACTEL_VID				0x1514
+#define MICROSEMI_ARROW_SF2PLUS_BOARD_PID	0x2008
+
+/* Olimex */
+#define OLIMEX_VID			0x15BA
+#define OLIMEX_ARM_USB_OCD_PID		0x0003
+#define OLIMEX_ARM_USB_TINY_PID	0x0004
+#define OLIMEX_ARM_USB_TINY_H_PID	0x002a
+#define OLIMEX_ARM_USB_OCD_H_PID	0x002b
+
+/*
+ * Telldus Technologies
+ */
+#define TELLDUS_VID			0x1781	/* Vendor ID */
+#define TELLDUS_TELLSTICK_PID		0x0C30	/* RF control dongle 433 MHz using FT232RL */
+
+/*
+ * NOVITUS printers
+ */
+#define NOVITUS_VID			0x1a28
+#define NOVITUS_BONO_E_PID		0x6010
+
+/*
+ * ICPDAS I-756*U devices
+ */
+#define ICPDAS_VID			0x1b5c
+#define ICPDAS_I7560U_PID		0x0103
+#define ICPDAS_I7561U_PID		0x0104
+#define ICPDAS_I7563U_PID		0x0105
+
+/*
+ * Airbus Defence and Space
+ */
+#define AIRBUS_DS_VID			0x1e8e  /* Vendor ID */
+#define AIRBUS_DS_P8GR			0x6001  /* Tetra P8GR */
+
+/*
+ * RT Systems programming cables for various ham radios
+ */
+/* This device uses the VID of FTDI */
+#define RTSYSTEMS_USB_VX8_PID   0x9e50  /* USB-VX8 USB to 7 pin modular plug for Yaesu VX-8 radio */
+
+#define RTSYSTEMS_VID		0x2100	/* Vendor ID */
+#define RTSYSTEMS_USB_S03_PID	0x9001	/* RTS-03 USB to Serial Adapter */
+#define RTSYSTEMS_USB_59_PID	0x9e50	/* USB-59 USB to 8 pin plug */
+#define RTSYSTEMS_USB_57A_PID	0x9e51	/* USB-57A USB to 4pin 3.5mm plug */
+#define RTSYSTEMS_USB_57B_PID	0x9e52	/* USB-57B USB to extended 4pin 3.5mm plug */
+#define RTSYSTEMS_USB_29A_PID	0x9e53	/* USB-29A USB to 3.5mm stereo plug */
+#define RTSYSTEMS_USB_29B_PID	0x9e54	/* USB-29B USB to 6 pin mini din */
+#define RTSYSTEMS_USB_29F_PID	0x9e55	/* USB-29F USB to 6 pin modular plug */
+#define RTSYSTEMS_USB_62B_PID	0x9e56	/* USB-62B USB to 8 pin mini din plug*/
+#define RTSYSTEMS_USB_S01_PID	0x9e57	/* USB-RTS01 USB to 3.5 mm stereo plug*/
+#define RTSYSTEMS_USB_63_PID	0x9e58	/* USB-63 USB to 9 pin female*/
+#define RTSYSTEMS_USB_29C_PID	0x9e59	/* USB-29C USB to 4 pin modular plug*/
+#define RTSYSTEMS_USB_81B_PID	0x9e5A	/* USB-81 USB to 8 pin mini din plug*/
+#define RTSYSTEMS_USB_82B_PID	0x9e5B	/* USB-82 USB to 2.5 mm stereo plug*/
+#define RTSYSTEMS_USB_K5D_PID	0x9e5C	/* USB-K5D USB to 8 pin modular plug*/
+#define RTSYSTEMS_USB_K4Y_PID	0x9e5D	/* USB-K4Y USB to 2.5/3.5 mm plugs*/
+#define RTSYSTEMS_USB_K5G_PID	0x9e5E	/* USB-K5G USB to 8 pin modular plug*/
+#define RTSYSTEMS_USB_S05_PID	0x9e5F	/* USB-RTS05 USB to 2.5 mm stereo plug*/
+#define RTSYSTEMS_USB_60_PID	0x9e60	/* USB-60 USB to 6 pin din*/
+#define RTSYSTEMS_USB_61_PID	0x9e61	/* USB-61 USB to 6 pin mini din*/
+#define RTSYSTEMS_USB_62_PID	0x9e62	/* USB-62 USB to 8 pin mini din*/
+#define RTSYSTEMS_USB_63B_PID	0x9e63	/* USB-63 USB to 9 pin female*/
+#define RTSYSTEMS_USB_64_PID	0x9e64	/* USB-64 USB to 9 pin male*/
+#define RTSYSTEMS_USB_65_PID	0x9e65	/* USB-65 USB to 9 pin female null modem*/
+#define RTSYSTEMS_USB_92_PID	0x9e66	/* USB-92 USB to 12 pin plug*/
+#define RTSYSTEMS_USB_92D_PID	0x9e67	/* USB-92D USB to 12 pin plug data*/
+#define RTSYSTEMS_USB_W5R_PID	0x9e68	/* USB-W5R USB to 8 pin modular plug*/
+#define RTSYSTEMS_USB_A5R_PID	0x9e69	/* USB-A5R USB to 8 pin modular plug*/
+#define RTSYSTEMS_USB_PW1_PID	0x9e6A	/* USB-PW1 USB to 8 pin modular plug*/
+
+/*
+ * Physik Instrumente
+ * http://www.physikinstrumente.com/en/products/
+ */
+/* These two devices use the VID of FTDI */
+#define PI_C865_PID	0xe0a0  /* PI C-865 Piezomotor Controller */
+#define PI_C857_PID	0xe0a1  /* PI Encoder Trigger Box */
+
+#define PI_VID              0x1a72  /* Vendor ID */
+#define PI_C866_PID	0x1000  /* PI C-866 Piezomotor Controller */
+#define PI_C663_PID	0x1001  /* PI C-663 Mercury-Step */
+#define PI_C725_PID	0x1002  /* PI C-725 Piezomotor Controller */
+#define PI_E517_PID	0x1005  /* PI E-517 Digital Piezo Controller Operation Module */
+#define PI_C863_PID	0x1007  /* PI C-863 */
+#define PI_E861_PID	0x1008  /* PI E-861 Piezomotor Controller */
+#define PI_C867_PID	0x1009  /* PI C-867 Piezomotor Controller */
+#define PI_E609_PID	0x100D  /* PI E-609 Digital Piezo Controller */
+#define PI_E709_PID	0x100E  /* PI E-709 Digital Piezo Controller */
+#define PI_100F_PID	0x100F  /* PI Digital Piezo Controller */
+#define PI_1011_PID	0x1011  /* PI Digital Piezo Controller */
+#define PI_1012_PID	0x1012  /* PI Motion Controller */
+#define PI_1013_PID	0x1013  /* PI Motion Controller */
+#define PI_1014_PID	0x1014  /* PI Device */
+#define PI_1015_PID	0x1015  /* PI Device */
+#define PI_1016_PID	0x1016  /* PI Digital Servo Module */
+
+/*
+ * Kondo Kagaku Co.Ltd.
+ * http://www.kondo-robot.com/EN
+ */
+#define KONDO_VID 		0x165c
+#define KONDO_USB_SERIAL_PID	0x0002
+
+/*
+ * Bayer Ascensia Contour blood glucose meter USB-converter cable.
+ * http://winglucofacts.com/cables/
+ */
+#define BAYER_VID                      0x1A79
+#define BAYER_CONTOUR_CABLE_PID        0x6001
+
+/*
+ * Matrix Orbital Intelligent USB displays.
+ * http://www.matrixorbital.com
+ */
+#define MTXORB_VID			0x1B3D
+#define MTXORB_FTDI_RANGE_0100_PID	0x0100
+#define MTXORB_FTDI_RANGE_0101_PID	0x0101
+#define MTXORB_FTDI_RANGE_0102_PID	0x0102
+#define MTXORB_FTDI_RANGE_0103_PID	0x0103
+#define MTXORB_FTDI_RANGE_0104_PID	0x0104
+#define MTXORB_FTDI_RANGE_0105_PID	0x0105
+#define MTXORB_FTDI_RANGE_0106_PID	0x0106
+#define MTXORB_FTDI_RANGE_0107_PID	0x0107
+#define MTXORB_FTDI_RANGE_0108_PID	0x0108
+#define MTXORB_FTDI_RANGE_0109_PID	0x0109
+#define MTXORB_FTDI_RANGE_010A_PID	0x010A
+#define MTXORB_FTDI_RANGE_010B_PID	0x010B
+#define MTXORB_FTDI_RANGE_010C_PID	0x010C
+#define MTXORB_FTDI_RANGE_010D_PID	0x010D
+#define MTXORB_FTDI_RANGE_010E_PID	0x010E
+#define MTXORB_FTDI_RANGE_010F_PID	0x010F
+#define MTXORB_FTDI_RANGE_0110_PID	0x0110
+#define MTXORB_FTDI_RANGE_0111_PID	0x0111
+#define MTXORB_FTDI_RANGE_0112_PID	0x0112
+#define MTXORB_FTDI_RANGE_0113_PID	0x0113
+#define MTXORB_FTDI_RANGE_0114_PID	0x0114
+#define MTXORB_FTDI_RANGE_0115_PID	0x0115
+#define MTXORB_FTDI_RANGE_0116_PID	0x0116
+#define MTXORB_FTDI_RANGE_0117_PID	0x0117
+#define MTXORB_FTDI_RANGE_0118_PID	0x0118
+#define MTXORB_FTDI_RANGE_0119_PID	0x0119
+#define MTXORB_FTDI_RANGE_011A_PID	0x011A
+#define MTXORB_FTDI_RANGE_011B_PID	0x011B
+#define MTXORB_FTDI_RANGE_011C_PID	0x011C
+#define MTXORB_FTDI_RANGE_011D_PID	0x011D
+#define MTXORB_FTDI_RANGE_011E_PID	0x011E
+#define MTXORB_FTDI_RANGE_011F_PID	0x011F
+#define MTXORB_FTDI_RANGE_0120_PID	0x0120
+#define MTXORB_FTDI_RANGE_0121_PID	0x0121
+#define MTXORB_FTDI_RANGE_0122_PID	0x0122
+#define MTXORB_FTDI_RANGE_0123_PID	0x0123
+#define MTXORB_FTDI_RANGE_0124_PID	0x0124
+#define MTXORB_FTDI_RANGE_0125_PID	0x0125
+#define MTXORB_FTDI_RANGE_0126_PID	0x0126
+#define MTXORB_FTDI_RANGE_0127_PID	0x0127
+#define MTXORB_FTDI_RANGE_0128_PID	0x0128
+#define MTXORB_FTDI_RANGE_0129_PID	0x0129
+#define MTXORB_FTDI_RANGE_012A_PID	0x012A
+#define MTXORB_FTDI_RANGE_012B_PID	0x012B
+#define MTXORB_FTDI_RANGE_012C_PID	0x012C
+#define MTXORB_FTDI_RANGE_012D_PID	0x012D
+#define MTXORB_FTDI_RANGE_012E_PID	0x012E
+#define MTXORB_FTDI_RANGE_012F_PID	0x012F
+#define MTXORB_FTDI_RANGE_0130_PID	0x0130
+#define MTXORB_FTDI_RANGE_0131_PID	0x0131
+#define MTXORB_FTDI_RANGE_0132_PID	0x0132
+#define MTXORB_FTDI_RANGE_0133_PID	0x0133
+#define MTXORB_FTDI_RANGE_0134_PID	0x0134
+#define MTXORB_FTDI_RANGE_0135_PID	0x0135
+#define MTXORB_FTDI_RANGE_0136_PID	0x0136
+#define MTXORB_FTDI_RANGE_0137_PID	0x0137
+#define MTXORB_FTDI_RANGE_0138_PID	0x0138
+#define MTXORB_FTDI_RANGE_0139_PID	0x0139
+#define MTXORB_FTDI_RANGE_013A_PID	0x013A
+#define MTXORB_FTDI_RANGE_013B_PID	0x013B
+#define MTXORB_FTDI_RANGE_013C_PID	0x013C
+#define MTXORB_FTDI_RANGE_013D_PID	0x013D
+#define MTXORB_FTDI_RANGE_013E_PID	0x013E
+#define MTXORB_FTDI_RANGE_013F_PID	0x013F
+#define MTXORB_FTDI_RANGE_0140_PID	0x0140
+#define MTXORB_FTDI_RANGE_0141_PID	0x0141
+#define MTXORB_FTDI_RANGE_0142_PID	0x0142
+#define MTXORB_FTDI_RANGE_0143_PID	0x0143
+#define MTXORB_FTDI_RANGE_0144_PID	0x0144
+#define MTXORB_FTDI_RANGE_0145_PID	0x0145
+#define MTXORB_FTDI_RANGE_0146_PID	0x0146
+#define MTXORB_FTDI_RANGE_0147_PID	0x0147
+#define MTXORB_FTDI_RANGE_0148_PID	0x0148
+#define MTXORB_FTDI_RANGE_0149_PID	0x0149
+#define MTXORB_FTDI_RANGE_014A_PID	0x014A
+#define MTXORB_FTDI_RANGE_014B_PID	0x014B
+#define MTXORB_FTDI_RANGE_014C_PID	0x014C
+#define MTXORB_FTDI_RANGE_014D_PID	0x014D
+#define MTXORB_FTDI_RANGE_014E_PID	0x014E
+#define MTXORB_FTDI_RANGE_014F_PID	0x014F
+#define MTXORB_FTDI_RANGE_0150_PID	0x0150
+#define MTXORB_FTDI_RANGE_0151_PID	0x0151
+#define MTXORB_FTDI_RANGE_0152_PID	0x0152
+#define MTXORB_FTDI_RANGE_0153_PID	0x0153
+#define MTXORB_FTDI_RANGE_0154_PID	0x0154
+#define MTXORB_FTDI_RANGE_0155_PID	0x0155
+#define MTXORB_FTDI_RANGE_0156_PID	0x0156
+#define MTXORB_FTDI_RANGE_0157_PID	0x0157
+#define MTXORB_FTDI_RANGE_0158_PID	0x0158
+#define MTXORB_FTDI_RANGE_0159_PID	0x0159
+#define MTXORB_FTDI_RANGE_015A_PID	0x015A
+#define MTXORB_FTDI_RANGE_015B_PID	0x015B
+#define MTXORB_FTDI_RANGE_015C_PID	0x015C
+#define MTXORB_FTDI_RANGE_015D_PID	0x015D
+#define MTXORB_FTDI_RANGE_015E_PID	0x015E
+#define MTXORB_FTDI_RANGE_015F_PID	0x015F
+#define MTXORB_FTDI_RANGE_0160_PID	0x0160
+#define MTXORB_FTDI_RANGE_0161_PID	0x0161
+#define MTXORB_FTDI_RANGE_0162_PID	0x0162
+#define MTXORB_FTDI_RANGE_0163_PID	0x0163
+#define MTXORB_FTDI_RANGE_0164_PID	0x0164
+#define MTXORB_FTDI_RANGE_0165_PID	0x0165
+#define MTXORB_FTDI_RANGE_0166_PID	0x0166
+#define MTXORB_FTDI_RANGE_0167_PID	0x0167
+#define MTXORB_FTDI_RANGE_0168_PID	0x0168
+#define MTXORB_FTDI_RANGE_0169_PID	0x0169
+#define MTXORB_FTDI_RANGE_016A_PID	0x016A
+#define MTXORB_FTDI_RANGE_016B_PID	0x016B
+#define MTXORB_FTDI_RANGE_016C_PID	0x016C
+#define MTXORB_FTDI_RANGE_016D_PID	0x016D
+#define MTXORB_FTDI_RANGE_016E_PID	0x016E
+#define MTXORB_FTDI_RANGE_016F_PID	0x016F
+#define MTXORB_FTDI_RANGE_0170_PID	0x0170
+#define MTXORB_FTDI_RANGE_0171_PID	0x0171
+#define MTXORB_FTDI_RANGE_0172_PID	0x0172
+#define MTXORB_FTDI_RANGE_0173_PID	0x0173
+#define MTXORB_FTDI_RANGE_0174_PID	0x0174
+#define MTXORB_FTDI_RANGE_0175_PID	0x0175
+#define MTXORB_FTDI_RANGE_0176_PID	0x0176
+#define MTXORB_FTDI_RANGE_0177_PID	0x0177
+#define MTXORB_FTDI_RANGE_0178_PID	0x0178
+#define MTXORB_FTDI_RANGE_0179_PID	0x0179
+#define MTXORB_FTDI_RANGE_017A_PID	0x017A
+#define MTXORB_FTDI_RANGE_017B_PID	0x017B
+#define MTXORB_FTDI_RANGE_017C_PID	0x017C
+#define MTXORB_FTDI_RANGE_017D_PID	0x017D
+#define MTXORB_FTDI_RANGE_017E_PID	0x017E
+#define MTXORB_FTDI_RANGE_017F_PID	0x017F
+#define MTXORB_FTDI_RANGE_0180_PID	0x0180
+#define MTXORB_FTDI_RANGE_0181_PID	0x0181
+#define MTXORB_FTDI_RANGE_0182_PID	0x0182
+#define MTXORB_FTDI_RANGE_0183_PID	0x0183
+#define MTXORB_FTDI_RANGE_0184_PID	0x0184
+#define MTXORB_FTDI_RANGE_0185_PID	0x0185
+#define MTXORB_FTDI_RANGE_0186_PID	0x0186
+#define MTXORB_FTDI_RANGE_0187_PID	0x0187
+#define MTXORB_FTDI_RANGE_0188_PID	0x0188
+#define MTXORB_FTDI_RANGE_0189_PID	0x0189
+#define MTXORB_FTDI_RANGE_018A_PID	0x018A
+#define MTXORB_FTDI_RANGE_018B_PID	0x018B
+#define MTXORB_FTDI_RANGE_018C_PID	0x018C
+#define MTXORB_FTDI_RANGE_018D_PID	0x018D
+#define MTXORB_FTDI_RANGE_018E_PID	0x018E
+#define MTXORB_FTDI_RANGE_018F_PID	0x018F
+#define MTXORB_FTDI_RANGE_0190_PID	0x0190
+#define MTXORB_FTDI_RANGE_0191_PID	0x0191
+#define MTXORB_FTDI_RANGE_0192_PID	0x0192
+#define MTXORB_FTDI_RANGE_0193_PID	0x0193
+#define MTXORB_FTDI_RANGE_0194_PID	0x0194
+#define MTXORB_FTDI_RANGE_0195_PID	0x0195
+#define MTXORB_FTDI_RANGE_0196_PID	0x0196
+#define MTXORB_FTDI_RANGE_0197_PID	0x0197
+#define MTXORB_FTDI_RANGE_0198_PID	0x0198
+#define MTXORB_FTDI_RANGE_0199_PID	0x0199
+#define MTXORB_FTDI_RANGE_019A_PID	0x019A
+#define MTXORB_FTDI_RANGE_019B_PID	0x019B
+#define MTXORB_FTDI_RANGE_019C_PID	0x019C
+#define MTXORB_FTDI_RANGE_019D_PID	0x019D
+#define MTXORB_FTDI_RANGE_019E_PID	0x019E
+#define MTXORB_FTDI_RANGE_019F_PID	0x019F
+#define MTXORB_FTDI_RANGE_01A0_PID	0x01A0
+#define MTXORB_FTDI_RANGE_01A1_PID	0x01A1
+#define MTXORB_FTDI_RANGE_01A2_PID	0x01A2
+#define MTXORB_FTDI_RANGE_01A3_PID	0x01A3
+#define MTXORB_FTDI_RANGE_01A4_PID	0x01A4
+#define MTXORB_FTDI_RANGE_01A5_PID	0x01A5
+#define MTXORB_FTDI_RANGE_01A6_PID	0x01A6
+#define MTXORB_FTDI_RANGE_01A7_PID	0x01A7
+#define MTXORB_FTDI_RANGE_01A8_PID	0x01A8
+#define MTXORB_FTDI_RANGE_01A9_PID	0x01A9
+#define MTXORB_FTDI_RANGE_01AA_PID	0x01AA
+#define MTXORB_FTDI_RANGE_01AB_PID	0x01AB
+#define MTXORB_FTDI_RANGE_01AC_PID	0x01AC
+#define MTXORB_FTDI_RANGE_01AD_PID	0x01AD
+#define MTXORB_FTDI_RANGE_01AE_PID	0x01AE
+#define MTXORB_FTDI_RANGE_01AF_PID	0x01AF
+#define MTXORB_FTDI_RANGE_01B0_PID	0x01B0
+#define MTXORB_FTDI_RANGE_01B1_PID	0x01B1
+#define MTXORB_FTDI_RANGE_01B2_PID	0x01B2
+#define MTXORB_FTDI_RANGE_01B3_PID	0x01B3
+#define MTXORB_FTDI_RANGE_01B4_PID	0x01B4
+#define MTXORB_FTDI_RANGE_01B5_PID	0x01B5
+#define MTXORB_FTDI_RANGE_01B6_PID	0x01B6
+#define MTXORB_FTDI_RANGE_01B7_PID	0x01B7
+#define MTXORB_FTDI_RANGE_01B8_PID	0x01B8
+#define MTXORB_FTDI_RANGE_01B9_PID	0x01B9
+#define MTXORB_FTDI_RANGE_01BA_PID	0x01BA
+#define MTXORB_FTDI_RANGE_01BB_PID	0x01BB
+#define MTXORB_FTDI_RANGE_01BC_PID	0x01BC
+#define MTXORB_FTDI_RANGE_01BD_PID	0x01BD
+#define MTXORB_FTDI_RANGE_01BE_PID	0x01BE
+#define MTXORB_FTDI_RANGE_01BF_PID	0x01BF
+#define MTXORB_FTDI_RANGE_01C0_PID	0x01C0
+#define MTXORB_FTDI_RANGE_01C1_PID	0x01C1
+#define MTXORB_FTDI_RANGE_01C2_PID	0x01C2
+#define MTXORB_FTDI_RANGE_01C3_PID	0x01C3
+#define MTXORB_FTDI_RANGE_01C4_PID	0x01C4
+#define MTXORB_FTDI_RANGE_01C5_PID	0x01C5
+#define MTXORB_FTDI_RANGE_01C6_PID	0x01C6
+#define MTXORB_FTDI_RANGE_01C7_PID	0x01C7
+#define MTXORB_FTDI_RANGE_01C8_PID	0x01C8
+#define MTXORB_FTDI_RANGE_01C9_PID	0x01C9
+#define MTXORB_FTDI_RANGE_01CA_PID	0x01CA
+#define MTXORB_FTDI_RANGE_01CB_PID	0x01CB
+#define MTXORB_FTDI_RANGE_01CC_PID	0x01CC
+#define MTXORB_FTDI_RANGE_01CD_PID	0x01CD
+#define MTXORB_FTDI_RANGE_01CE_PID	0x01CE
+#define MTXORB_FTDI_RANGE_01CF_PID	0x01CF
+#define MTXORB_FTDI_RANGE_01D0_PID	0x01D0
+#define MTXORB_FTDI_RANGE_01D1_PID	0x01D1
+#define MTXORB_FTDI_RANGE_01D2_PID	0x01D2
+#define MTXORB_FTDI_RANGE_01D3_PID	0x01D3
+#define MTXORB_FTDI_RANGE_01D4_PID	0x01D4
+#define MTXORB_FTDI_RANGE_01D5_PID	0x01D5
+#define MTXORB_FTDI_RANGE_01D6_PID	0x01D6
+#define MTXORB_FTDI_RANGE_01D7_PID	0x01D7
+#define MTXORB_FTDI_RANGE_01D8_PID	0x01D8
+#define MTXORB_FTDI_RANGE_01D9_PID	0x01D9
+#define MTXORB_FTDI_RANGE_01DA_PID	0x01DA
+#define MTXORB_FTDI_RANGE_01DB_PID	0x01DB
+#define MTXORB_FTDI_RANGE_01DC_PID	0x01DC
+#define MTXORB_FTDI_RANGE_01DD_PID	0x01DD
+#define MTXORB_FTDI_RANGE_01DE_PID	0x01DE
+#define MTXORB_FTDI_RANGE_01DF_PID	0x01DF
+#define MTXORB_FTDI_RANGE_01E0_PID	0x01E0
+#define MTXORB_FTDI_RANGE_01E1_PID	0x01E1
+#define MTXORB_FTDI_RANGE_01E2_PID	0x01E2
+#define MTXORB_FTDI_RANGE_01E3_PID	0x01E3
+#define MTXORB_FTDI_RANGE_01E4_PID	0x01E4
+#define MTXORB_FTDI_RANGE_01E5_PID	0x01E5
+#define MTXORB_FTDI_RANGE_01E6_PID	0x01E6
+#define MTXORB_FTDI_RANGE_01E7_PID	0x01E7
+#define MTXORB_FTDI_RANGE_01E8_PID	0x01E8
+#define MTXORB_FTDI_RANGE_01E9_PID	0x01E9
+#define MTXORB_FTDI_RANGE_01EA_PID	0x01EA
+#define MTXORB_FTDI_RANGE_01EB_PID	0x01EB
+#define MTXORB_FTDI_RANGE_01EC_PID	0x01EC
+#define MTXORB_FTDI_RANGE_01ED_PID	0x01ED
+#define MTXORB_FTDI_RANGE_01EE_PID	0x01EE
+#define MTXORB_FTDI_RANGE_01EF_PID	0x01EF
+#define MTXORB_FTDI_RANGE_01F0_PID	0x01F0
+#define MTXORB_FTDI_RANGE_01F1_PID	0x01F1
+#define MTXORB_FTDI_RANGE_01F2_PID	0x01F2
+#define MTXORB_FTDI_RANGE_01F3_PID	0x01F3
+#define MTXORB_FTDI_RANGE_01F4_PID	0x01F4
+#define MTXORB_FTDI_RANGE_01F5_PID	0x01F5
+#define MTXORB_FTDI_RANGE_01F6_PID	0x01F6
+#define MTXORB_FTDI_RANGE_01F7_PID	0x01F7
+#define MTXORB_FTDI_RANGE_01F8_PID	0x01F8
+#define MTXORB_FTDI_RANGE_01F9_PID	0x01F9
+#define MTXORB_FTDI_RANGE_01FA_PID	0x01FA
+#define MTXORB_FTDI_RANGE_01FB_PID	0x01FB
+#define MTXORB_FTDI_RANGE_01FC_PID	0x01FC
+#define MTXORB_FTDI_RANGE_01FD_PID	0x01FD
+#define MTXORB_FTDI_RANGE_01FE_PID	0x01FE
+#define MTXORB_FTDI_RANGE_01FF_PID	0x01FF
+#define MTXORB_FTDI_RANGE_4701_PID	0x4701
+#define MTXORB_FTDI_RANGE_9300_PID	0x9300
+#define MTXORB_FTDI_RANGE_9301_PID	0x9301
+#define MTXORB_FTDI_RANGE_9302_PID	0x9302
+#define MTXORB_FTDI_RANGE_9303_PID	0x9303
+#define MTXORB_FTDI_RANGE_9304_PID	0x9304
+#define MTXORB_FTDI_RANGE_9305_PID	0x9305
+#define MTXORB_FTDI_RANGE_9306_PID	0x9306
+#define MTXORB_FTDI_RANGE_9307_PID	0x9307
+#define MTXORB_FTDI_RANGE_9308_PID	0x9308
+#define MTXORB_FTDI_RANGE_9309_PID	0x9309
+#define MTXORB_FTDI_RANGE_930A_PID	0x930A
+#define MTXORB_FTDI_RANGE_930B_PID	0x930B
+#define MTXORB_FTDI_RANGE_930C_PID	0x930C
+#define MTXORB_FTDI_RANGE_930D_PID	0x930D
+#define MTXORB_FTDI_RANGE_930E_PID	0x930E
+#define MTXORB_FTDI_RANGE_930F_PID	0x930F
+#define MTXORB_FTDI_RANGE_9310_PID	0x9310
+#define MTXORB_FTDI_RANGE_9311_PID	0x9311
+#define MTXORB_FTDI_RANGE_9312_PID	0x9312
+#define MTXORB_FTDI_RANGE_9313_PID	0x9313
+#define MTXORB_FTDI_RANGE_9314_PID	0x9314
+#define MTXORB_FTDI_RANGE_9315_PID	0x9315
+#define MTXORB_FTDI_RANGE_9316_PID	0x9316
+#define MTXORB_FTDI_RANGE_9317_PID	0x9317
+#define MTXORB_FTDI_RANGE_9318_PID	0x9318
+#define MTXORB_FTDI_RANGE_9319_PID	0x9319
+#define MTXORB_FTDI_RANGE_931A_PID	0x931A
+#define MTXORB_FTDI_RANGE_931B_PID	0x931B
+#define MTXORB_FTDI_RANGE_931C_PID	0x931C
+#define MTXORB_FTDI_RANGE_931D_PID	0x931D
+#define MTXORB_FTDI_RANGE_931E_PID	0x931E
+#define MTXORB_FTDI_RANGE_931F_PID	0x931F
+
+/*
+ * The Mobility Lab (TML)
+ * Submitted by Pierre Castella
+ */
+#define TML_VID			0x1B91	/* Vendor ID */
+#define TML_USB_SERIAL_PID	0x0064	/* USB - Serial Converter */
+
+/* Alti-2 products  http://www.alti-2.com */
+#define ALTI2_VID	0x1BC9
+#define ALTI2_N3_PID	0x6001	/* Neptune 3 */
+
+/*
+ * Ionics PlugComputer
+ */
+#define IONICS_VID			0x1c0c
+#define IONICS_PLUGCOMPUTER_PID		0x0102
+
+/*
+ * Dresden Elektronik Sensor Terminal Board
+ */
+#define DE_VID			0x1cf1 /* Vendor ID */
+#define STB_PID			0x0001 /* Sensor Terminal Board */
+#define WHT_PID			0x0004 /* Wireless Handheld Terminal */
+
+/*
+ * STMicroelectonics
+ */
+#define ST_VID			0x0483
+#define ST_STMCLT_2232_PID	0x3746
+#define ST_STMCLT_4232_PID	0x3747
+
+/*
+ * Papouch products (http://www.papouch.com/)
+ * Submitted by Folkert van Heusden
+ */
+
+#define PAPOUCH_VID			0x5050	/* Vendor ID */
+#define PAPOUCH_SB485_PID		0x0100	/* Papouch SB485 USB-485/422 Converter */
+#define PAPOUCH_AP485_PID		0x0101	/* AP485 USB-RS485 Converter */
+#define PAPOUCH_SB422_PID		0x0102	/* Papouch SB422 USB-RS422 Converter  */
+#define PAPOUCH_SB485_2_PID		0x0103	/* Papouch SB485 USB-485/422 Converter */
+#define PAPOUCH_AP485_2_PID		0x0104	/* AP485 USB-RS485 Converter */
+#define PAPOUCH_SB422_2_PID		0x0105	/* Papouch SB422 USB-RS422 Converter  */
+#define PAPOUCH_SB485S_PID		0x0106	/* Papouch SB485S USB-485/422 Converter */
+#define PAPOUCH_SB485C_PID		0x0107	/* Papouch SB485C USB-485/422 Converter */
+#define PAPOUCH_LEC_PID			0x0300	/* LEC USB Converter */
+#define PAPOUCH_SB232_PID		0x0301	/* Papouch SB232 USB-RS232 Converter */
+#define PAPOUCH_TMU_PID			0x0400	/* TMU USB Thermometer */
+#define PAPOUCH_IRAMP_PID		0x0500	/* Papouch IRAmp Duplex */
+#define PAPOUCH_DRAK5_PID		0x0700	/* Papouch DRAK5 */
+#define PAPOUCH_QUIDO8x8_PID		0x0800	/* Papouch Quido 8/8 Module */
+#define PAPOUCH_QUIDO4x4_PID		0x0900	/* Papouch Quido 4/4 Module */
+#define PAPOUCH_QUIDO2x2_PID		0x0a00	/* Papouch Quido 2/2 Module */
+#define PAPOUCH_QUIDO10x1_PID		0x0b00	/* Papouch Quido 10/1 Module */
+#define PAPOUCH_QUIDO30x3_PID		0x0c00	/* Papouch Quido 30/3 Module */
+#define PAPOUCH_QUIDO60x3_PID		0x0d00	/* Papouch Quido 60(100)/3 Module */
+#define PAPOUCH_QUIDO2x16_PID		0x0e00	/* Papouch Quido 2/16 Module */
+#define PAPOUCH_QUIDO3x32_PID		0x0f00	/* Papouch Quido 3/32 Module */
+#define PAPOUCH_DRAK6_PID		0x1000	/* Papouch DRAK6 */
+#define PAPOUCH_UPSUSB_PID		0x8000	/* Papouch UPS-USB adapter */
+#define PAPOUCH_MU_PID			0x8001	/* MU controller */
+#define PAPOUCH_SIMUKEY_PID		0x8002	/* Papouch SimuKey */
+#define PAPOUCH_AD4USB_PID		0x8003	/* AD4USB Measurement Module */
+#define PAPOUCH_GMUX_PID		0x8004	/* Papouch GOLIATH MUX */
+#define PAPOUCH_GMSR_PID		0x8005	/* Papouch GOLIATH MSR */
+
+/*
+ * Marvell SheevaPlug
+ */
+#define MARVELL_VID		0x9e88
+#define MARVELL_SHEEVAPLUG_PID	0x9e8f
+
+/*
+ * Evolution Robotics products (http://www.evolution.com/).
+ * Submitted by Shawn M. Lavelle.
+ */
+#define EVOLUTION_VID		0xDEEE	/* Vendor ID */
+#define EVOLUTION_ER1_PID	0x0300	/* ER1 Control Module */
+#define EVO_8U232AM_PID		0x02FF	/* Evolution robotics RCM2 (FT232AM)*/
+#define EVO_HYBRID_PID		0x0302	/* Evolution robotics RCM4 PID (FT232BM)*/
+#define EVO_RCM4_PID		0x0303	/* Evolution robotics RCM4 PID */
+
+/*
+ * MJS Gadgets HD Radio / XM Radio / Sirius Radio interfaces (using VID 0x0403)
+ */
+#define MJSG_GENERIC_PID	0x9378
+#define MJSG_SR_RADIO_PID	0x9379
+#define MJSG_XM_RADIO_PID	0x937A
+#define MJSG_HD_RADIO_PID	0x937C
+
+/*
+ * D.O.Tec products (http://www.directout.eu)
+ */
+#define FTDI_DOTEC_PID 0x9868
+
+/*
+ * Xverve Signalyzer tools (http://www.signalyzer.com/)
+ */
+#define XVERVE_SIGNALYZER_ST_PID	0xBCA0
+#define XVERVE_SIGNALYZER_SLITE_PID	0xBCA1
+#define XVERVE_SIGNALYZER_SH2_PID	0xBCA2
+#define XVERVE_SIGNALYZER_SH4_PID	0xBCA4
+
+/*
+ * Segway Robotic Mobility Platform USB interface (using VID 0x0403)
+ * Submitted by John G. Rogers
+ */
+#define SEGWAY_RMP200_PID	0xe729
+
+
+/*
+ * Accesio USB Data Acquisition products (http://www.accesio.com/)
+ */
+#define ACCESIO_COM4SM_PID 	0xD578
+
+/* www.sciencescope.co.uk educational dataloggers */
+#define FTDI_SCIENCESCOPE_LOGBOOKML_PID		0xFF18
+#define FTDI_SCIENCESCOPE_LS_LOGBOOK_PID	0xFF1C
+#define FTDI_SCIENCESCOPE_HS_LOGBOOK_PID	0xFF1D
+
+/*
+ * Milkymist One JTAG/Serial
+ */
+#define QIHARDWARE_VID			0x20B7
+#define MILKYMISTONE_JTAGSERIAL_PID	0x0713
+
+/*
+ * CTI GmbH RS485 Converter http://www.cti-lean.com/
+ */
+/* USB-485-Mini*/
+#define FTDI_CTI_MINI_PID	0xF608
+/* USB-Nano-485*/
+#define FTDI_CTI_NANO_PID	0xF60B
+
+/*
+ * ZeitControl cardsystems GmbH rfid-readers http://zeitcontrol.de
+ */
+/* TagTracer MIFARE*/
+#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID	0xF7C0
+
+/*
+ * Rainforest Automation
+ */
+/* ZigBee controller */
+#define FTDI_RF_R106		0x8A28
+
+/*
+ * Product: HCP HIT GPRS modem
+ * Manufacturer: HCP d.o.o.
+ * ATI command output: Cinterion MC55i
+ */
+#define FTDI_CINTERION_MC55I_PID	0xA951
+
+/*
+ * Product: FirmwareHubEmulator
+ * Manufacturer: Harman Becker Automotive Systems
+ */
+#define FTDI_FHE_PID		0xA9A0
+
+/*
+ * Product: Comet Caller ID decoder
+ * Manufacturer: Crucible Technologies
+ */
+#define FTDI_CT_COMET_PID	0x8e08
+
+/*
+ * Product: Z3X Box
+ * Manufacturer: Smart GSM Team
+ */
+#define FTDI_Z3X_PID		0x0011
+
+/*
+ * Product: Cressi PC Interface
+ * Manufacturer: Cressi
+ */
+#define FTDI_CRESSI_PID		0x87d0
+
+/*
+ * Brainboxes devices
+ */
+#define BRAINBOXES_VID			0x05d1
+#define BRAINBOXES_VX_001_PID		0x1001 /* VX-001 ExpressCard 1 Port RS232 */
+#define BRAINBOXES_VX_012_PID		0x1002 /* VX-012 ExpressCard 2 Port RS232 */
+#define BRAINBOXES_VX_023_PID		0x1003 /* VX-023 ExpressCard 1 Port RS422/485 */
+#define BRAINBOXES_VX_034_PID		0x1004 /* VX-034 ExpressCard 2 Port RS422/485 */
+#define BRAINBOXES_US_101_PID		0x1011 /* US-101 1xRS232 */
+#define BRAINBOXES_US_324_PID		0x1013 /* US-324 1xRS422/485 1Mbaud */
+#define BRAINBOXES_US_606_1_PID		0x2001 /* US-606 6 Port RS232 Serial Port 1 and 2 */
+#define BRAINBOXES_US_606_2_PID		0x2002 /* US-606 6 Port RS232 Serial Port 3 and 4 */
+#define BRAINBOXES_US_606_3_PID		0x2003 /* US-606 6 Port RS232 Serial Port 4 and 6 */
+#define BRAINBOXES_US_701_1_PID		0x2011 /* US-701 4xRS232 1Mbaud Port 1 and 2 */
+#define BRAINBOXES_US_701_2_PID		0x2012 /* US-701 4xRS422 1Mbaud Port 3 and 4 */
+#define BRAINBOXES_US_279_1_PID		0x2021 /* US-279 8xRS422 1Mbaud Port 1 and 2 */
+#define BRAINBOXES_US_279_2_PID		0x2022 /* US-279 8xRS422 1Mbaud Port 3 and 4 */
+#define BRAINBOXES_US_279_3_PID		0x2023 /* US-279 8xRS422 1Mbaud Port 5 and 6 */
+#define BRAINBOXES_US_279_4_PID		0x2024 /* US-279 8xRS422 1Mbaud Port 7 and 8 */
+#define BRAINBOXES_US_346_1_PID		0x3011 /* US-346 4xRS422/485 1Mbaud Port 1 and 2 */
+#define BRAINBOXES_US_346_2_PID		0x3012 /* US-346 4xRS422/485 1Mbaud Port 3 and 4 */
+#define BRAINBOXES_US_257_PID		0x5001 /* US-257 2xRS232 1Mbaud */
+#define BRAINBOXES_US_313_PID		0x6001 /* US-313 2xRS422/485 1Mbaud */
+#define BRAINBOXES_US_357_PID		0x7001 /* US_357 1xRS232/422/485 */
+#define BRAINBOXES_US_842_1_PID		0x8001 /* US-842 8xRS422/485 1Mbaud Port 1 and 2 */
+#define BRAINBOXES_US_842_2_PID		0x8002 /* US-842 8xRS422/485 1Mbaud Port 3 and 4 */
+#define BRAINBOXES_US_842_3_PID		0x8003 /* US-842 8xRS422/485 1Mbaud Port 5 and 6 */
+#define BRAINBOXES_US_842_4_PID		0x8004 /* US-842 8xRS422/485 1Mbaud Port 7 and 8 */
+#define BRAINBOXES_US_160_1_PID		0x9001 /* US-160 16xRS232 1Mbaud Port 1 and 2 */
+#define BRAINBOXES_US_160_2_PID		0x9002 /* US-160 16xRS232 1Mbaud Port 3 and 4 */
+#define BRAINBOXES_US_160_3_PID		0x9003 /* US-160 16xRS232 1Mbaud Port 5 and 6 */
+#define BRAINBOXES_US_160_4_PID		0x9004 /* US-160 16xRS232 1Mbaud Port 7 and 8 */
+#define BRAINBOXES_US_160_5_PID		0x9005 /* US-160 16xRS232 1Mbaud Port 9 and 10 */
+#define BRAINBOXES_US_160_6_PID		0x9006 /* US-160 16xRS232 1Mbaud Port 11 and 12 */
+#define BRAINBOXES_US_160_7_PID		0x9007 /* US-160 16xRS232 1Mbaud Port 13 and 14 */
+#define BRAINBOXES_US_160_8_PID		0x9008 /* US-160 16xRS232 1Mbaud Port 15 and 16 */
+
+/*
+ * ekey biometric systems GmbH (http://ekey.net/)
+ */
+#define FTDI_EKEY_CONV_USB_PID		0xCB08	/* Converter USB */
+
+/*
+ * GE Healthcare devices
+ */
+#define GE_HEALTHCARE_VID		0x1901
+#define GE_HEALTHCARE_NEMO_TRACKER_PID	0x0015
+
+/*
+ * Active Research (Actisense) devices
+ */
+#define ACTISENSE_NDC_PID		0xD9A8 /* NDC USB Serial Adapter */
+#define ACTISENSE_USG_PID		0xD9A9 /* USG USB Serial Adapter */
+#define ACTISENSE_NGT_PID		0xD9AA /* NGT NMEA2000 Interface */
+#define ACTISENSE_NGW_PID		0xD9AB /* NGW NMEA2000 Gateway */
+#define ACTISENSE_D9AC_PID		0xD9AC /* Actisense Reserved */
+#define ACTISENSE_D9AD_PID		0xD9AD /* Actisense Reserved */
+#define ACTISENSE_D9AE_PID		0xD9AE /* Actisense Reserved */
+#define ACTISENSE_D9AF_PID		0xD9AF /* Actisense Reserved */
+#define CHETCO_SEAGAUGE_PID		0xA548 /* SeaGauge USB Adapter */
+#define CHETCO_SEASWITCH_PID		0xA549 /* SeaSwitch USB Adapter */
+#define CHETCO_SEASMART_NMEA2000_PID	0xA54A /* SeaSmart NMEA2000 Gateway */
+#define CHETCO_SEASMART_ETHERNET_PID	0xA54B /* SeaSmart Ethernet Gateway */
+#define CHETCO_SEASMART_WIFI_PID	0xA5AC /* SeaSmart Wifi Gateway */
+#define CHETCO_SEASMART_DISPLAY_PID	0xA5AD /* SeaSmart NMEA2000 Display */
+#define CHETCO_SEASMART_LITE_PID	0xA5AE /* SeaSmart Lite USB Adapter */
+#define CHETCO_SEASMART_ANALOG_PID	0xA5AF /* SeaSmart Analog Adapter */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
new file mode 100644
index 0000000..633550e
--- /dev/null
+++ b/drivers/usb/serial/garmin_gps.c
@@ -0,0 +1,1446 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Garmin GPS driver
+ *
+ * Copyright (C) 2006-2011 Hermann Kneissel herkne@gmx.de
+ *
+ * The latest version of the driver can be found at
+ * http://sourceforge.net/projects/garmin-gps/
+ *
+ * This driver has been derived from v2.1 of the visor driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* the mode to be set when the port ist opened */
+static int initial_mode = 1;
+
+#define GARMIN_VENDOR_ID             0x091E
+
+/*
+ * Version Information
+ */
+
+#define VERSION_MAJOR	0
+#define VERSION_MINOR	36
+
+#define _STR(s) #s
+#define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b)
+#define DRIVER_VERSION _DRIVER_VERSION(VERSION_MAJOR, VERSION_MINOR)
+#define DRIVER_AUTHOR "hermann kneissel"
+#define DRIVER_DESC "garmin gps driver"
+
+/* error codes returned by the driver */
+#define EINVPKT	1000	/* invalid packet structure */
+
+
+/* size of the header of a packet using the usb protocol */
+#define GARMIN_PKTHDR_LENGTH	12
+
+/* max. possible size of a packet using the serial protocol */
+#define MAX_SERIAL_PKT_SIZ (3 + 255 + 3)
+
+/*  max. possible size of a packet with worst case stuffing */
+#define MAX_SERIAL_PKT_SIZ_STUFFED (MAX_SERIAL_PKT_SIZ + 256)
+
+/* size of a buffer able to hold a complete (no stuffing) packet
+ * (the document protocol does not contain packets with a larger
+ *  size, but in theory a packet may be 64k+12 bytes - if in
+ *  later protocol versions larger packet sizes occur, this value
+ *  should be increased accordingly, so the input buffer is always
+ *  large enough the store a complete packet inclusive header) */
+#define GPS_IN_BUFSIZ  (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ)
+
+/* size of a buffer able to hold a complete (incl. stuffing) packet */
+#define GPS_OUT_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ_STUFFED)
+
+/* where to place the packet id of a serial packet, so we can
+ * prepend the usb-packet header without the need to move the
+ * packets data */
+#define GSP_INITIAL_OFFSET (GARMIN_PKTHDR_LENGTH-2)
+
+/* max. size of incoming private packets (header+1 param) */
+#define PRIVPKTSIZ (GARMIN_PKTHDR_LENGTH+4)
+
+#define GARMIN_LAYERID_TRANSPORT  0
+#define GARMIN_LAYERID_APPL      20
+/* our own layer-id to use for some control mechanisms */
+#define GARMIN_LAYERID_PRIVATE	0x01106E4B
+
+#define GARMIN_PKTID_PVT_DATA	51
+#define GARMIN_PKTID_L001_COMMAND_DATA 10
+
+#define CMND_ABORT_TRANSFER 0
+
+/* packet ids used in private layer */
+#define PRIV_PKTID_SET_DEBUG	1
+#define PRIV_PKTID_SET_MODE	2
+#define PRIV_PKTID_INFO_REQ	3
+#define PRIV_PKTID_INFO_RESP	4
+#define PRIV_PKTID_RESET_REQ	5
+#define PRIV_PKTID_SET_DEF_MODE	6
+
+
+#define ETX	0x03
+#define DLE	0x10
+#define ACK	0x06
+#define NAK	0x15
+
+/* structure used to queue incoming packets */
+struct garmin_packet {
+	struct list_head  list;
+	int               seq;
+	/* the real size of the data array, always > 0 */
+	int               size;
+	__u8              data[1];
+};
+
+/* structure used to keep the current state of the driver */
+struct garmin_data {
+	__u8   state;
+	__u16  flags;
+	__u8   mode;
+	__u8   count;
+	__u8   pkt_id;
+	__u32  serial_num;
+	struct timer_list timer;
+	struct usb_serial_port *port;
+	int    seq_counter;
+	int    insize;
+	int    outsize;
+	__u8   inbuffer [GPS_IN_BUFSIZ];  /* tty -> usb */
+	__u8   outbuffer[GPS_OUT_BUFSIZ]; /* usb -> tty */
+	__u8   privpkt[4*6];
+	spinlock_t lock;
+	struct list_head pktlist;
+	struct usb_anchor write_urbs;
+};
+
+
+#define STATE_NEW            0
+#define STATE_INITIAL_DELAY  1
+#define STATE_TIMEOUT        2
+#define STATE_SESSION_REQ1   3
+#define STATE_SESSION_REQ2   4
+#define STATE_ACTIVE         5
+
+#define STATE_RESET	     8
+#define STATE_DISCONNECTED   9
+#define STATE_WAIT_TTY_ACK  10
+#define STATE_GSP_WAIT_DATA 11
+
+#define MODE_NATIVE          0
+#define MODE_GARMIN_SERIAL   1
+
+/* Flags used in garmin_data.flags: */
+#define FLAGS_SESSION_REPLY_MASK  0x00C0
+#define FLAGS_SESSION_REPLY1_SEEN 0x0080
+#define FLAGS_SESSION_REPLY2_SEEN 0x0040
+#define FLAGS_BULK_IN_ACTIVE      0x0020
+#define FLAGS_BULK_IN_RESTART     0x0010
+#define FLAGS_THROTTLED           0x0008
+#define APP_REQ_SEEN              0x0004
+#define APP_RESP_SEEN             0x0002
+#define CLEAR_HALT_REQUIRED       0x0001
+
+#define FLAGS_QUEUING             0x0100
+#define FLAGS_DROP_DATA           0x0800
+
+#define FLAGS_GSP_SKIP            0x1000
+#define FLAGS_GSP_DLESEEN         0x2000
+
+
+
+
+
+
+/* function prototypes */
+static int gsp_next_packet(struct garmin_data *garmin_data_p);
+static int garmin_write_bulk(struct usb_serial_port *port,
+			     const unsigned char *buf, int count,
+			     int dismiss_ack);
+
+/* some special packets to be send or received */
+static unsigned char const GARMIN_START_SESSION_REQ[]
+	= { 0, 0, 0, 0,  5, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_START_SESSION_REPLY[]
+	= { 0, 0, 0, 0,  6, 0, 0, 0, 4, 0, 0, 0 };
+static unsigned char const GARMIN_BULK_IN_AVAIL_REPLY[]
+	= { 0, 0, 0, 0,  2, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_APP_LAYER_REPLY[]
+	= { 0x14, 0, 0, 0 };
+static unsigned char const GARMIN_START_PVT_REQ[]
+	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 49, 0 };
+static unsigned char const GARMIN_STOP_PVT_REQ[]
+	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 50, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ[]
+	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ_V2[]
+	= { 20, 0, 0, 0,  10, 0, 0, 0, 1, 0, 0, 0, 0 };
+static unsigned char const PRIVATE_REQ[]
+	=    { 0x4B, 0x6E, 0x10, 0x01,  0xFF, 0, 0, 0, 0xFF, 0, 0, 0 };
+
+
+
+static const struct usb_device_id id_table[] = {
+	/* the same device id seems to be used by all
+	   usb enabled GPS devices */
+	{ USB_DEVICE(GARMIN_VENDOR_ID, 3) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+static inline int getLayerId(const __u8 *usbPacket)
+{
+	return __le32_to_cpup((__le32 *)(usbPacket));
+}
+
+static inline int getPacketId(const __u8 *usbPacket)
+{
+	return __le32_to_cpup((__le32 *)(usbPacket+4));
+}
+
+static inline int getDataLength(const __u8 *usbPacket)
+{
+	return __le32_to_cpup((__le32 *)(usbPacket+8));
+}
+
+
+/*
+ * check if the usb-packet in buf contains an abort-transfer command.
+ * (if yes, all queued data will be dropped)
+ */
+static inline int isAbortTrfCmnd(const unsigned char *buf)
+{
+	if (memcmp(buf, GARMIN_STOP_TRANSFER_REQ,
+			sizeof(GARMIN_STOP_TRANSFER_REQ)) == 0 ||
+	    memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2,
+			sizeof(GARMIN_STOP_TRANSFER_REQ_V2)) == 0)
+		return 1;
+	else
+		return 0;
+}
+
+
+
+static void send_to_tty(struct usb_serial_port *port,
+			char *data, unsigned int actual_length)
+{
+	if (actual_length) {
+		usb_serial_debug_data(&port->dev, __func__, actual_length, data);
+		tty_insert_flip_string(&port->port, data, actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+}
+
+
+/******************************************************************************
+ * packet queue handling
+ ******************************************************************************/
+
+/*
+ * queue a received (usb-)packet for later processing
+ */
+static int pkt_add(struct garmin_data *garmin_data_p,
+		   unsigned char *data, unsigned int data_length)
+{
+	int state = 0;
+	int result = 0;
+	unsigned long flags;
+	struct garmin_packet *pkt;
+
+	/* process only packets containing data ... */
+	if (data_length) {
+		pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
+								GFP_ATOMIC);
+		if (!pkt)
+			return 0;
+
+		pkt->size = data_length;
+		memcpy(pkt->data, data, data_length);
+
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags |= FLAGS_QUEUING;
+		result = list_empty(&garmin_data_p->pktlist);
+		pkt->seq = garmin_data_p->seq_counter++;
+		list_add_tail(&pkt->list, &garmin_data_p->pktlist);
+		state = garmin_data_p->state;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+		dev_dbg(&garmin_data_p->port->dev,
+			"%s - added: pkt: %d - %d bytes\n", __func__,
+			pkt->seq, data_length);
+
+		/* in serial mode, if someone is waiting for data from
+		   the device, convert and send the next packet to tty. */
+		if (result && (state == STATE_GSP_WAIT_DATA))
+			gsp_next_packet(garmin_data_p);
+	}
+	return result;
+}
+
+
+/* get the next pending packet */
+static struct garmin_packet *pkt_pop(struct garmin_data *garmin_data_p)
+{
+	unsigned long flags;
+	struct garmin_packet *result = NULL;
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	if (!list_empty(&garmin_data_p->pktlist)) {
+		result = (struct garmin_packet *)garmin_data_p->pktlist.next;
+		list_del(&result->list);
+	}
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+	return result;
+}
+
+
+/* free up all queued data */
+static void pkt_clear(struct garmin_data *garmin_data_p)
+{
+	unsigned long flags;
+	struct garmin_packet *result = NULL;
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	while (!list_empty(&garmin_data_p->pktlist)) {
+		result = (struct garmin_packet *)garmin_data_p->pktlist.next;
+		list_del(&result->list);
+		kfree(result);
+	}
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+}
+
+
+/******************************************************************************
+ * garmin serial protocol handling handling
+ ******************************************************************************/
+
+/* send an ack packet back to the tty */
+static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id)
+{
+	__u8 pkt[10];
+	__u8 cksum = 0;
+	__u8 *ptr = pkt;
+	unsigned  l = 0;
+
+	dev_dbg(&garmin_data_p->port->dev, "%s - pkt-id: 0x%X.\n", __func__,
+			pkt_id);
+
+	*ptr++ = DLE;
+	*ptr++ = ACK;
+	cksum += ACK;
+
+	*ptr++ = 2;
+	cksum += 2;
+
+	*ptr++ = pkt_id;
+	cksum += pkt_id;
+
+	if (pkt_id == DLE)
+		*ptr++ = DLE;
+
+	*ptr++ = 0;
+	*ptr++ = (-cksum) & 0xFF;
+	*ptr++ = DLE;
+	*ptr++ = ETX;
+
+	l = ptr-pkt;
+
+	send_to_tty(garmin_data_p->port, pkt, l);
+	return 0;
+}
+
+
+
+/*
+ * called for a complete packet received from tty layer
+ *
+ * the complete packet (pktid ... cksum) is in garmin_data_p->inbuf starting
+ * at GSP_INITIAL_OFFSET.
+ *
+ * count - number of bytes in the input buffer including space reserved for
+ *         the usb header: GSP_INITIAL_OFFSET + number of bytes in packet
+ *         (including pkt-id, data-length a. cksum)
+ */
+static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
+{
+	struct device *dev = &garmin_data_p->port->dev;
+	unsigned long flags;
+	const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
+	__le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
+	int cksum = 0;
+	int n = 0;
+	int pktid = recpkt[0];
+	int size = recpkt[1];
+
+	usb_serial_debug_data(&garmin_data_p->port->dev, __func__,
+			      count-GSP_INITIAL_OFFSET, recpkt);
+
+	if (size != (count-GSP_INITIAL_OFFSET-3)) {
+		dev_dbg(dev, "%s - invalid size, expected %d bytes, got %d\n",
+			__func__, size, (count-GSP_INITIAL_OFFSET-3));
+		return -EINVPKT;
+	}
+
+	cksum += *recpkt++;
+	cksum += *recpkt++;
+
+	/* sanity check, remove after test ... */
+	if ((__u8 *)&(usbdata[3]) != recpkt) {
+		dev_dbg(dev, "%s - ptr mismatch %p - %p\n", __func__,
+			&(usbdata[4]), recpkt);
+		return -EINVPKT;
+	}
+
+	while (n < size) {
+		cksum += *recpkt++;
+		n++;
+	}
+
+	if (((cksum + *recpkt) & 0xff) != 0) {
+		dev_dbg(dev, "%s - invalid checksum, expected %02x, got %02x\n",
+			__func__, -cksum & 0xff, *recpkt);
+		return -EINVPKT;
+	}
+
+	usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
+	usbdata[1] = __cpu_to_le32(pktid);
+	usbdata[2] = __cpu_to_le32(size);
+
+	garmin_write_bulk(garmin_data_p->port, garmin_data_p->inbuffer,
+			   GARMIN_PKTHDR_LENGTH+size, 0);
+
+	/* if this was an abort-transfer command, flush all
+	   queued data. */
+	if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags |= FLAGS_DROP_DATA;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+		pkt_clear(garmin_data_p);
+	}
+
+	return count;
+}
+
+
+
+/*
+ * Called for data received from tty
+ *
+ * buf contains the data read, it may span more than one packet or even
+ * incomplete packets
+ *
+ * input record should be a serial-record, but it may not be complete.
+ * Copy it into our local buffer, until an etx is seen (or an error
+ * occurs).
+ * Once the record is complete, convert into a usb packet and send it
+ * to the bulk pipe, send an ack back to the tty.
+ *
+ * If the input is an ack, just send the last queued packet to the
+ * tty layer.
+ *
+ * if the input is an abort command, drop all queued data.
+ */
+
+static int gsp_receive(struct garmin_data *garmin_data_p,
+		       const unsigned char *buf, int count)
+{
+	struct device *dev = &garmin_data_p->port->dev;
+	unsigned long flags;
+	int offs = 0;
+	int ack_or_nak_seen = 0;
+	__u8 *dest;
+	int size;
+	/* dleSeen: set if last byte read was a DLE */
+	int dleSeen;
+	/* skip: if set, skip incoming data until possible start of
+	 *       new packet
+	 */
+	int skip;
+	__u8 data;
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	dest = garmin_data_p->inbuffer;
+	size = garmin_data_p->insize;
+	dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+	skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	/* dev_dbg(dev, "%s - dle=%d skip=%d size=%d count=%d\n",
+		__func__, dleSeen, skip, size, count); */
+
+	if (size == 0)
+		size = GSP_INITIAL_OFFSET;
+
+	while (offs < count) {
+
+		data = *(buf+offs);
+		offs++;
+
+		if (data == DLE) {
+			if (skip) { /* start of a new pkt */
+				skip = 0;
+				size = GSP_INITIAL_OFFSET;
+				dleSeen = 1;
+			} else if (dleSeen) {
+				dest[size++] = data;
+				dleSeen = 0;
+			} else {
+				dleSeen = 1;
+			}
+		} else if (data == ETX) {
+			if (dleSeen) {
+				/* packet complete */
+
+				data = dest[GSP_INITIAL_OFFSET];
+
+				if (data == ACK) {
+					ack_or_nak_seen = ACK;
+					dev_dbg(dev, "ACK packet complete.\n");
+				} else if (data == NAK) {
+					ack_or_nak_seen = NAK;
+					dev_dbg(dev, "NAK packet complete.\n");
+				} else {
+					dev_dbg(dev, "packet complete - id=0x%X.\n",
+							data);
+					gsp_rec_packet(garmin_data_p, size);
+				}
+
+				skip = 1;
+				size = GSP_INITIAL_OFFSET;
+				dleSeen = 0;
+			} else {
+				dest[size++] = data;
+			}
+		} else if (!skip) {
+
+			if (dleSeen) {
+				size = GSP_INITIAL_OFFSET;
+				dleSeen = 0;
+			}
+
+			dest[size++] = data;
+		}
+
+		if (size >= GPS_IN_BUFSIZ) {
+			dev_dbg(dev, "%s - packet too large.\n", __func__);
+			skip = 1;
+			size = GSP_INITIAL_OFFSET;
+			dleSeen = 0;
+		}
+	}
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+
+	garmin_data_p->insize = size;
+
+	/* copy flags back to structure */
+	if (skip)
+		garmin_data_p->flags |= FLAGS_GSP_SKIP;
+	else
+		garmin_data_p->flags &= ~FLAGS_GSP_SKIP;
+
+	if (dleSeen)
+		garmin_data_p->flags |= FLAGS_GSP_DLESEEN;
+	else
+		garmin_data_p->flags &= ~FLAGS_GSP_DLESEEN;
+
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	if (ack_or_nak_seen) {
+		if (gsp_next_packet(garmin_data_p) > 0)
+			garmin_data_p->state = STATE_ACTIVE;
+		else
+			garmin_data_p->state = STATE_GSP_WAIT_DATA;
+	}
+	return count;
+}
+
+
+
+/*
+ * Sends a usb packet to the tty
+ *
+ * Assumes, that all packages and at an usb-packet boundary.
+ *
+ * return <0 on error, 0 if packet is incomplete or > 0 if packet was sent
+ */
+static int gsp_send(struct garmin_data *garmin_data_p,
+		    const unsigned char *buf, int count)
+{
+	struct device *dev = &garmin_data_p->port->dev;
+	const unsigned char *src;
+	unsigned char *dst;
+	int pktid = 0;
+	int datalen = 0;
+	int cksum = 0;
+	int i = 0;
+	int k;
+
+	dev_dbg(dev, "%s - state %d - %d bytes.\n", __func__,
+		garmin_data_p->state, count);
+
+	k = garmin_data_p->outsize;
+	if ((k+count) > GPS_OUT_BUFSIZ) {
+		dev_dbg(dev, "packet too large\n");
+		garmin_data_p->outsize = 0;
+		return -4;
+	}
+
+	memcpy(garmin_data_p->outbuffer+k, buf, count);
+	k += count;
+	garmin_data_p->outsize = k;
+
+	if (k >= GARMIN_PKTHDR_LENGTH) {
+		pktid  = getPacketId(garmin_data_p->outbuffer);
+		datalen = getDataLength(garmin_data_p->outbuffer);
+		i = GARMIN_PKTHDR_LENGTH + datalen;
+		if (k < i)
+			return 0;
+	} else {
+		return 0;
+	}
+
+	dev_dbg(dev, "%s - %d bytes in buffer, %d bytes in pkt.\n", __func__, k, i);
+
+	/* garmin_data_p->outbuffer now contains a complete packet */
+
+	usb_serial_debug_data(&garmin_data_p->port->dev, __func__, k,
+			      garmin_data_p->outbuffer);
+
+	garmin_data_p->outsize = 0;
+
+	if (getLayerId(garmin_data_p->outbuffer) != GARMIN_LAYERID_APPL) {
+		dev_dbg(dev, "not an application packet (%d)\n",
+				getLayerId(garmin_data_p->outbuffer));
+		return -1;
+	}
+
+	if (pktid > 255) {
+		dev_dbg(dev, "packet-id %d too large\n", pktid);
+		return -2;
+	}
+
+	if (datalen > 255) {
+		dev_dbg(dev, "packet-size %d too large\n", datalen);
+		return -3;
+	}
+
+	/* the serial protocol should be able to handle this packet */
+
+	k = 0;
+	src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
+	for (i = 0; i < datalen; i++) {
+		if (*src++ == DLE)
+			k++;
+	}
+
+	src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
+	if (k > (GARMIN_PKTHDR_LENGTH-2)) {
+		/* can't add stuffing DLEs in place, move data to end
+		   of buffer ... */
+		dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
+		memcpy(dst, src, datalen);
+		src = dst;
+	}
+
+	dst = garmin_data_p->outbuffer;
+
+	*dst++ = DLE;
+	*dst++ = pktid;
+	cksum += pktid;
+	*dst++ = datalen;
+	cksum += datalen;
+	if (datalen == DLE)
+		*dst++ = DLE;
+
+	for (i = 0; i < datalen; i++) {
+		__u8 c = *src++;
+		*dst++ = c;
+		cksum += c;
+		if (c == DLE)
+			*dst++ = DLE;
+	}
+
+	cksum = -cksum & 0xFF;
+	*dst++ = cksum;
+	if (cksum == DLE)
+		*dst++ = DLE;
+	*dst++ = DLE;
+	*dst++ = ETX;
+
+	i = dst-garmin_data_p->outbuffer;
+
+	send_to_tty(garmin_data_p->port, garmin_data_p->outbuffer, i);
+
+	garmin_data_p->pkt_id = pktid;
+	garmin_data_p->state  = STATE_WAIT_TTY_ACK;
+
+	return i;
+}
+
+
+/*
+ * Process the next pending data packet - if there is one
+ */
+static int gsp_next_packet(struct garmin_data *garmin_data_p)
+{
+	int result = 0;
+	struct garmin_packet *pkt = NULL;
+
+	while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
+		dev_dbg(&garmin_data_p->port->dev, "%s - next pkt: %d\n", __func__, pkt->seq);
+		result = gsp_send(garmin_data_p, pkt->data, pkt->size);
+		if (result > 0) {
+			kfree(pkt);
+			return result;
+		}
+		kfree(pkt);
+	}
+	return result;
+}
+
+
+
+/******************************************************************************
+ * garmin native mode
+ ******************************************************************************/
+
+
+/*
+ * Called for data received from tty
+ *
+ * The input data is expected to be in garmin usb-packet format.
+ *
+ * buf contains the data read, it may span more than one packet
+ * or even incomplete packets
+ */
+static int nat_receive(struct garmin_data *garmin_data_p,
+		       const unsigned char *buf, int count)
+{
+	unsigned long flags;
+	__u8 *dest;
+	int offs = 0;
+	int result = count;
+	int len;
+
+	while (offs < count) {
+		/* if buffer contains header, copy rest of data */
+		if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH)
+			len = GARMIN_PKTHDR_LENGTH
+			      +getDataLength(garmin_data_p->inbuffer);
+		else
+			len = GARMIN_PKTHDR_LENGTH;
+
+		if (len >= GPS_IN_BUFSIZ) {
+			/* seems to be an invalid packet, ignore rest
+			   of input */
+			dev_dbg(&garmin_data_p->port->dev,
+				"%s - packet size too large: %d\n",
+				__func__, len);
+			garmin_data_p->insize = 0;
+			count = 0;
+			result = -EINVPKT;
+		} else {
+			len -= garmin_data_p->insize;
+			if (len > (count-offs))
+				len = (count-offs);
+			if (len > 0) {
+				dest = garmin_data_p->inbuffer
+						+ garmin_data_p->insize;
+				memcpy(dest, buf+offs, len);
+				garmin_data_p->insize += len;
+				offs += len;
+			}
+		}
+
+		/* do we have a complete packet ? */
+		if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH) {
+			len = GARMIN_PKTHDR_LENGTH+
+			   getDataLength(garmin_data_p->inbuffer);
+			if (garmin_data_p->insize >= len) {
+				garmin_write_bulk(garmin_data_p->port,
+						   garmin_data_p->inbuffer,
+						   len, 0);
+				garmin_data_p->insize = 0;
+
+				/* if this was an abort-transfer command,
+				   flush all queued data. */
+				if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+					spin_lock_irqsave(&garmin_data_p->lock,
+									flags);
+					garmin_data_p->flags |= FLAGS_DROP_DATA;
+					spin_unlock_irqrestore(
+						&garmin_data_p->lock, flags);
+					pkt_clear(garmin_data_p);
+				}
+			}
+		}
+	}
+	return result;
+}
+
+
+/******************************************************************************
+ * private packets
+ ******************************************************************************/
+
+static void priv_status_resp(struct usb_serial_port *port)
+{
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	__le32 *pkt = (__le32 *)garmin_data_p->privpkt;
+
+	pkt[0] = __cpu_to_le32(GARMIN_LAYERID_PRIVATE);
+	pkt[1] = __cpu_to_le32(PRIV_PKTID_INFO_RESP);
+	pkt[2] = __cpu_to_le32(12);
+	pkt[3] = __cpu_to_le32(VERSION_MAJOR << 16 | VERSION_MINOR);
+	pkt[4] = __cpu_to_le32(garmin_data_p->mode);
+	pkt[5] = __cpu_to_le32(garmin_data_p->serial_num);
+
+	send_to_tty(port, (__u8 *)pkt, 6 * 4);
+}
+
+
+/******************************************************************************
+ * Garmin specific driver functions
+ ******************************************************************************/
+
+static int process_resetdev_request(struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int status;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
+	garmin_data_p->state = STATE_RESET;
+	garmin_data_p->serial_num = 0;
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	usb_kill_urb(port->interrupt_in_urb);
+	dev_dbg(&port->dev, "%s - usb_reset_device\n", __func__);
+	status = usb_reset_device(port->serial->dev);
+	if (status)
+		dev_dbg(&port->dev, "%s - usb_reset_device failed: %d\n",
+			__func__, status);
+	return status;
+}
+
+
+
+/*
+ * clear all cached data
+ */
+static int garmin_clear(struct garmin_data *garmin_data_p)
+{
+	unsigned long flags;
+
+	/* flush all queued data */
+	pkt_clear(garmin_data_p);
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	garmin_data_p->insize = 0;
+	garmin_data_p->outsize = 0;
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	return 0;
+}
+
+
+static int garmin_init_session(struct usb_serial_port *port)
+{
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	int status;
+	int i;
+
+	usb_kill_urb(port->interrupt_in_urb);
+
+	status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (status) {
+		dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+				status);
+		return status;
+	}
+
+	/*
+	 * using the initialization method from gpsbabel. See comments in
+	 * gpsbabel/jeeps/gpslibusb.c gusb_reset_toggles()
+	 */
+	dev_dbg(&port->dev, "%s - starting session ...\n", __func__);
+	garmin_data_p->state = STATE_ACTIVE;
+
+	for (i = 0; i < 3; i++) {
+		status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ,
+				sizeof(GARMIN_START_SESSION_REQ), 0);
+		if (status < 0)
+			goto err_kill_urbs;
+	}
+
+	return 0;
+
+err_kill_urbs:
+	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	return status;
+}
+
+
+
+static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int status = 0;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	garmin_data_p->mode  = initial_mode;
+	garmin_data_p->count = 0;
+	garmin_data_p->flags &= FLAGS_SESSION_REPLY1_SEEN;
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	/* shutdown any bulk reads that might be going on */
+	usb_kill_urb(port->read_urb);
+
+	if (garmin_data_p->state == STATE_RESET)
+		status = garmin_init_session(port);
+
+	garmin_data_p->state = STATE_ACTIVE;
+	return status;
+}
+
+
+static void garmin_close(struct usb_serial_port *port)
+{
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	dev_dbg(&port->dev, "%s - mode=%d state=%d flags=0x%X\n",
+		__func__, garmin_data_p->mode, garmin_data_p->state,
+		garmin_data_p->flags);
+
+	garmin_clear(garmin_data_p);
+
+	/* shutdown our urbs */
+	usb_kill_urb(port->read_urb);
+	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
+
+	/* keep reset state so we know that we must start a new session */
+	if (garmin_data_p->state != STATE_RESET)
+		garmin_data_p->state = STATE_DISCONNECTED;
+}
+
+
+static void garmin_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+
+	if (port) {
+		struct garmin_data *garmin_data_p =
+					usb_get_serial_port_data(port);
+
+		if (getLayerId(urb->transfer_buffer) == GARMIN_LAYERID_APPL) {
+
+			if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+				gsp_send_ack(garmin_data_p,
+					((__u8 *)urb->transfer_buffer)[4]);
+			}
+		}
+		usb_serial_port_softint(port);
+	}
+
+	/* Ignore errors that resulted from garmin_write_bulk with
+	   dismiss_ack = 1 */
+
+	/* free up the transfer buffer, as usb_free_urb() does not do this */
+	kfree(urb->transfer_buffer);
+}
+
+
+static int garmin_write_bulk(struct usb_serial_port *port,
+			      const unsigned char *buf, int count,
+			      int dismiss_ack)
+{
+	unsigned long flags;
+	struct usb_serial *serial = port->serial;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	struct urb *urb;
+	unsigned char *buffer;
+	int status;
+
+	spin_lock_irqsave(&garmin_data_p->lock, flags);
+	garmin_data_p->flags &= ~FLAGS_DROP_DATA;
+	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+	buffer = kmalloc(count, GFP_ATOMIC);
+	if (!buffer)
+		return -ENOMEM;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		kfree(buffer);
+		return -ENOMEM;
+	}
+
+	memcpy(buffer, buf, count);
+
+	usb_serial_debug_data(&port->dev, __func__, count, buffer);
+
+	usb_fill_bulk_urb(urb, serial->dev,
+				usb_sndbulkpipe(serial->dev,
+					port->bulk_out_endpointAddress),
+				buffer, count,
+				garmin_write_bulk_callback,
+				dismiss_ack ? NULL : port);
+	urb->transfer_flags |= URB_ZERO_PACKET;
+
+	if (getLayerId(buffer) == GARMIN_LAYERID_APPL) {
+
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags |= APP_REQ_SEEN;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+		if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
+			pkt_clear(garmin_data_p);
+			garmin_data_p->state = STATE_GSP_WAIT_DATA;
+		}
+	}
+
+	/* send it down the pipe */
+	usb_anchor_urb(urb, &garmin_data_p->write_urbs);
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&port->dev,
+		   "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+				__func__, status);
+		count = status;
+		usb_unanchor_urb(urb);
+		kfree(buffer);
+	}
+
+	/* we are done with this urb, so let the host driver
+	 * really free it when it is finished with it */
+	usb_free_urb(urb);
+
+	return count;
+}
+
+static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
+					 const unsigned char *buf, int count)
+{
+	struct device *dev = &port->dev;
+	int pktid, pktsiz, len;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	__le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
+
+	usb_serial_debug_data(dev, __func__, count, buf);
+
+	if (garmin_data_p->state == STATE_RESET)
+		return -EIO;
+
+	/* check for our private packets */
+	if (count >= GARMIN_PKTHDR_LENGTH) {
+		len = PRIVPKTSIZ;
+		if (count < len)
+			len = count;
+
+		memcpy(garmin_data_p->privpkt, buf, len);
+
+		pktsiz = getDataLength(garmin_data_p->privpkt);
+		pktid  = getPacketId(garmin_data_p->privpkt);
+
+		if (count == (GARMIN_PKTHDR_LENGTH + pktsiz) &&
+				getLayerId(garmin_data_p->privpkt) ==
+						GARMIN_LAYERID_PRIVATE) {
+
+			dev_dbg(dev, "%s - processing private request %d\n",
+				__func__, pktid);
+
+			/* drop all unfinished transfers */
+			garmin_clear(garmin_data_p);
+
+			switch (pktid) {
+			case PRIV_PKTID_SET_MODE:
+				if (pktsiz != 4)
+					return -EINVPKT;
+				garmin_data_p->mode = __le32_to_cpu(privpkt[3]);
+				dev_dbg(dev, "%s - mode set to %d\n",
+					__func__, garmin_data_p->mode);
+				break;
+
+			case PRIV_PKTID_INFO_REQ:
+				priv_status_resp(port);
+				break;
+
+			case PRIV_PKTID_RESET_REQ:
+				process_resetdev_request(port);
+				break;
+
+			case PRIV_PKTID_SET_DEF_MODE:
+				if (pktsiz != 4)
+					return -EINVPKT;
+				initial_mode = __le32_to_cpu(privpkt[3]);
+				dev_dbg(dev, "%s - initial_mode set to %d\n",
+					__func__,
+					garmin_data_p->mode);
+				break;
+			}
+			return count;
+		}
+	}
+
+	if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+		return gsp_receive(garmin_data_p, buf, count);
+	} else {	/* MODE_NATIVE */
+		return nat_receive(garmin_data_p, buf, count);
+	}
+}
+
+
+static int garmin_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	/*
+	 * Report back the bytes currently available in the output buffer.
+	 */
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	return GPS_OUT_BUFSIZ-garmin_data_p->outsize;
+}
+
+
+static void garmin_read_process(struct garmin_data *garmin_data_p,
+				 unsigned char *data, unsigned data_length,
+				 int bulk_data)
+{
+	unsigned long flags;
+
+	if (garmin_data_p->flags & FLAGS_DROP_DATA) {
+		/* abort-transfer cmd is active */
+		dev_dbg(&garmin_data_p->port->dev, "%s - pkt dropped\n", __func__);
+	} else if (garmin_data_p->state != STATE_DISCONNECTED &&
+		garmin_data_p->state != STATE_RESET) {
+
+		/* if throttling is active or postprecessing is required
+		   put the received data in the input queue, otherwise
+		   send it directly to the tty port */
+		if (garmin_data_p->flags & FLAGS_QUEUING) {
+			pkt_add(garmin_data_p, data, data_length);
+		} else if (bulk_data ||
+			   getLayerId(data) == GARMIN_LAYERID_APPL) {
+
+			spin_lock_irqsave(&garmin_data_p->lock, flags);
+			garmin_data_p->flags |= APP_RESP_SEEN;
+			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+			if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+				pkt_add(garmin_data_p, data, data_length);
+			} else {
+				send_to_tty(garmin_data_p->port, data,
+						data_length);
+			}
+		}
+		/* ignore system layer packets ... */
+	}
+}
+
+
+static void garmin_read_bulk_callback(struct urb *urb)
+{
+	unsigned long flags;
+	struct usb_serial_port *port = urb->context;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	int retval;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
+			__func__, status);
+		return;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	garmin_read_process(garmin_data_p, data, urb->actual_length, 1);
+
+	if (urb->actual_length == 0 &&
+			(garmin_data_p->flags & FLAGS_BULK_IN_RESTART) != 0) {
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (retval)
+			dev_err(&port->dev,
+				"%s - failed resubmitting read urb, error %d\n",
+				__func__, retval);
+	} else if (urb->actual_length > 0) {
+		/* Continue trying to read until nothing more is received  */
+		if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
+			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (retval)
+				dev_err(&port->dev,
+					"%s - failed resubmitting read urb, error %d\n",
+					__func__, retval);
+		}
+	} else {
+		dev_dbg(&port->dev, "%s - end of bulk data\n", __func__);
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+	}
+}
+
+
+static void garmin_read_int_callback(struct urb *urb)
+{
+	unsigned long flags;
+	int retval;
+	struct usb_serial_port *port = urb->context;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		return;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
+			      urb->transfer_buffer);
+
+	if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) &&
+		memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY,
+				sizeof(GARMIN_BULK_IN_AVAIL_REPLY)) == 0) {
+
+		dev_dbg(&port->dev, "%s - bulk data available.\n", __func__);
+
+		if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) == 0) {
+
+			/* bulk data available */
+			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (retval) {
+				dev_err(&port->dev,
+				 "%s - failed submitting read urb, error %d\n",
+							__func__, retval);
+			} else {
+				spin_lock_irqsave(&garmin_data_p->lock, flags);
+				garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
+				spin_unlock_irqrestore(&garmin_data_p->lock,
+									flags);
+			}
+		} else {
+			/* bulk-in transfer still active */
+			spin_lock_irqsave(&garmin_data_p->lock, flags);
+			garmin_data_p->flags |= FLAGS_BULK_IN_RESTART;
+			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+		}
+
+	} else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
+			 && memcmp(data, GARMIN_START_SESSION_REPLY,
+				 sizeof(GARMIN_START_SESSION_REPLY)) == 0) {
+
+		spin_lock_irqsave(&garmin_data_p->lock, flags);
+		garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
+		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+		/* save the serial number */
+		garmin_data_p->serial_num = __le32_to_cpup(
+					(__le32 *)(data+GARMIN_PKTHDR_LENGTH));
+
+		dev_dbg(&port->dev, "%s - start-of-session reply seen - serial %u.\n",
+			__func__, garmin_data_p->serial_num);
+	}
+
+	garmin_read_process(garmin_data_p, data, urb->actual_length, 0);
+
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting interrupt urb\n",
+			__func__, retval);
+}
+
+
+/*
+ * Sends the next queued packt to the tty port (garmin native mode only)
+ * and then sets a timer to call itself again until all queued data
+ * is sent.
+ */
+static int garmin_flush_queue(struct garmin_data *garmin_data_p)
+{
+	unsigned long flags;
+	struct garmin_packet *pkt;
+
+	if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
+		pkt = pkt_pop(garmin_data_p);
+		if (pkt != NULL) {
+			send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
+			kfree(pkt);
+			mod_timer(&garmin_data_p->timer, (1)+jiffies);
+
+		} else {
+			spin_lock_irqsave(&garmin_data_p->lock, flags);
+			garmin_data_p->flags &= ~FLAGS_QUEUING;
+			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+		}
+	}
+	return 0;
+}
+
+
+static void garmin_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	/* set flag, data received will be put into a queue
+	   for later processing */
+	spin_lock_irq(&garmin_data_p->lock);
+	garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
+	spin_unlock_irq(&garmin_data_p->lock);
+}
+
+
+static void garmin_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	int status;
+
+	spin_lock_irq(&garmin_data_p->lock);
+	garmin_data_p->flags &= ~FLAGS_THROTTLED;
+	spin_unlock_irq(&garmin_data_p->lock);
+
+	/* in native mode send queued data to tty, in
+	   serial mode nothing needs to be done here */
+	if (garmin_data_p->mode == MODE_NATIVE)
+		garmin_flush_queue(garmin_data_p);
+
+	if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) != 0) {
+		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
+		if (status)
+			dev_err(&port->dev,
+				"%s - failed resubmitting read urb, error %d\n",
+				__func__, status);
+	}
+}
+
+/*
+ * The timer is currently only used to send queued packets to
+ * the tty in cases where the protocol provides no own handshaking
+ * to initiate the transfer.
+ */
+static void timeout_handler(struct timer_list *t)
+{
+	struct garmin_data *garmin_data_p = from_timer(garmin_data_p, t, timer);
+
+	/* send the next queued packet to the tty port */
+	if (garmin_data_p->mode == MODE_NATIVE)
+		if (garmin_data_p->flags & FLAGS_QUEUING)
+			garmin_flush_queue(garmin_data_p);
+}
+
+
+
+static int garmin_port_probe(struct usb_serial_port *port)
+{
+	int status;
+	struct garmin_data *garmin_data_p;
+
+	garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+	if (!garmin_data_p)
+		return -ENOMEM;
+
+	timer_setup(&garmin_data_p->timer, timeout_handler, 0);
+	spin_lock_init(&garmin_data_p->lock);
+	INIT_LIST_HEAD(&garmin_data_p->pktlist);
+	garmin_data_p->port = port;
+	garmin_data_p->state = 0;
+	garmin_data_p->flags = 0;
+	garmin_data_p->count = 0;
+	init_usb_anchor(&garmin_data_p->write_urbs);
+	usb_set_serial_port_data(port, garmin_data_p);
+
+	status = garmin_init_session(port);
+	if (status)
+		goto err_free;
+
+	return 0;
+err_free:
+	kfree(garmin_data_p);
+
+	return status;
+}
+
+
+static int garmin_port_remove(struct usb_serial_port *port)
+{
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
+	usb_kill_urb(port->interrupt_in_urb);
+	del_timer_sync(&garmin_data_p->timer);
+	kfree(garmin_data_p);
+	return 0;
+}
+
+
+/* All of the device info needed */
+static struct usb_serial_driver garmin_device = {
+	.driver = {
+		.owner       = THIS_MODULE,
+		.name        = "garmin_gps",
+	},
+	.description         = "Garmin GPS usb/tty",
+	.id_table            = id_table,
+	.num_ports           = 1,
+	.open                = garmin_open,
+	.close               = garmin_close,
+	.throttle            = garmin_throttle,
+	.unthrottle          = garmin_unthrottle,
+	.port_probe		= garmin_port_probe,
+	.port_remove		= garmin_port_remove,
+	.write               = garmin_write,
+	.write_room          = garmin_write_room,
+	.write_bulk_callback = garmin_write_bulk_callback,
+	.read_bulk_callback  = garmin_read_bulk_callback,
+	.read_int_callback   = garmin_read_int_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&garmin_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(initial_mode, int, S_IRUGO);
+MODULE_PARM_DESC(initial_mode, "Initial mode");
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
new file mode 100644
index 0000000..2274d96
--- /dev/null
+++ b/drivers/usb/serial/generic.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Serial Converter Generic functions
+ *
+ * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com)
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+#include <linux/serial.h>
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+
+static __u16 vendor  = 0x05f9;
+static __u16 product = 0xffff;
+
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
+
+static int usb_serial_generic_probe(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	struct device *dev = &serial->interface->dev;
+
+	dev_info(dev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");
+	dev_info(dev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");
+
+	return 0;
+}
+
+static int usb_serial_generic_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	struct device *dev = &serial->interface->dev;
+	int num_ports;
+
+	num_ports = max(epds->num_bulk_in, epds->num_bulk_out);
+
+	if (num_ports == 0) {
+		dev_err(dev, "device has no bulk endpoints\n");
+		return -ENODEV;
+	}
+
+	return num_ports;
+}
+
+static struct usb_serial_driver usb_serial_generic_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"generic",
+	},
+	.id_table =		generic_device_ids,
+	.probe =		usb_serial_generic_probe,
+	.calc_num_ports =	usb_serial_generic_calc_num_ports,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.resume =		usb_serial_generic_resume,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&usb_serial_generic_device, NULL
+};
+
+#endif
+
+int usb_serial_generic_register(void)
+{
+	int retval = 0;
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	generic_device_ids[0].idVendor = vendor;
+	generic_device_ids[0].idProduct = product;
+	generic_device_ids[0].match_flags =
+		USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
+
+	retval = usb_serial_register_drivers(serial_drivers,
+			"usbserial_generic", generic_device_ids);
+#endif
+	return retval;
+}
+
+void usb_serial_generic_deregister(void)
+{
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	usb_serial_deregister_drivers(serial_drivers);
+#endif
+}
+
+int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	port->throttled = 0;
+	port->throttle_req = 0;
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (port->bulk_in_size)
+		result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
+
+	return result;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_open);
+
+void usb_serial_generic_close(struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int i;
+
+	if (port->bulk_out_size) {
+		for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+			usb_kill_urb(port->write_urbs[i]);
+
+		spin_lock_irqsave(&port->lock, flags);
+		kfifo_reset_out(&port->write_fifo);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+	if (port->bulk_in_size) {
+		for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+			usb_kill_urb(port->read_urbs[i]);
+	}
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_close);
+
+int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock);
+}
+
+/**
+ * usb_serial_generic_write_start - start writing buffered data
+ * @port: usb-serial port
+ * @mem_flags: flags to use for memory allocations
+ *
+ * Serialised using USB_SERIAL_WRITE_BUSY flag.
+ *
+ * Return: Zero on success or if busy, otherwise a negative errno value.
+ */
+int usb_serial_generic_write_start(struct usb_serial_port *port,
+							gfp_t mem_flags)
+{
+	struct urb *urb;
+	int count, result;
+	unsigned long flags;
+	int i;
+
+	if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
+		return 0;
+retry:
+	spin_lock_irqsave(&port->lock, flags);
+	if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
+		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
+		spin_unlock_irqrestore(&port->lock, flags);
+		return 0;
+	}
+	i = (int)find_first_bit(&port->write_urbs_free,
+						ARRAY_SIZE(port->write_urbs));
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	urb = port->write_urbs[i];
+	count = port->serial->type->prepare_write_buffer(port,
+						urb->transfer_buffer,
+						port->bulk_out_size);
+	urb->transfer_buffer_length = count;
+	usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer);
+	spin_lock_irqsave(&port->lock, flags);
+	port->tx_bytes += count;
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	clear_bit(i, &port->write_urbs_free);
+	result = usb_submit_urb(urb, mem_flags);
+	if (result) {
+		dev_err_console(port, "%s - error submitting urb: %d\n",
+						__func__, result);
+		set_bit(i, &port->write_urbs_free);
+		spin_lock_irqsave(&port->lock, flags);
+		port->tx_bytes -= count;
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
+		return result;
+	}
+
+	goto retry;	/* try sending off another urb */
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
+
+/**
+ * usb_serial_generic_write - generic write function
+ * @tty: tty for the port
+ * @port: usb-serial port
+ * @buf: data to write
+ * @count: number of bytes to write
+ *
+ * Return: The number of characters buffered, which may be anything from
+ * zero to @count, or a negative errno value.
+ */
+int usb_serial_generic_write(struct tty_struct *tty,
+	struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+	int result;
+
+	if (!port->bulk_out_size)
+		return -ENODEV;
+
+	if (!count)
+		return 0;
+
+	count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
+	result = usb_serial_generic_write_start(port, GFP_ATOMIC);
+	if (result)
+		return result;
+
+	return count;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_write);
+
+int usb_serial_generic_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned long flags;
+	int room;
+
+	if (!port->bulk_out_size)
+		return 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+	room = kfifo_avail(&port->write_fifo);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned long flags;
+	int chars;
+
+	if (!port->bulk_out_size)
+		return 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+	chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
+
+void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int bps;
+	unsigned long period;
+	unsigned long expire;
+
+	bps = tty_get_baud_rate(tty);
+	if (!bps)
+		bps = 9600;	/* B0 */
+	/*
+	 * Use a poll-period of roughly the time it takes to send one
+	 * character or at least one jiffy.
+	 */
+	period = max_t(unsigned long, (10 * HZ / bps), 1);
+	if (timeout)
+		period = min_t(unsigned long, period, timeout);
+
+	dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n",
+					__func__, jiffies_to_msecs(timeout),
+					jiffies_to_msecs(period));
+	expire = jiffies + timeout;
+	while (!port->serial->type->tx_empty(port)) {
+		schedule_timeout_interruptible(period);
+		if (signal_pending(current))
+			break;
+		if (timeout && time_after(jiffies, expire))
+			break;
+	}
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent);
+
+static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+						int index, gfp_t mem_flags)
+{
+	int res;
+
+	if (!test_and_clear_bit(index, &port->read_urbs_free))
+		return 0;
+
+	dev_dbg(&port->dev, "%s - urb %d\n", __func__, index);
+
+	res = usb_submit_urb(port->read_urbs[index], mem_flags);
+	if (res) {
+		if (res != -EPERM && res != -ENODEV) {
+			dev_err(&port->dev,
+					"%s - usb_submit_urb failed: %d\n",
+					__func__, res);
+		}
+		set_bit(index, &port->read_urbs_free);
+		return res;
+	}
+
+	return 0;
+}
+
+int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
+					gfp_t mem_flags)
+{
+	int res;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+		res = usb_serial_generic_submit_read_urb(port, i, mem_flags);
+		if (res)
+			goto err;
+	}
+
+	return 0;
+err:
+	for (; i >= 0; --i)
+		usb_kill_urb(port->read_urbs[i]);
+
+	return res;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs);
+
+void usb_serial_generic_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	char *ch = (char *)urb->transfer_buffer;
+	int i;
+
+	if (!urb->actual_length)
+		return;
+	/*
+	 * The per character mucking around with sysrq path it too slow for
+	 * stuff like 3G modems, so shortcircuit it in the 99.9999999% of
+	 * cases where the USB serial is not a console anyway.
+	 */
+	if (!port->port.console || !port->sysrq) {
+		tty_insert_flip_string(&port->port, ch, urb->actual_length);
+	} else {
+		for (i = 0; i < urb->actual_length; i++, ch++) {
+			if (!usb_serial_handle_sysrq_char(port, *ch))
+				tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
+		}
+	}
+	tty_flip_buffer_push(&port->port);
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb);
+
+void usb_serial_generic_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	int status = urb->status;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+		if (urb == port->read_urbs[i])
+			break;
+	}
+	set_bit(i, &port->read_urbs_free);
+
+	dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
+							urb->actual_length);
+	switch (status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+							__func__, status);
+		return;
+	case -EPIPE:
+		dev_err(&port->dev, "%s - urb stopped: %d\n",
+							__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+							__func__, status);
+		goto resubmit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+	port->serial->type->process_read_urb(urb);
+
+resubmit:
+	/* Throttle the device if requested by tty */
+	spin_lock_irqsave(&port->lock, flags);
+	port->throttled = port->throttle_req;
+	if (!port->throttled) {
+		spin_unlock_irqrestore(&port->lock, flags);
+		usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
+	} else {
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
+
+void usb_serial_generic_write_bulk_callback(struct urb *urb)
+{
+	unsigned long flags;
+	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
+		if (port->write_urbs[i] == urb)
+			break;
+	}
+	spin_lock_irqsave(&port->lock, flags);
+	port->tx_bytes -= urb->transfer_buffer_length;
+	set_bit(i, &port->write_urbs_free);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	switch (status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+							__func__, status);
+		return;
+	case -EPIPE:
+		dev_err_console(port, "%s - urb stopped: %d\n",
+							__func__, status);
+		return;
+	default:
+		dev_err_console(port, "%s - nonzero urb status: %d\n",
+							__func__, status);
+		goto resubmit;
+	}
+
+resubmit:
+	usb_serial_generic_write_start(port, GFP_ATOMIC);
+	usb_serial_port_softint(port);
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
+
+void usb_serial_generic_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	port->throttle_req = 1;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_throttle);
+
+void usb_serial_generic_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int was_throttled;
+
+	spin_lock_irq(&port->lock);
+	was_throttled = port->throttled;
+	port->throttled = port->throttle_req = 0;
+	spin_unlock_irq(&port->lock);
+
+	if (was_throttled)
+		usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
+
+static bool usb_serial_generic_msr_changed(struct tty_struct *tty,
+				unsigned long arg, struct async_icount *cprev)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct async_icount cnow;
+	unsigned long flags;
+	bool ret;
+
+	/*
+	 * Use tty-port initialised flag to detect all hangups including the
+	 * one generated at USB-device disconnect.
+	 */
+	if (!tty_port_initialized(&port->port))
+		return true;
+
+	spin_lock_irqsave(&port->lock, flags);
+	cnow = port->icount;				/* atomic copy*/
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	ret =	((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
+		((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
+		((arg & TIOCM_CD)  && (cnow.dcd != cprev->dcd)) ||
+		((arg & TIOCM_CTS) && (cnow.cts != cprev->cts));
+
+	*cprev = cnow;
+
+	return ret;
+}
+
+int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct async_icount cnow;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	cnow = port->icount;				/* atomic copy */
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	ret = wait_event_interruptible(port->port.delta_msr_wait,
+			usb_serial_generic_msr_changed(tty, arg, &cnow));
+	if (!ret && !tty_port_initialized(&port->port))
+		ret = -EIO;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait);
+
+int usb_serial_generic_get_icount(struct tty_struct *tty,
+					struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct async_icount cnow;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	cnow = port->icount;				/* atomic copy */
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->tx = cnow.tx;
+	icount->rx = cnow.rx;
+	icount->frame = cnow.frame;
+	icount->parity = cnow.parity;
+	icount->overrun = cnow.overrun;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount);
+
+#ifdef CONFIG_MAGIC_SYSRQ
+int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
+{
+	if (port->sysrq && port->port.console) {
+		if (ch && time_before(jiffies, port->sysrq)) {
+			handle_sysrq(ch);
+			port->sysrq = 0;
+			return 1;
+		}
+		port->sysrq = 0;
+	}
+	return 0;
+}
+#else
+int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
+{
+	return 0;
+}
+#endif
+EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
+
+int usb_serial_handle_break(struct usb_serial_port *port)
+{
+	if (!port->sysrq) {
+		port->sysrq = jiffies + HZ*5;
+		return 1;
+	}
+	port->sysrq = 0;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_serial_handle_break);
+
+/**
+ * usb_serial_handle_dcd_change - handle a change of carrier detect state
+ * @port: usb-serial port
+ * @tty: tty for the port
+ * @status: new carrier detect status, nonzero if active
+ */
+void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
+				struct tty_struct *tty, unsigned int status)
+{
+	struct tty_port *port = &usb_port->port;
+
+	dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
+
+	if (tty) {
+		struct tty_ldisc *ld = tty_ldisc_ref(tty);
+
+		if (ld) {
+			if (ld->ops->dcd_change)
+				ld->ops->dcd_change(tty, status);
+			tty_ldisc_deref(ld);
+		}
+	}
+
+	if (status)
+		wake_up_interruptible(&port->open_wait);
+	else if (tty && !C_CLOCAL(tty))
+		tty_hangup(tty);
+}
+EXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change);
+
+int usb_serial_generic_resume(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	int i, c = 0, r;
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		if (!tty_port_initialized(&port->port))
+			continue;
+
+		if (port->bulk_in_size) {
+			r = usb_serial_generic_submit_read_urbs(port,
+								GFP_NOIO);
+			if (r < 0)
+				c++;
+		}
+
+		if (port->bulk_out_size) {
+			r = usb_serial_generic_write_start(port, GFP_NOIO);
+			if (r < 0)
+				c++;
+		}
+	}
+
+	return c ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
diff --git a/drivers/usb/serial/io_16654.h b/drivers/usb/serial/io_16654.h
new file mode 100644
index 0000000..4980f72
--- /dev/null
+++ b/drivers/usb/serial/io_16654.h
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+/************************************************************************
+ *
+ *	16654.H		Definitions for 16C654 UART used on EdgePorts
+ *
+ *	Copyright (C) 1998 Inside Out Networks, Inc.
+ *
+ ************************************************************************/
+
+#if !defined(_16654_H)
+#define	_16654_H
+
+/************************************************************************
+ *
+ *			D e f i n e s   /   T y p e d e f s
+ *
+ ************************************************************************/
+
+	//
+	// UART register numbers
+	// Numbers 0-7 are passed to the Edgeport directly. Numbers 8 and
+	// above are used internally to indicate that we must enable access
+	// to them via LCR bit 0x80 or LCR = 0xBF.
+	// The register number sent to the Edgeport is then (x & 0x7).
+	//
+	// Driver must not access registers that affect operation of the
+	// the EdgePort firmware -- that includes THR, RHR, IER, FCR.
+
+
+#define THR			0	// ! Transmit Holding Register (Write)
+#define RDR			0	// ! Receive Holding Register (Read)
+#define IER			1	// ! Interrupt Enable Register
+#define FCR			2	// ! Fifo Control Register (Write)
+#define ISR			2	// Interrupt Status Register (Read)
+#define LCR			3	// Line Control Register
+#define MCR			4	// Modem Control Register
+#define LSR			5	// Line Status Register
+#define MSR			6	// Modem Status Register
+#define SPR			7	// ScratchPad Register
+#define DLL			8	// Bank2[ 0 ] Divisor Latch LSB
+#define DLM			9	// Bank2[ 1 ] Divisor Latch MSB
+#define EFR			10	// Bank2[ 2 ] Extended Function Register
+//efine unused			11	// Bank2[ 3 ]
+#define XON1			12	// Bank2[ 4 ] Xon-1
+#define XON2			13	// Bank2[ 5 ] Xon-2
+#define XOFF1			14	// Bank2[ 6 ] Xoff-1
+#define XOFF2			15	// Bank2[ 7 ] Xoff-2
+
+#define	NUM_16654_REGS		16
+
+#define IS_REG_2ND_BANK(x)	((x) >= 8)
+
+	//
+	// Bit definitions for each register
+	//
+
+#define IER_RX			0x01	// Enable receive interrupt
+#define IER_TX			0x02	// Enable transmit interrupt
+#define IER_RXS			0x04	// Enable receive status interrupt
+#define IER_MDM			0x08	// Enable modem status interrupt
+#define IER_SLEEP		0x10	// Enable sleep mode
+#define IER_XOFF		0x20	// Enable s/w flow control (XOFF) interrupt
+#define IER_RTS			0x40	// Enable RTS interrupt
+#define IER_CTS			0x80	// Enable CTS interrupt
+#define IER_ENABLE_ALL		0xFF	// Enable all ints
+
+
+#define FCR_FIFO_EN		0x01	// Enable FIFOs
+#define FCR_RXCLR		0x02	// Reset Rx FIFO
+#define FCR_TXCLR		0x04	// Reset Tx FIFO
+#define FCR_DMA_BLK		0x08	// Enable DMA block mode
+#define FCR_TX_LEVEL_MASK	0x30	// Mask for Tx FIFO Level
+#define FCR_TX_LEVEL_8		0x00	// Tx FIFO Level =  8 bytes
+#define FCR_TX_LEVEL_16		0x10	// Tx FIFO Level = 16 bytes
+#define FCR_TX_LEVEL_32		0x20	// Tx FIFO Level = 32 bytes
+#define FCR_TX_LEVEL_56		0x30	// Tx FIFO Level = 56 bytes
+#define FCR_RX_LEVEL_MASK	0xC0	// Mask for Rx FIFO Level
+#define FCR_RX_LEVEL_8		0x00	// Rx FIFO Level =  8 bytes
+#define FCR_RX_LEVEL_16		0x40	// Rx FIFO Level = 16 bytes
+#define FCR_RX_LEVEL_56		0x80	// Rx FIFO Level = 56 bytes
+#define FCR_RX_LEVEL_60		0xC0	// Rx FIFO Level = 60 bytes
+
+
+#define ISR_INT_MDM_STATUS	0x00	// Modem status int pending
+#define ISR_INT_NONE		0x01	// No interrupt pending
+#define ISR_INT_TXRDY		0x02	// Tx ready int pending
+#define ISR_INT_RXRDY		0x04	// Rx ready int pending
+#define ISR_INT_LINE_STATUS	0x06	// Line status int pending
+#define ISR_INT_RX_TIMEOUT	0x0C	// Rx timeout int pending
+#define ISR_INT_RX_XOFF		0x10	// Rx Xoff int pending
+#define ISR_INT_RTS_CTS		0x20	// RTS/CTS change int pending
+#define ISR_FIFO_ENABLED	0xC0	// Bits set if FIFOs enabled
+#define ISR_INT_BITS_MASK	0x3E	// Mask to isolate valid int causes
+
+
+#define LCR_BITS_5		0x00	// 5 bits/char
+#define LCR_BITS_6		0x01	// 6 bits/char
+#define LCR_BITS_7		0x02	// 7 bits/char
+#define LCR_BITS_8		0x03	// 8 bits/char
+#define LCR_BITS_MASK		0x03	// Mask for bits/char field
+
+#define LCR_STOP_1		0x00	// 1 stop bit
+#define LCR_STOP_1_5		0x04	// 1.5 stop bits (if 5   bits/char)
+#define LCR_STOP_2		0x04	// 2 stop bits   (if 6-8 bits/char)
+#define LCR_STOP_MASK		0x04	// Mask for stop bits field
+
+#define LCR_PAR_NONE		0x00	// No parity
+#define LCR_PAR_ODD		0x08	// Odd parity
+#define LCR_PAR_EVEN		0x18	// Even parity
+#define LCR_PAR_MARK		0x28	// Force parity bit to 1
+#define LCR_PAR_SPACE		0x38	// Force parity bit to 0
+#define LCR_PAR_MASK		0x38	// Mask for parity field
+
+#define LCR_SET_BREAK		0x40	// Set Break condition
+#define LCR_DL_ENABLE		0x80	// Enable access to divisor latch
+
+#define LCR_ACCESS_EFR		0xBF	// Load this value to access DLL,DLM,
+					// and also the '654-only registers
+					// EFR, XON1, XON2, XOFF1, XOFF2
+
+
+#define MCR_DTR			0x01	// Assert DTR
+#define MCR_RTS			0x02	// Assert RTS
+#define MCR_OUT1		0x04	// Loopback only: Sets state of RI
+#define MCR_MASTER_IE		0x08	// Enable interrupt outputs
+#define MCR_LOOPBACK		0x10	// Set internal (digital) loopback mode
+#define MCR_XON_ANY		0x20	// Enable any char to exit XOFF mode
+#define MCR_IR_ENABLE		0x40	// Enable IrDA functions
+#define MCR_BRG_DIV_4		0x80	// Divide baud rate clk by /4 instead of /1
+
+
+#define LSR_RX_AVAIL		0x01	// Rx data available
+#define LSR_OVER_ERR		0x02	// Rx overrun
+#define LSR_PAR_ERR		0x04	// Rx parity error
+#define LSR_FRM_ERR		0x08	// Rx framing error
+#define LSR_BREAK		0x10	// Rx break condition detected
+#define LSR_TX_EMPTY		0x20	// Tx Fifo empty
+#define LSR_TX_ALL_EMPTY	0x40	// Tx Fifo and shift register empty
+#define LSR_FIFO_ERR		0x80	// Rx Fifo contains at least 1 erred char
+
+
+#define EDGEPORT_MSR_DELTA_CTS	0x01	// CTS changed from last read
+#define EDGEPORT_MSR_DELTA_DSR	0x02	// DSR changed from last read
+#define EDGEPORT_MSR_DELTA_RI	0x04	// RI  changed from 0 -> 1
+#define EDGEPORT_MSR_DELTA_CD	0x08	// CD  changed from last read
+#define EDGEPORT_MSR_CTS	0x10	// Current state of CTS
+#define EDGEPORT_MSR_DSR	0x20	// Current state of DSR
+#define EDGEPORT_MSR_RI		0x40	// Current state of RI
+#define EDGEPORT_MSR_CD		0x80	// Current state of CD
+
+
+
+					//	Tx		Rx
+					//-------------------------------
+#define EFR_SWFC_NONE		0x00	//	None		None
+#define EFR_SWFC_RX1		0x02 	//	None		XOFF1
+#define EFR_SWFC_RX2		0x01 	//	None		XOFF2
+#define EFR_SWFC_RX12		0x03 	//	None		XOFF1 & XOFF2
+#define EFR_SWFC_TX1		0x08 	//	XOFF1		None
+#define EFR_SWFC_TX1_RX1	0x0a 	//	XOFF1		XOFF1
+#define EFR_SWFC_TX1_RX2	0x09 	//	XOFF1		XOFF2
+#define EFR_SWFC_TX1_RX12	0x0b 	//	XOFF1		XOFF1 & XOFF2
+#define EFR_SWFC_TX2		0x04 	//	XOFF2		None
+#define EFR_SWFC_TX2_RX1	0x06 	//	XOFF2		XOFF1
+#define EFR_SWFC_TX2_RX2	0x05 	//	XOFF2		XOFF2
+#define EFR_SWFC_TX2_RX12	0x07 	//	XOFF2		XOFF1 & XOFF2
+#define EFR_SWFC_TX12		0x0c 	//	XOFF1 & XOFF2	None
+#define EFR_SWFC_TX12_RX1	0x0e 	//	XOFF1 & XOFF2	XOFF1
+#define EFR_SWFC_TX12_RX2	0x0d 	//	XOFF1 & XOFF2	XOFF2
+#define EFR_SWFC_TX12_RX12	0x0f 	//	XOFF1 & XOFF2	XOFF1 & XOFF2
+
+#define EFR_TX_FC_MASK		0x0c	// Mask to isolate Rx flow control
+#define EFR_TX_FC_NONE		0x00	// No Tx Xon/Xoff flow control
+#define EFR_TX_FC_X1		0x08	// Transmit Xon1/Xoff1
+#define EFR_TX_FC_X2		0x04	// Transmit Xon2/Xoff2
+#define EFR_TX_FC_X1_2		0x0c	// Transmit Xon1&2/Xoff1&2
+
+#define EFR_RX_FC_MASK		0x03	// Mask to isolate Rx flow control
+#define EFR_RX_FC_NONE		0x00	// No Rx Xon/Xoff flow control
+#define EFR_RX_FC_X1		0x02	// Receiver compares Xon1/Xoff1
+#define EFR_RX_FC_X2		0x01	// Receiver compares Xon2/Xoff2
+#define EFR_RX_FC_X1_2		0x03	// Receiver compares Xon1&2/Xoff1&2
+
+
+#define EFR_SWFC_MASK		0x0F	// Mask for software flow control field
+#define EFR_ENABLE_16654	0x10	// Enable 16C654 features
+#define EFR_SPEC_DETECT		0x20	// Enable special character detect interrupt
+#define EFR_AUTO_RTS		0x40	// Use RTS for Rx flow control
+#define EFR_AUTO_CTS		0x80	// Use CTS for Tx flow control
+
+#endif	// if !defined(_16654_H)
+
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
new file mode 100644
index 0000000..97c69d3
--- /dev/null
+++ b/drivers/usb/serial/io_edgeport.c
@@ -0,0 +1,3258 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Edgeport USB Serial Converter driver
+ *
+ * Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Supports the following devices:
+ *	Edgeport/4
+ *	Edgeport/4t
+ *	Edgeport/2
+ *	Edgeport/4i
+ *	Edgeport/2i
+ *	Edgeport/421
+ *	Edgeport/21
+ *	Rapidport/4
+ *	Edgeport/8
+ *	Edgeport/2D8
+ *	Edgeport/4D8
+ *	Edgeport/8i
+ *
+ * For questions or problems with this driver, contact Inside Out
+ * Networks technical support, or Peter Berger <pberger@brimson.com>,
+ * or Al Borchers <alborchers@steinerpoint.com>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/serial.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "io_edgeport.h"
+#include "io_ionsp.h"		/* info for the iosp messages */
+#include "io_16654.h"		/* 16654 UART defines */
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
+#define DRIVER_DESC "Edgeport USB Serial Driver"
+
+#define MAX_NAME_LEN		64
+
+#define OPEN_TIMEOUT		(5*HZ)		/* 5 seconds */
+
+static const struct usb_device_id edgeport_2port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
+	{ }
+};
+
+static const struct usb_device_id edgeport_4port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+	{ }
+};
+
+static const struct usb_device_id edgeport_8port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+	{ }
+};
+
+static const struct usb_device_id Epic_port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
+	{ }
+};
+
+/* Devices that this driver supports */
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_RAPIDPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4T) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_421) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_21) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2_DIN) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4_DIN) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+
+/* receive port state */
+enum RXSTATE {
+	EXPECT_HDR1 = 0,    /* Expect header byte 1 */
+	EXPECT_HDR2 = 1,    /* Expect header byte 2 */
+	EXPECT_DATA = 2,    /* Expect 'RxBytesRemaining' data */
+	EXPECT_HDR3 = 3,    /* Expect header byte 3 (for status hdrs only) */
+};
+
+
+/* Transmit Fifo
+ * This Transmit queue is an extension of the edgeport Rx buffer.
+ * The maximum amount of data buffered in both the edgeport
+ * Rx buffer (maxTxCredits) and this buffer will never exceed maxTxCredits.
+ */
+struct TxFifo {
+	unsigned int	head;	/* index to head pointer (write) */
+	unsigned int	tail;	/* index to tail pointer (read)  */
+	unsigned int	count;	/* Bytes in queue */
+	unsigned int	size;	/* Max size of queue (equal to Max number of TxCredits) */
+	unsigned char	*fifo;	/* allocated Buffer */
+};
+
+/* This structure holds all of the local port information */
+struct edgeport_port {
+	__u16			txCredits;		/* our current credits for this port */
+	__u16			maxTxCredits;		/* the max size of the port */
+
+	struct TxFifo		txfifo;			/* transmit fifo -- size will be maxTxCredits */
+	struct urb		*write_urb;		/* write URB for this port */
+	bool			write_in_progress;	/* 'true' while a write URB is outstanding */
+	spinlock_t		ep_lock;
+
+	__u8			shadowLCR;		/* last LCR value received */
+	__u8			shadowMCR;		/* last MCR value received */
+	__u8			shadowMSR;		/* last MSR value received */
+	__u8			shadowLSR;		/* last LSR value received */
+	__u8			shadowXonChar;		/* last value set as XON char in Edgeport */
+	__u8			shadowXoffChar;		/* last value set as XOFF char in Edgeport */
+	__u8			validDataMask;
+	__u32			baudRate;
+
+	bool			open;
+	bool			openPending;
+	bool			commandPending;
+	bool			closePending;
+	bool			chaseResponsePending;
+
+	wait_queue_head_t	wait_chase;		/* for handling sleeping while waiting for chase to finish */
+	wait_queue_head_t	wait_open;		/* for handling sleeping while waiting for open to finish */
+	wait_queue_head_t	wait_command;		/* for handling sleeping while waiting for command to finish */
+
+	struct usb_serial_port	*port;			/* loop back to the owner of this object */
+};
+
+
+/* This structure holds all of the individual device information */
+struct edgeport_serial {
+	char			name[MAX_NAME_LEN+2];		/* string name of this device */
+
+	struct edge_manuf_descriptor	manuf_descriptor;	/* the manufacturer descriptor */
+	struct edge_boot_descriptor	boot_descriptor;	/* the boot firmware descriptor */
+	struct edgeport_product_info	product_info;		/* Product Info */
+	struct edge_compatibility_descriptor epic_descriptor;	/* Edgeport compatible descriptor */
+	int			is_epic;			/* flag if EPiC device or not */
+
+	__u8			interrupt_in_endpoint;		/* the interrupt endpoint handle */
+	unsigned char		*interrupt_in_buffer;		/* the buffer we use for the interrupt endpoint */
+	struct urb		*interrupt_read_urb;		/* our interrupt urb */
+
+	__u8			bulk_in_endpoint;		/* the bulk in endpoint handle */
+	unsigned char		*bulk_in_buffer;		/* the buffer we use for the bulk in endpoint */
+	struct urb		*read_urb;			/* our bulk read urb */
+	bool			read_in_progress;
+	spinlock_t		es_lock;
+
+	__u8			bulk_out_endpoint;		/* the bulk out endpoint handle */
+
+	__s16			rxBytesAvail;			/* the number of bytes that we need to read from this device */
+
+	enum RXSTATE		rxState;			/* the current state of the bulk receive processor */
+	__u8			rxHeader1;			/* receive header byte 1 */
+	__u8			rxHeader2;			/* receive header byte 2 */
+	__u8			rxHeader3;			/* receive header byte 3 */
+	__u8			rxPort;				/* the port that we are currently receiving data for */
+	__u8			rxStatusCode;			/* the receive status code */
+	__u8			rxStatusParam;			/* the receive status paramater */
+	__s16			rxBytesRemaining;		/* the number of port bytes left to read */
+	struct usb_serial	*serial;			/* loop back to the owner of this object */
+};
+
+/* baud rate information */
+struct divisor_table_entry {
+	__u32   BaudRate;
+	__u16  Divisor;
+};
+
+/*
+ * Define table of divisors for Rev A EdgePort/4 hardware
+ * These assume a 3.6864MHz crystal, the standard /16, and
+ * MCR.7 = 0.
+ */
+
+static const struct divisor_table_entry divisor_table[] = {
+	{   50,		4608},
+	{   75,		3072},
+	{   110,	2095},	/* 2094.545455 => 230450   => .0217 % over */
+	{   134,	1713},	/* 1713.011152 => 230398.5 => .00065% under */
+	{   150,	1536},
+	{   300,	768},
+	{   600,	384},
+	{   1200,	192},
+	{   1800,	128},
+	{   2400,	96},
+	{   4800,	48},
+	{   7200,	32},
+	{   9600,	24},
+	{   14400,	16},
+	{   19200,	12},
+	{   38400,	6},
+	{   57600,	4},
+	{   115200,	2},
+	{   230400,	1},
+};
+
+/* Number of outstanding Command Write Urbs */
+static atomic_t CmdUrbs = ATOMIC_INIT(0);
+
+
+/* local function prototypes */
+
+/* function prototypes for all URB callbacks */
+static void edge_interrupt_callback(struct urb *urb);
+static void edge_bulk_in_callback(struct urb *urb);
+static void edge_bulk_out_data_callback(struct urb *urb);
+static void edge_bulk_out_cmd_callback(struct urb *urb);
+
+/* function prototypes for the usbserial callbacks */
+static int edge_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void edge_close(struct usb_serial_port *port);
+static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *buf, int count);
+static int edge_write_room(struct tty_struct *tty);
+static int edge_chars_in_buffer(struct tty_struct *tty);
+static void edge_throttle(struct tty_struct *tty);
+static void edge_unthrottle(struct tty_struct *tty);
+static void edge_set_termios(struct tty_struct *tty,
+					struct usb_serial_port *port,
+					struct ktermios *old_termios);
+static int  edge_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg);
+static void edge_break(struct tty_struct *tty, int break_state);
+static int  edge_tiocmget(struct tty_struct *tty);
+static int  edge_tiocmset(struct tty_struct *tty,
+					unsigned int set, unsigned int clear);
+static int  edge_startup(struct usb_serial *serial);
+static void edge_disconnect(struct usb_serial *serial);
+static void edge_release(struct usb_serial *serial);
+static int edge_port_probe(struct usb_serial_port *port);
+static int edge_port_remove(struct usb_serial_port *port);
+
+/* function prototypes for all of our local functions */
+
+static void  process_rcvd_data(struct edgeport_serial *edge_serial,
+				unsigned char *buffer, __u16 bufferLength);
+static void process_rcvd_status(struct edgeport_serial *edge_serial,
+				__u8 byte2, __u8 byte3);
+static void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
+		int length);
+static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr);
+static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
+				__u8 lsr, __u8 data);
+static int  send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command,
+				__u8 param);
+static int  calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor);
+static int  send_cmd_write_baud_rate(struct edgeport_port *edge_port,
+				int baudRate);
+static void change_port_settings(struct tty_struct *tty,
+				struct edgeport_port *edge_port,
+				struct ktermios *old_termios);
+static int  send_cmd_write_uart_register(struct edgeport_port *edge_port,
+				__u8 regNum, __u8 regValue);
+static int  write_cmd_usb(struct edgeport_port *edge_port,
+				unsigned char *buffer, int writeLength);
+static void send_more_port_data(struct edgeport_serial *edge_serial,
+				struct edgeport_port *edge_port);
+
+static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
+					__u16 length, const __u8 *data);
+static int rom_read(struct usb_serial *serial, __u16 extAddr, __u16 addr,
+						__u16 length, __u8 *data);
+static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
+					__u16 length, const __u8 *data);
+static void get_manufacturing_desc(struct edgeport_serial *edge_serial);
+static void get_boot_desc(struct edgeport_serial *edge_serial);
+static void load_application_firmware(struct edgeport_serial *edge_serial);
+
+static void unicode_to_ascii(char *string, int buflen,
+				__le16 *unicode, int unicode_size);
+
+
+/* ************************************************************************ */
+/* ************************************************************************ */
+/* ************************************************************************ */
+/* ************************************************************************ */
+
+/************************************************************************
+ *									*
+ * update_edgeport_E2PROM()	Compare current versions of		*
+ *				Boot ROM and Manufacture 		*
+ *				Descriptors with versions		*
+ *				embedded in this driver			*
+ *									*
+ ************************************************************************/
+static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+	__u32 BootCurVer;
+	__u32 BootNewVer;
+	__u8 BootMajorVersion;
+	__u8 BootMinorVersion;
+	__u16 BootBuildNumber;
+	__u32 Bootaddr;
+	const struct ihex_binrec *rec;
+	const struct firmware *fw;
+	const char *fw_name;
+	int response;
+
+	switch (edge_serial->product_info.iDownloadFile) {
+	case EDGE_DOWNLOAD_FILE_I930:
+		fw_name	= "edgeport/boot.fw";
+		break;
+	case EDGE_DOWNLOAD_FILE_80251:
+		fw_name	= "edgeport/boot2.fw";
+		break;
+	default:
+		return;
+	}
+
+	response = request_ihex_firmware(&fw, fw_name,
+					 &edge_serial->serial->dev->dev);
+	if (response) {
+		dev_err(dev, "Failed to load image \"%s\" err %d\n",
+		       fw_name, response);
+		return;
+	}
+
+	rec = (const struct ihex_binrec *)fw->data;
+	BootMajorVersion = rec->data[0];
+	BootMinorVersion = rec->data[1];
+	BootBuildNumber = (rec->data[2] << 8) | rec->data[3];
+
+	/* Check Boot Image Version */
+	BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) +
+		     (edge_serial->boot_descriptor.MinorVersion << 16) +
+		      le16_to_cpu(edge_serial->boot_descriptor.BuildNumber);
+
+	BootNewVer = (BootMajorVersion << 24) +
+		     (BootMinorVersion << 16) +
+		      BootBuildNumber;
+
+	dev_dbg(dev, "Current Boot Image version %d.%d.%d\n",
+	    edge_serial->boot_descriptor.MajorVersion,
+	    edge_serial->boot_descriptor.MinorVersion,
+	    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
+
+
+	if (BootNewVer > BootCurVer) {
+		dev_dbg(dev, "**Update Boot Image from %d.%d.%d to %d.%d.%d\n",
+		    edge_serial->boot_descriptor.MajorVersion,
+		    edge_serial->boot_descriptor.MinorVersion,
+		    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
+		    BootMajorVersion, BootMinorVersion, BootBuildNumber);
+
+		dev_dbg(dev, "Downloading new Boot Image\n");
+
+		for (rec = ihex_next_binrec(rec); rec;
+		     rec = ihex_next_binrec(rec)) {
+			Bootaddr = be32_to_cpu(rec->addr);
+			response = rom_write(edge_serial->serial,
+					     Bootaddr >> 16,
+					     Bootaddr & 0xFFFF,
+					     be16_to_cpu(rec->len),
+					     &rec->data[0]);
+			if (response < 0) {
+				dev_err(&edge_serial->serial->dev->dev,
+					"rom_write failed (%x, %x, %d)\n",
+					Bootaddr >> 16, Bootaddr & 0xFFFF,
+					be16_to_cpu(rec->len));
+				break;
+			}
+		}
+	} else {
+		dev_dbg(dev, "Boot Image -- already up to date\n");
+	}
+	release_firmware(fw);
+}
+
+#if 0
+/************************************************************************
+ *
+ *  Get string descriptor from device
+ *
+ ************************************************************************/
+static int get_string_desc(struct usb_device *dev, int Id,
+				struct usb_string_descriptor **pRetDesc)
+{
+	struct usb_string_descriptor StringDesc;
+	struct usb_string_descriptor *pStringDesc;
+
+	dev_dbg(&dev->dev, "%s - USB String ID = %d\n", __func__, Id);
+
+	if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc,
+						sizeof(StringDesc)))
+		return 0;
+
+	pStringDesc = kmalloc(StringDesc.bLength, GFP_KERNEL);
+	if (!pStringDesc)
+		return -1;
+
+	if (!usb_get_descriptor(dev, USB_DT_STRING, Id, pStringDesc,
+							StringDesc.bLength)) {
+		kfree(pStringDesc);
+		return -1;
+	}
+
+	*pRetDesc = pStringDesc;
+	return 0;
+}
+#endif
+
+static void dump_product_info(struct edgeport_serial *edge_serial,
+			      struct edgeport_product_info *product_info)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+
+	/* Dump Product Info structure */
+	dev_dbg(dev, "**Product Information:\n");
+	dev_dbg(dev, "  ProductId             %x\n", product_info->ProductId);
+	dev_dbg(dev, "  NumPorts              %d\n", product_info->NumPorts);
+	dev_dbg(dev, "  ProdInfoVer           %d\n", product_info->ProdInfoVer);
+	dev_dbg(dev, "  IsServer              %d\n", product_info->IsServer);
+	dev_dbg(dev, "  IsRS232               %d\n", product_info->IsRS232);
+	dev_dbg(dev, "  IsRS422               %d\n", product_info->IsRS422);
+	dev_dbg(dev, "  IsRS485               %d\n", product_info->IsRS485);
+	dev_dbg(dev, "  RomSize               %d\n", product_info->RomSize);
+	dev_dbg(dev, "  RamSize               %d\n", product_info->RamSize);
+	dev_dbg(dev, "  CpuRev                %x\n", product_info->CpuRev);
+	dev_dbg(dev, "  BoardRev              %x\n", product_info->BoardRev);
+	dev_dbg(dev, "  BootMajorVersion      %d.%d.%d\n",
+		product_info->BootMajorVersion,
+		product_info->BootMinorVersion,
+		le16_to_cpu(product_info->BootBuildNumber));
+	dev_dbg(dev, "  FirmwareMajorVersion  %d.%d.%d\n",
+		product_info->FirmwareMajorVersion,
+		product_info->FirmwareMinorVersion,
+		le16_to_cpu(product_info->FirmwareBuildNumber));
+	dev_dbg(dev, "  ManufactureDescDate   %d/%d/%d\n",
+		product_info->ManufactureDescDate[0],
+		product_info->ManufactureDescDate[1],
+		product_info->ManufactureDescDate[2]+1900);
+	dev_dbg(dev, "  iDownloadFile         0x%x\n",
+		product_info->iDownloadFile);
+	dev_dbg(dev, "  EpicVer               %d\n", product_info->EpicVer);
+}
+
+static void get_product_info(struct edgeport_serial *edge_serial)
+{
+	struct edgeport_product_info *product_info = &edge_serial->product_info;
+
+	memset(product_info, 0, sizeof(struct edgeport_product_info));
+
+	product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP);
+	product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts;
+	product_info->ProdInfoVer = 0;
+
+	product_info->RomSize = edge_serial->manuf_descriptor.RomSize;
+	product_info->RamSize = edge_serial->manuf_descriptor.RamSize;
+	product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev;
+	product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev;
+
+	product_info->BootMajorVersion =
+				edge_serial->boot_descriptor.MajorVersion;
+	product_info->BootMinorVersion =
+				edge_serial->boot_descriptor.MinorVersion;
+	product_info->BootBuildNumber =
+				edge_serial->boot_descriptor.BuildNumber;
+
+	memcpy(product_info->ManufactureDescDate,
+			edge_serial->manuf_descriptor.DescDate,
+			sizeof(edge_serial->manuf_descriptor.DescDate));
+
+	/* check if this is 2nd generation hardware */
+	if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct)
+					    & ION_DEVICE_ID_80251_NETCHIP)
+		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251;
+	else
+		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930;
+
+	/* Determine Product type and set appropriate flags */
+	switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) {
+	case ION_DEVICE_ID_EDGEPORT_COMPATIBLE:
+	case ION_DEVICE_ID_EDGEPORT_4T:
+	case ION_DEVICE_ID_EDGEPORT_4:
+	case ION_DEVICE_ID_EDGEPORT_2:
+	case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU:
+	case ION_DEVICE_ID_EDGEPORT_8:
+	case ION_DEVICE_ID_EDGEPORT_421:
+	case ION_DEVICE_ID_EDGEPORT_21:
+	case ION_DEVICE_ID_EDGEPORT_2_DIN:
+	case ION_DEVICE_ID_EDGEPORT_4_DIN:
+	case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU:
+		product_info->IsRS232 = 1;
+		break;
+
+	case ION_DEVICE_ID_EDGEPORT_2I:	/* Edgeport/2 RS422/RS485 */
+		product_info->IsRS422 = 1;
+		product_info->IsRS485 = 1;
+		break;
+
+	case ION_DEVICE_ID_EDGEPORT_8I:	/* Edgeport/4 RS422 */
+	case ION_DEVICE_ID_EDGEPORT_4I:	/* Edgeport/4 RS422 */
+		product_info->IsRS422 = 1;
+		break;
+	}
+
+	dump_product_info(edge_serial, product_info);
+}
+
+static int get_epic_descriptor(struct edgeport_serial *ep)
+{
+	int result;
+	struct usb_serial *serial = ep->serial;
+	struct edgeport_product_info *product_info = &ep->product_info;
+	struct edge_compatibility_descriptor *epic;
+	struct edge_compatibility_bits *bits;
+	struct device *dev = &serial->dev->dev;
+
+	ep->is_epic = 0;
+
+	epic = kmalloc(sizeof(*epic), GFP_KERNEL);
+	if (!epic)
+		return -ENOMEM;
+
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				 USB_REQUEST_ION_GET_EPIC_DESC,
+				 0xC0, 0x00, 0x00,
+				 epic, sizeof(*epic),
+				 300);
+	if (result == sizeof(*epic)) {
+		ep->is_epic = 1;
+		memcpy(&ep->epic_descriptor, epic, sizeof(*epic));
+		memset(product_info, 0, sizeof(struct edgeport_product_info));
+
+		product_info->NumPorts = epic->NumPorts;
+		product_info->ProdInfoVer = 0;
+		product_info->FirmwareMajorVersion = epic->MajorVersion;
+		product_info->FirmwareMinorVersion = epic->MinorVersion;
+		product_info->FirmwareBuildNumber = epic->BuildNumber;
+		product_info->iDownloadFile = epic->iDownloadFile;
+		product_info->EpicVer = epic->EpicVer;
+		product_info->Epic = epic->Supports;
+		product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE;
+		dump_product_info(ep, product_info);
+
+		bits = &ep->epic_descriptor.Supports;
+		dev_dbg(dev, "**EPIC descriptor:\n");
+		dev_dbg(dev, "  VendEnableSuspend: %s\n", bits->VendEnableSuspend ? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPOpen         : %s\n", bits->IOSPOpen	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPClose        : %s\n", bits->IOSPClose	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPChase        : %s\n", bits->IOSPChase	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPSetRxFlow    : %s\n", bits->IOSPSetRxFlow	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPSetTxFlow    : %s\n", bits->IOSPSetTxFlow	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPSetXChar     : %s\n", bits->IOSPSetXChar	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPRxCheck      : %s\n", bits->IOSPRxCheck	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPSetClrBreak  : %s\n", bits->IOSPSetClrBreak	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPWriteMCR     : %s\n", bits->IOSPWriteMCR	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPWriteLCR     : %s\n", bits->IOSPWriteLCR	? "TRUE": "FALSE");
+		dev_dbg(dev, "  IOSPSetBaudRate  : %s\n", bits->IOSPSetBaudRate	? "TRUE": "FALSE");
+		dev_dbg(dev, "  TrueEdgeport     : %s\n", bits->TrueEdgeport	? "TRUE": "FALSE");
+
+		result = 0;
+	} else if (result >= 0) {
+		dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n",
+			 result);
+		result = -EIO;
+	}
+
+	kfree(epic);
+
+	return result;
+}
+
+
+/************************************************************************/
+/************************************************************************/
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/************************************************************************/
+/************************************************************************/
+
+/*****************************************************************************
+ * edge_interrupt_callback
+ *	this is the callback function for when we have received data on the
+ *	interrupt endpoint.
+ *****************************************************************************/
+static void edge_interrupt_callback(struct urb *urb)
+{
+	struct edgeport_serial *edge_serial = urb->context;
+	struct device *dev;
+	struct edgeport_port *edge_port;
+	struct usb_serial_port *port;
+	unsigned char *data = urb->transfer_buffer;
+	int length = urb->actual_length;
+	unsigned long flags;
+	int bytes_avail;
+	int position;
+	int txCredits;
+	int portNumber;
+	int result;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
+		goto exit;
+	}
+
+	dev = &edge_serial->serial->dev->dev;
+
+	/* process this interrupt-read even if there are no ports open */
+	if (length) {
+		usb_serial_debug_data(dev, __func__, length, data);
+
+		if (length > 1) {
+			bytes_avail = data[0] | (data[1] << 8);
+			if (bytes_avail) {
+				spin_lock_irqsave(&edge_serial->es_lock, flags);
+				edge_serial->rxBytesAvail += bytes_avail;
+				dev_dbg(dev,
+					"%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n",
+					__func__, bytes_avail,
+					edge_serial->rxBytesAvail,
+					edge_serial->read_in_progress);
+
+				if (edge_serial->rxBytesAvail > 0 &&
+				    !edge_serial->read_in_progress) {
+					dev_dbg(dev, "%s - posting a read\n", __func__);
+					edge_serial->read_in_progress = true;
+
+					/* we have pending bytes on the
+					   bulk in pipe, send a request */
+					result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
+					if (result) {
+						dev_err(dev,
+							"%s - usb_submit_urb(read bulk) failed with result = %d\n",
+							__func__, result);
+						edge_serial->read_in_progress = false;
+					}
+				}
+				spin_unlock_irqrestore(&edge_serial->es_lock,
+						       flags);
+			}
+		}
+		/* grab the txcredits for the ports if available */
+		position = 2;
+		portNumber = 0;
+		while ((position < length) &&
+				(portNumber < edge_serial->serial->num_ports)) {
+			txCredits = data[position] | (data[position+1] << 8);
+			if (txCredits) {
+				port = edge_serial->serial->port[portNumber];
+				edge_port = usb_get_serial_port_data(port);
+				if (edge_port->open) {
+					spin_lock_irqsave(&edge_port->ep_lock,
+							  flags);
+					edge_port->txCredits += txCredits;
+					spin_unlock_irqrestore(&edge_port->ep_lock,
+							       flags);
+					dev_dbg(dev, "%s - txcredits for port%d = %d\n",
+						__func__, portNumber,
+						edge_port->txCredits);
+
+					/* tell the tty driver that something
+					   has changed */
+					tty_port_tty_wakeup(&edge_port->port->port);
+					/* Since we have more credit, check
+					   if more data can be sent */
+					send_more_port_data(edge_serial,
+								edge_port);
+				}
+			}
+			position += 2;
+			++portNumber;
+		}
+	}
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting control urb\n",
+						__func__, result);
+}
+
+
+/*****************************************************************************
+ * edge_bulk_in_callback
+ *	this is the callback function for when we have received data on the
+ *	bulk in endpoint.
+ *****************************************************************************/
+static void edge_bulk_in_callback(struct urb *urb)
+{
+	struct edgeport_serial	*edge_serial = urb->context;
+	struct device *dev;
+	unsigned char		*data = urb->transfer_buffer;
+	int			retval;
+	__u16			raw_data_length;
+	int status = urb->status;
+	unsigned long flags;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
+			__func__, status);
+		edge_serial->read_in_progress = false;
+		return;
+	}
+
+	if (urb->actual_length == 0) {
+		dev_dbg(&urb->dev->dev, "%s - read bulk callback with no data\n", __func__);
+		edge_serial->read_in_progress = false;
+		return;
+	}
+
+	dev = &edge_serial->serial->dev->dev;
+	raw_data_length = urb->actual_length;
+
+	usb_serial_debug_data(dev, __func__, raw_data_length, data);
+
+	spin_lock_irqsave(&edge_serial->es_lock, flags);
+
+	/* decrement our rxBytes available by the number that we just got */
+	edge_serial->rxBytesAvail -= raw_data_length;
+
+	dev_dbg(dev, "%s - Received = %d, rxBytesAvail %d\n", __func__,
+		raw_data_length, edge_serial->rxBytesAvail);
+
+	process_rcvd_data(edge_serial, data, urb->actual_length);
+
+	/* check to see if there's any more data for us to read */
+	if (edge_serial->rxBytesAvail > 0) {
+		dev_dbg(dev, "%s - posting a read\n", __func__);
+		retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
+		if (retval) {
+			dev_err(dev,
+				"%s - usb_submit_urb(read bulk) failed, retval = %d\n",
+				__func__, retval);
+			edge_serial->read_in_progress = false;
+		}
+	} else {
+		edge_serial->read_in_progress = false;
+	}
+
+	spin_unlock_irqrestore(&edge_serial->es_lock, flags);
+}
+
+
+/*****************************************************************************
+ * edge_bulk_out_data_callback
+ *	this is the callback function for when we have finished sending
+ *	serial data on the bulk out endpoint.
+ *****************************************************************************/
+static void edge_bulk_out_data_callback(struct urb *urb)
+{
+	struct edgeport_port *edge_port = urb->context;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev,
+			"%s - nonzero write bulk status received: %d\n",
+			__func__, status);
+	}
+
+	if (edge_port->open)
+		tty_port_tty_wakeup(&edge_port->port->port);
+
+	/* Release the Write URB */
+	edge_port->write_in_progress = false;
+
+	/* Check if more data needs to be sent */
+	send_more_port_data((struct edgeport_serial *)
+		(usb_get_serial_data(edge_port->port->serial)), edge_port);
+}
+
+
+/*****************************************************************************
+ * BulkOutCmdCallback
+ *	this is the callback function for when we have finished sending a
+ *	command	on the bulk out endpoint.
+ *****************************************************************************/
+static void edge_bulk_out_cmd_callback(struct urb *urb)
+{
+	struct edgeport_port *edge_port = urb->context;
+	int status = urb->status;
+
+	atomic_dec(&CmdUrbs);
+	dev_dbg(&urb->dev->dev, "%s - FREE URB %p (outstanding %d)\n",
+		__func__, urb, atomic_read(&CmdUrbs));
+
+
+	/* clean up the transfer buffer */
+	kfree(urb->transfer_buffer);
+
+	/* Free the command urb */
+	usb_free_urb(urb);
+
+	if (status) {
+		dev_dbg(&urb->dev->dev,
+			"%s - nonzero write bulk status received: %d\n",
+			__func__, status);
+		return;
+	}
+
+	/* tell the tty driver that something has changed */
+	if (edge_port->open)
+		tty_port_tty_wakeup(&edge_port->port->port);
+
+	/* we have completed the command */
+	edge_port->commandPending = false;
+	wake_up(&edge_port->wait_command);
+}
+
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * SerialOpen
+ *	this function is called by the tty driver when a port is opened
+ *	If successful, we return 0
+ *	Otherwise we return a negative error number.
+ *****************************************************************************/
+static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	struct usb_serial *serial;
+	struct edgeport_serial *edge_serial;
+	int response;
+
+	if (edge_port == NULL)
+		return -ENODEV;
+
+	/* see if we've set up our endpoint info yet (can't set it up
+	   in edge_startup as the structures were not set up at that time.) */
+	serial = port->serial;
+	edge_serial = usb_get_serial_data(serial);
+	if (edge_serial == NULL)
+		return -ENODEV;
+	if (edge_serial->interrupt_in_buffer == NULL) {
+		struct usb_serial_port *port0 = serial->port[0];
+
+		/* not set up yet, so do it now */
+		edge_serial->interrupt_in_buffer =
+					port0->interrupt_in_buffer;
+		edge_serial->interrupt_in_endpoint =
+					port0->interrupt_in_endpointAddress;
+		edge_serial->interrupt_read_urb = port0->interrupt_in_urb;
+		edge_serial->bulk_in_buffer = port0->bulk_in_buffer;
+		edge_serial->bulk_in_endpoint =
+					port0->bulk_in_endpointAddress;
+		edge_serial->read_urb = port0->read_urb;
+		edge_serial->bulk_out_endpoint =
+					port0->bulk_out_endpointAddress;
+
+		/* set up our interrupt urb */
+		usb_fill_int_urb(edge_serial->interrupt_read_urb,
+		      serial->dev,
+		      usb_rcvintpipe(serial->dev,
+				port0->interrupt_in_endpointAddress),
+		      port0->interrupt_in_buffer,
+		      edge_serial->interrupt_read_urb->transfer_buffer_length,
+		      edge_interrupt_callback, edge_serial,
+		      edge_serial->interrupt_read_urb->interval);
+
+		/* set up our bulk in urb */
+		usb_fill_bulk_urb(edge_serial->read_urb, serial->dev,
+			usb_rcvbulkpipe(serial->dev,
+				port0->bulk_in_endpointAddress),
+			port0->bulk_in_buffer,
+			edge_serial->read_urb->transfer_buffer_length,
+			edge_bulk_in_callback, edge_serial);
+		edge_serial->read_in_progress = false;
+
+		/* start interrupt read for this edgeport
+		 * this interrupt will continue as long
+		 * as the edgeport is connected */
+		response = usb_submit_urb(edge_serial->interrupt_read_urb,
+								GFP_KERNEL);
+		if (response) {
+			dev_err(dev, "%s - Error %d submitting control urb\n",
+				__func__, response);
+		}
+	}
+
+	/* initialize our wait queues */
+	init_waitqueue_head(&edge_port->wait_open);
+	init_waitqueue_head(&edge_port->wait_chase);
+	init_waitqueue_head(&edge_port->wait_command);
+
+	/* initialize our port settings */
+	edge_port->txCredits = 0;	/* Can't send any data yet */
+	/* Must always set this bit to enable ints! */
+	edge_port->shadowMCR = MCR_MASTER_IE;
+	edge_port->chaseResponsePending = false;
+
+	/* send a open port command */
+	edge_port->openPending = true;
+	edge_port->open        = false;
+	response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
+
+	if (response < 0) {
+		dev_err(dev, "%s - error sending open port command\n", __func__);
+		edge_port->openPending = false;
+		return -ENODEV;
+	}
+
+	/* now wait for the port to be completely opened */
+	wait_event_timeout(edge_port->wait_open, !edge_port->openPending,
+								OPEN_TIMEOUT);
+
+	if (!edge_port->open) {
+		/* open timed out */
+		dev_dbg(dev, "%s - open timedout\n", __func__);
+		edge_port->openPending = false;
+		return -ENODEV;
+	}
+
+	/* create the txfifo */
+	edge_port->txfifo.head	= 0;
+	edge_port->txfifo.tail	= 0;
+	edge_port->txfifo.count	= 0;
+	edge_port->txfifo.size	= edge_port->maxTxCredits;
+	edge_port->txfifo.fifo	= kmalloc(edge_port->maxTxCredits, GFP_KERNEL);
+
+	if (!edge_port->txfifo.fifo) {
+		edge_close(port);
+		return -ENOMEM;
+	}
+
+	/* Allocate a URB for the write */
+	edge_port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+	edge_port->write_in_progress = false;
+
+	if (!edge_port->write_urb) {
+		edge_close(port);
+		return -ENOMEM;
+	}
+
+	dev_dbg(dev, "%s - Initialize TX fifo to %d bytes\n",
+		__func__, edge_port->maxTxCredits);
+
+	return 0;
+}
+
+
+/************************************************************************
+ *
+ * block_until_chase_response
+ *
+ *	This function will block the close until one of the following:
+ *		1. Response to our Chase comes from Edgeport
+ *		2. A timeout of 10 seconds without activity has expired
+ *		   (1K of Edgeport data @ 2400 baud ==> 4 sec to empty)
+ *
+ ************************************************************************/
+static void block_until_chase_response(struct edgeport_port *edge_port)
+{
+	struct device *dev = &edge_port->port->dev;
+	DEFINE_WAIT(wait);
+	__u16 lastCredits;
+	int timeout = 1*HZ;
+	int loop = 10;
+
+	while (1) {
+		/* Save Last credits */
+		lastCredits = edge_port->txCredits;
+
+		/* Did we get our Chase response */
+		if (!edge_port->chaseResponsePending) {
+			dev_dbg(dev, "%s - Got Chase Response\n", __func__);
+
+			/* did we get all of our credit back? */
+			if (edge_port->txCredits == edge_port->maxTxCredits) {
+				dev_dbg(dev, "%s - Got all credits\n", __func__);
+				return;
+			}
+		}
+
+		/* Block the thread for a while */
+		prepare_to_wait(&edge_port->wait_chase, &wait,
+						TASK_UNINTERRUPTIBLE);
+		schedule_timeout(timeout);
+		finish_wait(&edge_port->wait_chase, &wait);
+
+		if (lastCredits == edge_port->txCredits) {
+			/* No activity.. count down. */
+			loop--;
+			if (loop == 0) {
+				edge_port->chaseResponsePending = false;
+				dev_dbg(dev, "%s - Chase TIMEOUT\n", __func__);
+				return;
+			}
+		} else {
+			/* Reset timeout value back to 10 seconds */
+			dev_dbg(dev, "%s - Last %d, Current %d\n", __func__,
+					lastCredits, edge_port->txCredits);
+			loop = 10;
+		}
+	}
+}
+
+
+/************************************************************************
+ *
+ * block_until_tx_empty
+ *
+ *	This function will block the close until one of the following:
+ *		1. TX count are 0
+ *		2. The edgeport has stopped
+ *		3. A timeout of 3 seconds without activity has expired
+ *
+ ************************************************************************/
+static void block_until_tx_empty(struct edgeport_port *edge_port)
+{
+	struct device *dev = &edge_port->port->dev;
+	DEFINE_WAIT(wait);
+	struct TxFifo *fifo = &edge_port->txfifo;
+	__u32 lastCount;
+	int timeout = HZ/10;
+	int loop = 30;
+
+	while (1) {
+		/* Save Last count */
+		lastCount = fifo->count;
+
+		/* Is the Edgeport Buffer empty? */
+		if (lastCount == 0) {
+			dev_dbg(dev, "%s - TX Buffer Empty\n", __func__);
+			return;
+		}
+
+		/* Block the thread for a while */
+		prepare_to_wait(&edge_port->wait_chase, &wait,
+						TASK_UNINTERRUPTIBLE);
+		schedule_timeout(timeout);
+		finish_wait(&edge_port->wait_chase, &wait);
+
+		dev_dbg(dev, "%s wait\n", __func__);
+
+		if (lastCount == fifo->count) {
+			/* No activity.. count down. */
+			loop--;
+			if (loop == 0) {
+				dev_dbg(dev, "%s - TIMEOUT\n", __func__);
+				return;
+			}
+		} else {
+			/* Reset timeout value back to seconds */
+			loop = 30;
+		}
+	}
+}
+
+
+/*****************************************************************************
+ * edge_close
+ *	this function is called by the tty driver when a port is closed
+ *****************************************************************************/
+static void edge_close(struct usb_serial_port *port)
+{
+	struct edgeport_serial *edge_serial;
+	struct edgeport_port *edge_port;
+	int status;
+
+	edge_serial = usb_get_serial_data(port->serial);
+	edge_port = usb_get_serial_port_data(port);
+	if (edge_serial == NULL || edge_port == NULL)
+		return;
+
+	/* block until tx is empty */
+	block_until_tx_empty(edge_port);
+
+	edge_port->closePending = true;
+
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPChase) {
+		/* flush and chase */
+		edge_port->chaseResponsePending = true;
+
+		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
+		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
+		if (status == 0)
+			/* block until chase finished */
+			block_until_chase_response(edge_port);
+		else
+			edge_port->chaseResponsePending = false;
+	}
+
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPClose) {
+	       /* close the port */
+		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
+		send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
+	}
+
+	/* port->close = true; */
+	edge_port->closePending = false;
+	edge_port->open = false;
+	edge_port->openPending = false;
+
+	usb_kill_urb(edge_port->write_urb);
+
+	if (edge_port->write_urb) {
+		/* if this urb had a transfer buffer already
+				(old transfer) free it */
+		kfree(edge_port->write_urb->transfer_buffer);
+		usb_free_urb(edge_port->write_urb);
+		edge_port->write_urb = NULL;
+	}
+	kfree(edge_port->txfifo.fifo);
+	edge_port->txfifo.fifo = NULL;
+}
+
+/*****************************************************************************
+ * SerialWrite
+ *	this function is called by the tty driver when data should be written
+ *	to the port.
+ *	If successful, we return the number of bytes written, otherwise we
+ *	return a negative error number.
+ *****************************************************************************/
+static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *data, int count)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct TxFifo *fifo;
+	int copySize;
+	int bytesleft;
+	int firsthalf;
+	int secondhalf;
+	unsigned long flags;
+
+	if (edge_port == NULL)
+		return -ENODEV;
+
+	/* get a pointer to the Tx fifo */
+	fifo = &edge_port->txfifo;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	/* calculate number of bytes to put in fifo */
+	copySize = min((unsigned int)count,
+				(edge_port->txCredits - fifo->count));
+
+	dev_dbg(&port->dev, "%s of %d byte(s) Fifo room  %d -- will copy %d bytes\n",
+		__func__, count, edge_port->txCredits - fifo->count, copySize);
+
+	/* catch writes of 0 bytes which the tty driver likes to give us,
+	   and when txCredits is empty */
+	if (copySize == 0) {
+		dev_dbg(&port->dev, "%s - copySize = Zero\n", __func__);
+		goto finish_write;
+	}
+
+	/* queue the data
+	 * since we can never overflow the buffer we do not have to check for a
+	 * full condition
+	 *
+	 * the copy is done is two parts -- first fill to the end of the buffer
+	 * then copy the reset from the start of the buffer
+	 */
+	bytesleft = fifo->size - fifo->head;
+	firsthalf = min(bytesleft, copySize);
+	dev_dbg(&port->dev, "%s - copy %d bytes of %d into fifo \n", __func__,
+		firsthalf, bytesleft);
+
+	/* now copy our data */
+	memcpy(&fifo->fifo[fifo->head], data, firsthalf);
+	usb_serial_debug_data(&port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]);
+
+	/* update the index and size */
+	fifo->head  += firsthalf;
+	fifo->count += firsthalf;
+
+	/* wrap the index */
+	if (fifo->head == fifo->size)
+		fifo->head = 0;
+
+	secondhalf = copySize-firsthalf;
+
+	if (secondhalf) {
+		dev_dbg(&port->dev, "%s - copy rest of data %d\n", __func__, secondhalf);
+		memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
+		usb_serial_debug_data(&port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]);
+		/* update the index and size */
+		fifo->count += secondhalf;
+		fifo->head  += secondhalf;
+		/* No need to check for wrap since we can not get to end of
+		 * the fifo in this part
+		 */
+	}
+
+finish_write:
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	send_more_port_data((struct edgeport_serial *)
+			usb_get_serial_data(port->serial), edge_port);
+
+	dev_dbg(&port->dev, "%s wrote %d byte(s) TxCredits %d, Fifo %d\n",
+		__func__, copySize, edge_port->txCredits, fifo->count);
+
+	return copySize;
+}
+
+
+/************************************************************************
+ *
+ * send_more_port_data()
+ *
+ *	This routine attempts to write additional UART transmit data
+ *	to a port over the USB bulk pipe. It is called (1) when new
+ *	data has been written to a port's TxBuffer from higher layers
+ *	(2) when the peripheral sends us additional TxCredits indicating
+ *	that it can accept more	Tx data for a given port; and (3) when
+ *	a bulk write completes successfully and we want to see if we
+ *	can transmit more.
+ *
+ ************************************************************************/
+static void send_more_port_data(struct edgeport_serial *edge_serial,
+					struct edgeport_port *edge_port)
+{
+	struct TxFifo	*fifo = &edge_port->txfifo;
+	struct device	*dev = &edge_port->port->dev;
+	struct urb	*urb;
+	unsigned char	*buffer;
+	int		status;
+	int		count;
+	int		bytesleft;
+	int		firsthalf;
+	int		secondhalf;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	if (edge_port->write_in_progress ||
+	    !edge_port->open             ||
+	    (fifo->count == 0)) {
+		dev_dbg(dev, "%s EXIT - fifo %d, PendingWrite = %d\n",
+			__func__, fifo->count, edge_port->write_in_progress);
+		goto exit_send;
+	}
+
+	/* since the amount of data in the fifo will always fit into the
+	 * edgeport buffer we do not need to check the write length
+	 *
+	 * Do we have enough credits for this port to make it worthwhile
+	 * to bother queueing a write. If it's too small, say a few bytes,
+	 * it's better to wait for more credits so we can do a larger write.
+	 */
+	if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) {
+		dev_dbg(dev, "%s Not enough credit - fifo %d TxCredit %d\n",
+			__func__, fifo->count, edge_port->txCredits);
+		goto exit_send;
+	}
+
+	/* lock this write */
+	edge_port->write_in_progress = true;
+
+	/* get a pointer to the write_urb */
+	urb = edge_port->write_urb;
+
+	/* make sure transfer buffer is freed */
+	kfree(urb->transfer_buffer);
+	urb->transfer_buffer = NULL;
+
+	/* build the data header for the buffer and port that we are about
+	   to send out */
+	count = fifo->count;
+	buffer = kmalloc(count+2, GFP_ATOMIC);
+	if (!buffer) {
+		edge_port->write_in_progress = false;
+		goto exit_send;
+	}
+	buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->port_number, count);
+	buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->port_number, count);
+
+	/* now copy our data */
+	bytesleft =  fifo->size - fifo->tail;
+	firsthalf = min(bytesleft, count);
+	memcpy(&buffer[2], &fifo->fifo[fifo->tail], firsthalf);
+	fifo->tail  += firsthalf;
+	fifo->count -= firsthalf;
+	if (fifo->tail == fifo->size)
+		fifo->tail = 0;
+
+	secondhalf = count-firsthalf;
+	if (secondhalf) {
+		memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail],
+								secondhalf);
+		fifo->tail  += secondhalf;
+		fifo->count -= secondhalf;
+	}
+
+	if (count)
+		usb_serial_debug_data(&edge_port->port->dev, __func__, count, &buffer[2]);
+
+	/* fill up the urb with all of our data and submit it */
+	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
+			usb_sndbulkpipe(edge_serial->serial->dev,
+					edge_serial->bulk_out_endpoint),
+			buffer, count+2,
+			edge_bulk_out_data_callback, edge_port);
+
+	/* decrement the number of credits we have by the number we just sent */
+	edge_port->txCredits -= count;
+	edge_port->port->icount.tx += count;
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		/* something went wrong */
+		dev_err_console(edge_port->port,
+			"%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n",
+				__func__, status);
+		edge_port->write_in_progress = false;
+
+		/* revert the credits as something bad happened. */
+		edge_port->txCredits += count;
+		edge_port->port->icount.tx -= count;
+	}
+	dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n",
+		__func__, count, edge_port->txCredits, fifo->count);
+
+exit_send:
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+}
+
+
+/*****************************************************************************
+ * edge_write_room
+ *	this function is called by the tty driver when it wants to know how
+ *	many bytes of data we can accept for a specific port. If successful,
+ *	we return the amount of room that we have for this port	(the txCredits)
+ *	otherwise we return a negative error number.
+ *****************************************************************************/
+static int edge_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int room;
+	unsigned long flags;
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->closePending)
+		return 0;
+
+	if (!edge_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return 0;
+	}
+
+	/* total of both buffers is still txCredit */
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	room = edge_port->txCredits - edge_port->txfifo.count;
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+
+/*****************************************************************************
+ * edge_chars_in_buffer
+ *	this function is called by the tty driver when it wants to know how
+ *	many bytes of data we currently have outstanding in the port (data that
+ *	has been written, but hasn't made it out the port yet)
+ *	If successful, we return the number of bytes left to be written in the
+ *	system,
+ *	Otherwise we return a negative error number.
+ *****************************************************************************/
+static int edge_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int num_chars;
+	unsigned long flags;
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->closePending)
+		return 0;
+
+	if (!edge_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return 0;
+	}
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	num_chars = edge_port->maxTxCredits - edge_port->txCredits +
+						edge_port->txfifo.count;
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+	if (num_chars) {
+		dev_dbg(&port->dev, "%s - returns %d\n", __func__, num_chars);
+	}
+
+	return num_chars;
+}
+
+
+/*****************************************************************************
+ * SerialThrottle
+ *	this function is called by the tty driver when it wants to stop the data
+ *	being read from the port.
+ *****************************************************************************/
+static void edge_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status;
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the stop character */
+	if (I_IXOFF(tty)) {
+		unsigned char stop_char = STOP_CHAR(tty);
+		status = edge_write(tty, port, &stop_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		edge_port->shadowMCR &= ~MCR_RTS;
+		status = send_cmd_write_uart_register(edge_port, MCR,
+							edge_port->shadowMCR);
+		if (status != 0)
+			return;
+	}
+}
+
+
+/*****************************************************************************
+ * edge_unthrottle
+ *	this function is called by the tty driver when it wants to resume the
+ *	data being read from the port (called after SerialThrottle is called)
+ *****************************************************************************/
+static void edge_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status;
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the start character */
+	if (I_IXOFF(tty)) {
+		unsigned char start_char = START_CHAR(tty);
+		status = edge_write(tty, port, &start_char, 1);
+		if (status <= 0)
+			return;
+	}
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		edge_port->shadowMCR |= MCR_RTS;
+		send_cmd_write_uart_register(edge_port, MCR,
+						edge_port->shadowMCR);
+	}
+}
+
+
+/*****************************************************************************
+ * SerialSetTermios
+ *	this function is called by the tty driver when it wants to change
+ * the termios structure
+ *****************************************************************************/
+static void edge_set_termios(struct tty_struct *tty,
+	struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* change the port settings to the new ones specified */
+	change_port_settings(tty, edge_port, old_termios);
+}
+
+
+/*****************************************************************************
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ *****************************************************************************/
+static int get_lsr_info(struct edgeport_port *edge_port,
+						unsigned int __user *value)
+{
+	unsigned int result = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	if (edge_port->maxTxCredits == edge_port->txCredits &&
+	    edge_port->txfifo.count == 0) {
+		dev_dbg(&edge_port->port->dev, "%s -- Empty\n", __func__);
+		result = TIOCSER_TEMT;
+	}
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+static int edge_tiocmset(struct tty_struct *tty,
+					unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int mcr;
+
+	mcr = edge_port->shadowMCR;
+	if (set & TIOCM_RTS)
+		mcr |= MCR_RTS;
+	if (set & TIOCM_DTR)
+		mcr |= MCR_DTR;
+	if (set & TIOCM_LOOP)
+		mcr |= MCR_LOOPBACK;
+
+	if (clear & TIOCM_RTS)
+		mcr &= ~MCR_RTS;
+	if (clear & TIOCM_DTR)
+		mcr &= ~MCR_DTR;
+	if (clear & TIOCM_LOOP)
+		mcr &= ~MCR_LOOPBACK;
+
+	edge_port->shadowMCR = mcr;
+
+	send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR);
+
+	return 0;
+}
+
+static int edge_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int result = 0;
+	unsigned int msr;
+	unsigned int mcr;
+
+	msr = edge_port->shadowMSR;
+	mcr = edge_port->shadowMCR;
+	result = ((mcr & MCR_DTR)	? TIOCM_DTR: 0)	  /* 0x002 */
+		  | ((mcr & MCR_RTS)	? TIOCM_RTS: 0)   /* 0x004 */
+		  | ((msr & EDGEPORT_MSR_CTS)	? TIOCM_CTS: 0)   /* 0x020 */
+		  | ((msr & EDGEPORT_MSR_CD)	? TIOCM_CAR: 0)   /* 0x040 */
+		  | ((msr & EDGEPORT_MSR_RI)	? TIOCM_RI:  0)   /* 0x080 */
+		  | ((msr & EDGEPORT_MSR_DSR)	? TIOCM_DSR: 0);  /* 0x100 */
+
+	return result;
+}
+
+static int get_serial_info(struct edgeport_port *edge_port,
+				struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= edge_port->port->minor;
+	tmp.port		= edge_port->port->port_number;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= edge_port->maxTxCredits;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/*****************************************************************************
+ * SerialIoctl
+ *	this function handles any ioctl calls to the driver
+ *****************************************************************************/
+static int edge_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	DEFINE_WAIT(wait);
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	switch (cmd) {
+	case TIOCSERGETLSR:
+		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
+		return get_lsr_info(edge_port, (unsigned int __user *) arg);
+
+	case TIOCGSERIAL:
+		dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
+		return get_serial_info(edge_port, (struct serial_struct __user *) arg);
+	}
+	return -ENOIOCTLCMD;
+}
+
+
+/*****************************************************************************
+ * SerialBreak
+ *	this function sends a break to the port
+ *****************************************************************************/
+static void edge_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
+	int status;
+
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPChase) {
+		/* flush and chase */
+		edge_port->chaseResponsePending = true;
+
+		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
+		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
+		if (status == 0) {
+			/* block until chase finished */
+			block_until_chase_response(edge_port);
+		} else {
+			edge_port->chaseResponsePending = false;
+		}
+	}
+
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) {
+		if (break_state == -1) {
+			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
+			status = send_iosp_ext_cmd(edge_port,
+						IOSP_CMD_SET_BREAK, 0);
+		} else {
+			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLEAR_BREAK\n", __func__);
+			status = send_iosp_ext_cmd(edge_port,
+						IOSP_CMD_CLEAR_BREAK, 0);
+		}
+		if (status)
+			dev_dbg(&port->dev, "%s - error sending break set/clear command.\n",
+				__func__);
+	}
+}
+
+
+/*****************************************************************************
+ * process_rcvd_data
+ *	this function handles the data received on the bulk in pipe.
+ *****************************************************************************/
+static void process_rcvd_data(struct edgeport_serial *edge_serial,
+				unsigned char *buffer, __u16 bufferLength)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+	struct usb_serial_port *port;
+	struct edgeport_port *edge_port;
+	__u16 lastBufferLength;
+	__u16 rxLen;
+
+	lastBufferLength = bufferLength + 1;
+
+	while (bufferLength > 0) {
+		/* failsafe incase we get a message that we don't understand */
+		if (lastBufferLength == bufferLength) {
+			dev_dbg(dev, "%s - stuck in loop, exiting it.\n", __func__);
+			break;
+		}
+		lastBufferLength = bufferLength;
+
+		switch (edge_serial->rxState) {
+		case EXPECT_HDR1:
+			edge_serial->rxHeader1 = *buffer;
+			++buffer;
+			--bufferLength;
+
+			if (bufferLength == 0) {
+				edge_serial->rxState = EXPECT_HDR2;
+				break;
+			}
+			/* otherwise, drop on through */
+		case EXPECT_HDR2:
+			edge_serial->rxHeader2 = *buffer;
+			++buffer;
+			--bufferLength;
+
+			dev_dbg(dev, "%s - Hdr1=%02X Hdr2=%02X\n", __func__,
+				edge_serial->rxHeader1, edge_serial->rxHeader2);
+			/* Process depending on whether this header is
+			 * data or status */
+
+			if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) {
+				/* Decode this status header and go to
+				 * EXPECT_HDR1 (if we can process the status
+				 * with only 2 bytes), or go to EXPECT_HDR3 to
+				 * get the third byte. */
+				edge_serial->rxPort =
+				    IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
+				edge_serial->rxStatusCode =
+				    IOSP_GET_STATUS_CODE(
+						edge_serial->rxHeader1);
+
+				if (!IOSP_STATUS_IS_2BYTE(
+						edge_serial->rxStatusCode)) {
+					/* This status needs additional bytes.
+					 * Save what we have and then wait for
+					 * more data.
+					 */
+					edge_serial->rxStatusParam
+						= edge_serial->rxHeader2;
+					edge_serial->rxState = EXPECT_HDR3;
+					break;
+				}
+				/* We have all the header bytes, process the
+				   status now */
+				process_rcvd_status(edge_serial,
+						edge_serial->rxHeader2, 0);
+				edge_serial->rxState = EXPECT_HDR1;
+				break;
+			} else {
+				edge_serial->rxPort =
+				    IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
+				edge_serial->rxBytesRemaining =
+				    IOSP_GET_HDR_DATA_LEN(
+						edge_serial->rxHeader1,
+						edge_serial->rxHeader2);
+				dev_dbg(dev, "%s - Data for Port %u Len %u\n",
+					__func__,
+					edge_serial->rxPort,
+					edge_serial->rxBytesRemaining);
+
+				/* ASSERT(DevExt->RxPort < DevExt->NumPorts);
+				 * ASSERT(DevExt->RxBytesRemaining <
+				 *		IOSP_MAX_DATA_LENGTH);
+				 */
+
+				if (bufferLength == 0) {
+					edge_serial->rxState = EXPECT_DATA;
+					break;
+				}
+				/* Else, drop through */
+			}
+		case EXPECT_DATA: /* Expect data */
+			if (bufferLength < edge_serial->rxBytesRemaining) {
+				rxLen = bufferLength;
+				/* Expect data to start next buffer */
+				edge_serial->rxState = EXPECT_DATA;
+			} else {
+				/* BufLen >= RxBytesRemaining */
+				rxLen = edge_serial->rxBytesRemaining;
+				/* Start another header next time */
+				edge_serial->rxState = EXPECT_HDR1;
+			}
+
+			bufferLength -= rxLen;
+			edge_serial->rxBytesRemaining -= rxLen;
+
+			/* spit this data back into the tty driver if this
+			   port is open */
+			if (rxLen) {
+				port = edge_serial->serial->port[
+							edge_serial->rxPort];
+				edge_port = usb_get_serial_port_data(port);
+				if (edge_port->open) {
+					dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n",
+						__func__, rxLen,
+						edge_serial->rxPort);
+					edge_tty_recv(edge_port->port, buffer,
+							rxLen);
+					edge_port->port->icount.rx += rxLen;
+				}
+				buffer += rxLen;
+			}
+			break;
+
+		case EXPECT_HDR3:	/* Expect 3rd byte of status header */
+			edge_serial->rxHeader3 = *buffer;
+			++buffer;
+			--bufferLength;
+
+			/* We have all the header bytes, process the
+			   status now */
+			process_rcvd_status(edge_serial,
+				edge_serial->rxStatusParam,
+				edge_serial->rxHeader3);
+			edge_serial->rxState = EXPECT_HDR1;
+			break;
+		}
+	}
+}
+
+
+/*****************************************************************************
+ * process_rcvd_status
+ *	this function handles the any status messages received on the
+ *	bulk in pipe.
+ *****************************************************************************/
+static void process_rcvd_status(struct edgeport_serial *edge_serial,
+						__u8 byte2, __u8 byte3)
+{
+	struct usb_serial_port *port;
+	struct edgeport_port *edge_port;
+	struct tty_struct *tty;
+	struct device *dev;
+	__u8 code = edge_serial->rxStatusCode;
+
+	/* switch the port pointer to the one being currently talked about */
+	port = edge_serial->serial->port[edge_serial->rxPort];
+	edge_port = usb_get_serial_port_data(port);
+	if (edge_port == NULL) {
+		dev_err(&edge_serial->serial->dev->dev,
+			"%s - edge_port == NULL for port %d\n",
+					__func__, edge_serial->rxPort);
+		return;
+	}
+	dev = &port->dev;
+
+	if (code == IOSP_EXT_STATUS) {
+		switch (byte2) {
+		case IOSP_EXT_STATUS_CHASE_RSP:
+			/* we want to do EXT status regardless of port
+			 * open/closed */
+			dev_dbg(dev, "%s - Port %u EXT CHASE_RSP Data = %02x\n",
+				__func__, edge_serial->rxPort, byte3);
+			/* Currently, the only EXT_STATUS is Chase, so process
+			 * here instead of one more call to one more subroutine
+			 * If/when more EXT_STATUS, there'll be more work to do
+			 * Also, we currently clear flag and close the port
+			 * regardless of content of above's Byte3.
+			 * We could choose to do something else when Byte3 says
+			 * Timeout on Chase from Edgeport, like wait longer in
+			 * block_until_chase_response, but for now we don't.
+			 */
+			edge_port->chaseResponsePending = false;
+			wake_up(&edge_port->wait_chase);
+			return;
+
+		case IOSP_EXT_STATUS_RX_CHECK_RSP:
+			dev_dbg(dev, "%s ========== Port %u CHECK_RSP Sequence = %02x =============\n",
+				__func__, edge_serial->rxPort, byte3);
+			/* Port->RxCheckRsp = true; */
+			return;
+		}
+	}
+
+	if (code == IOSP_STATUS_OPEN_RSP) {
+		edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3);
+		edge_port->maxTxCredits = edge_port->txCredits;
+		dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n",
+			__func__, edge_serial->rxPort, byte2, edge_port->txCredits);
+		handle_new_msr(edge_port, byte2);
+
+		/* send the current line settings to the port so we are
+		   in sync with any further termios calls */
+		tty = tty_port_tty_get(&edge_port->port->port);
+		if (tty) {
+			change_port_settings(tty,
+				edge_port, &tty->termios);
+			tty_kref_put(tty);
+		}
+
+		/* we have completed the open */
+		edge_port->openPending = false;
+		edge_port->open = true;
+		wake_up(&edge_port->wait_open);
+		return;
+	}
+
+	/* If port is closed, silently discard all rcvd status. We can
+	 * have cases where buffered status is received AFTER the close
+	 * port command is sent to the Edgeport.
+	 */
+	if (!edge_port->open || edge_port->closePending)
+		return;
+
+	switch (code) {
+	/* Not currently sent by Edgeport */
+	case IOSP_STATUS_LSR:
+		dev_dbg(dev, "%s - Port %u LSR Status = %02x\n",
+			__func__, edge_serial->rxPort, byte2);
+		handle_new_lsr(edge_port, false, byte2, 0);
+		break;
+
+	case IOSP_STATUS_LSR_DATA:
+		dev_dbg(dev, "%s - Port %u LSR Status = %02x, Data = %02x\n",
+			__func__, edge_serial->rxPort, byte2, byte3);
+		/* byte2 is LSR Register */
+		/* byte3 is broken data byte */
+		handle_new_lsr(edge_port, true, byte2, byte3);
+		break;
+	/*
+	 *	case IOSP_EXT_4_STATUS:
+	 *		dev_dbg(dev, "%s - Port %u LSR Status = %02x Data = %02x\n",
+	 *			__func__, edge_serial->rxPort, byte2, byte3);
+	 *		break;
+	 */
+	case IOSP_STATUS_MSR:
+		dev_dbg(dev, "%s - Port %u MSR Status = %02x\n",
+			__func__, edge_serial->rxPort, byte2);
+		/*
+		 * Process this new modem status and generate appropriate
+		 * events, etc, based on the new status. This routine
+		 * also saves the MSR in Port->ShadowMsr.
+		 */
+		handle_new_msr(edge_port, byte2);
+		break;
+
+	default:
+		dev_dbg(dev, "%s - Unrecognized IOSP status code %u\n", __func__, code);
+		break;
+	}
+}
+
+
+/*****************************************************************************
+ * edge_tty_recv
+ *	this function passes data on to the tty flip buffer
+ *****************************************************************************/
+static void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
+		int length)
+{
+	int cnt;
+
+	cnt = tty_insert_flip_string(&port->port, data, length);
+	if (cnt < length) {
+		dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
+				__func__, length - cnt);
+	}
+	data += cnt;
+	length -= cnt;
+
+	tty_flip_buffer_push(&port->port);
+}
+
+
+/*****************************************************************************
+ * handle_new_msr
+ *	this function handles any change to the msr register for a port.
+ *****************************************************************************/
+static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr)
+{
+	struct  async_icount *icount;
+
+	if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
+			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
+		icount = &edge_port->port->icount;
+
+		/* update input line counters */
+		if (newMsr & EDGEPORT_MSR_DELTA_CTS)
+			icount->cts++;
+		if (newMsr & EDGEPORT_MSR_DELTA_DSR)
+			icount->dsr++;
+		if (newMsr & EDGEPORT_MSR_DELTA_CD)
+			icount->dcd++;
+		if (newMsr & EDGEPORT_MSR_DELTA_RI)
+			icount->rng++;
+		wake_up_interruptible(&edge_port->port->port.delta_msr_wait);
+	}
+
+	/* Save the new modem status */
+	edge_port->shadowMSR = newMsr & 0xf0;
+}
+
+
+/*****************************************************************************
+ * handle_new_lsr
+ *	this function handles any change to the lsr register for a port.
+ *****************************************************************************/
+static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
+							__u8 lsr, __u8 data)
+{
+	__u8 newLsr = (__u8) (lsr & (__u8)
+		(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK));
+	struct async_icount *icount;
+
+	edge_port->shadowLSR = lsr;
+
+	if (newLsr & LSR_BREAK) {
+		/*
+		 * Parity and Framing errors only count if they
+		 * occur exclusive of a break being
+		 * received.
+		 */
+		newLsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
+	}
+
+	/* Place LSR data byte into Rx buffer */
+	if (lsrData)
+		edge_tty_recv(edge_port->port, &data, 1);
+
+	/* update input line counters */
+	icount = &edge_port->port->icount;
+	if (newLsr & LSR_BREAK)
+		icount->brk++;
+	if (newLsr & LSR_OVER_ERR)
+		icount->overrun++;
+	if (newLsr & LSR_PAR_ERR)
+		icount->parity++;
+	if (newLsr & LSR_FRM_ERR)
+		icount->frame++;
+}
+
+
+/****************************************************************************
+ * sram_write
+ *	writes a number of bytes to the Edgeport device's sram starting at the
+ *	given address.
+ *	If successful returns the number of bytes written, otherwise it returns
+ *	a negative error number of the problem.
+ ****************************************************************************/
+static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
+					__u16 length, const __u8 *data)
+{
+	int result;
+	__u16 current_length;
+	unsigned char *transfer_buffer;
+
+	dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length);
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	/* need to split these writes up into 64 byte chunks */
+	result = 0;
+	while (length > 0) {
+		if (length > 64)
+			current_length = 64;
+		else
+			current_length = length;
+
+/*		dev_dbg(&serial->dev->dev, "%s - writing %x, %x, %d\n", __func__, extAddr, addr, current_length); */
+		memcpy(transfer_buffer, data, current_length);
+		result = usb_control_msg(serial->dev,
+					usb_sndctrlpipe(serial->dev, 0),
+					USB_REQUEST_ION_WRITE_RAM,
+					0x40, addr, extAddr, transfer_buffer,
+					current_length, 300);
+		if (result < 0)
+			break;
+		length -= current_length;
+		addr += current_length;
+		data += current_length;
+	}
+
+	kfree(transfer_buffer);
+	return result;
+}
+
+
+/****************************************************************************
+ * rom_write
+ *	writes a number of bytes to the Edgeport device's ROM starting at the
+ *	given address.
+ *	If successful returns the number of bytes written, otherwise it returns
+ *	a negative error number of the problem.
+ ****************************************************************************/
+static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
+					__u16 length, const __u8 *data)
+{
+	int result;
+	__u16 current_length;
+	unsigned char *transfer_buffer;
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	/* need to split these writes up into 64 byte chunks */
+	result = 0;
+	while (length > 0) {
+		if (length > 64)
+			current_length = 64;
+		else
+			current_length = length;
+		memcpy(transfer_buffer, data, current_length);
+		result = usb_control_msg(serial->dev,
+					usb_sndctrlpipe(serial->dev, 0),
+					USB_REQUEST_ION_WRITE_ROM, 0x40,
+					addr, extAddr,
+					transfer_buffer, current_length, 300);
+		if (result < 0)
+			break;
+		length -= current_length;
+		addr += current_length;
+		data += current_length;
+	}
+
+	kfree(transfer_buffer);
+	return result;
+}
+
+
+/****************************************************************************
+ * rom_read
+ *	reads a number of bytes from the Edgeport device starting at the given
+ *	address.
+ *	Returns zero on success or a negative error number.
+ ****************************************************************************/
+static int rom_read(struct usb_serial *serial, __u16 extAddr,
+					__u16 addr, __u16 length, __u8 *data)
+{
+	int result;
+	__u16 current_length;
+	unsigned char *transfer_buffer;
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	/* need to split these reads up into 64 byte chunks */
+	result = 0;
+	while (length > 0) {
+		if (length > 64)
+			current_length = 64;
+		else
+			current_length = length;
+		result = usb_control_msg(serial->dev,
+					usb_rcvctrlpipe(serial->dev, 0),
+					USB_REQUEST_ION_READ_ROM,
+					0xC0, addr, extAddr, transfer_buffer,
+					current_length, 300);
+		if (result < current_length) {
+			if (result >= 0)
+				result = -EIO;
+			break;
+		}
+		memcpy(data, transfer_buffer, current_length);
+		length -= current_length;
+		addr += current_length;
+		data += current_length;
+
+		result = 0;
+	}
+
+	kfree(transfer_buffer);
+	return result;
+}
+
+
+/****************************************************************************
+ * send_iosp_ext_cmd
+ *	Is used to send a IOSP message to the Edgeport device
+ ****************************************************************************/
+static int send_iosp_ext_cmd(struct edgeport_port *edge_port,
+						__u8 command, __u8 param)
+{
+	unsigned char   *buffer;
+	unsigned char   *currentCommand;
+	int             length = 0;
+	int             status = 0;
+
+	buffer = kmalloc(10, GFP_ATOMIC);
+	if (!buffer)
+		return -ENOMEM;
+
+	currentCommand = buffer;
+
+	MAKE_CMD_EXT_CMD(&currentCommand, &length, edge_port->port->port_number,
+			 command, param);
+
+	status = write_cmd_usb(edge_port, buffer, length);
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree(buffer);
+	}
+
+	return status;
+}
+
+
+/*****************************************************************************
+ * write_cmd_usb
+ *	this function writes the given buffer out to the bulk write endpoint.
+ *****************************************************************************/
+static int write_cmd_usb(struct edgeport_port *edge_port,
+					unsigned char *buffer, int length)
+{
+	struct edgeport_serial *edge_serial =
+				usb_get_serial_data(edge_port->port->serial);
+	struct device *dev = &edge_port->port->dev;
+	int status = 0;
+	struct urb *urb;
+
+	usb_serial_debug_data(dev, __func__, length, buffer);
+
+	/* Allocate our next urb */
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	atomic_inc(&CmdUrbs);
+	dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n",
+		__func__, urb, atomic_read(&CmdUrbs));
+
+	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
+			usb_sndbulkpipe(edge_serial->serial->dev,
+					edge_serial->bulk_out_endpoint),
+			buffer, length, edge_bulk_out_cmd_callback, edge_port);
+
+	edge_port->commandPending = true;
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (status) {
+		/* something went wrong */
+		dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
+			__func__, status);
+		usb_free_urb(urb);
+		atomic_dec(&CmdUrbs);
+		return status;
+	}
+
+#if 0
+	wait_event(&edge_port->wait_command, !edge_port->commandPending);
+
+	if (edge_port->commandPending) {
+		/* command timed out */
+		dev_dbg(dev, "%s - command timed out\n", __func__);
+		status = -EINVAL;
+	}
+#endif
+	return status;
+}
+
+
+/*****************************************************************************
+ * send_cmd_write_baud_rate
+ *	this function sends the proper command to change the baud rate of the
+ *	specified port.
+ *****************************************************************************/
+static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
+								int baudRate)
+{
+	struct edgeport_serial *edge_serial =
+				usb_get_serial_data(edge_port->port->serial);
+	struct device *dev = &edge_port->port->dev;
+	unsigned char *cmdBuffer;
+	unsigned char *currCmd;
+	int cmdLen = 0;
+	int divisor;
+	int status;
+	u32 number = edge_port->port->port_number;
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) {
+		dev_dbg(dev, "SendCmdWriteBaudRate - NOT Setting baud rate for port, baud = %d\n",
+			baudRate);
+		return 0;
+	}
+
+	dev_dbg(dev, "%s - baud = %d\n", __func__, baudRate);
+
+	status = calc_baud_rate_divisor(dev, baudRate, &divisor);
+	if (status) {
+		dev_err(dev, "%s - bad baud rate\n", __func__);
+		return status;
+	}
+
+	/* Alloc memory for the string of commands. */
+	cmdBuffer =  kmalloc(0x100, GFP_ATOMIC);
+	if (!cmdBuffer)
+		return -ENOMEM;
+
+	currCmd = cmdBuffer;
+
+	/* Enable access to divisor latch */
+	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE);
+
+	/* Write the divisor itself */
+	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLL, LOW8(divisor));
+	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLM, HIGH8(divisor));
+
+	/* Restore original value to disable access to divisor latch */
+	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR,
+						edge_port->shadowLCR);
+
+	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree(cmdBuffer);
+	}
+
+	return status;
+}
+
+
+/*****************************************************************************
+ * calc_baud_rate_divisor
+ *	this function calculates the proper baud rate divisor for the specified
+ *	baud rate.
+ *****************************************************************************/
+static int calc_baud_rate_divisor(struct device *dev, int baudrate, int *divisor)
+{
+	int i;
+	__u16 custom;
+
+	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
+		if (divisor_table[i].BaudRate == baudrate) {
+			*divisor = divisor_table[i].Divisor;
+			return 0;
+		}
+	}
+
+	/* We have tried all of the standard baud rates
+	 * lets try to calculate the divisor for this baud rate
+	 * Make sure the baud rate is reasonable */
+	if (baudrate > 50 && baudrate < 230400) {
+		/* get divisor */
+		custom = (__u16)((230400L + baudrate/2) / baudrate);
+
+		*divisor = custom;
+
+		dev_dbg(dev, "%s - Baud %d = %d\n", __func__, baudrate, custom);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+/*****************************************************************************
+ * send_cmd_write_uart_register
+ *  this function builds up a uart register message and sends to the device.
+ *****************************************************************************/
+static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
+						__u8 regNum, __u8 regValue)
+{
+	struct edgeport_serial *edge_serial =
+				usb_get_serial_data(edge_port->port->serial);
+	struct device *dev = &edge_port->port->dev;
+	unsigned char *cmdBuffer;
+	unsigned char *currCmd;
+	unsigned long cmdLen = 0;
+	int status;
+
+	dev_dbg(dev, "%s - write to %s register 0x%02x\n",
+		(regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPWriteMCR &&
+	    regNum == MCR) {
+		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to MCR Register\n");
+		return 0;
+	}
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPWriteLCR &&
+	    regNum == LCR) {
+		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to LCR Register\n");
+		return 0;
+	}
+
+	/* Alloc memory for the string of commands. */
+	cmdBuffer = kmalloc(0x10, GFP_ATOMIC);
+	if (cmdBuffer == NULL)
+		return -ENOMEM;
+
+	currCmd = cmdBuffer;
+
+	/* Build a cmd in the buffer to write the given register */
+	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->port_number,
+			   regNum, regValue);
+
+	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree(cmdBuffer);
+	}
+
+	return status;
+}
+
+
+/*****************************************************************************
+ * change_port_settings
+ *	This routine is called to set the UART on the device to match the
+ *	specified new settings.
+ *****************************************************************************/
+
+static void change_port_settings(struct tty_struct *tty,
+	struct edgeport_port *edge_port, struct ktermios *old_termios)
+{
+	struct device *dev = &edge_port->port->dev;
+	struct edgeport_serial *edge_serial =
+			usb_get_serial_data(edge_port->port->serial);
+	int baud;
+	unsigned cflag;
+	__u8 mask = 0xff;
+	__u8 lData;
+	__u8 lParity;
+	__u8 lStop;
+	__u8 rxFlow;
+	__u8 txFlow;
+	int status;
+
+	if (!edge_port->open &&
+	    !edge_port->openPending) {
+		dev_dbg(dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	cflag = tty->termios.c_cflag;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = LCR_BITS_5; mask = 0x1f;
+		dev_dbg(dev, "%s - data bits = 5\n", __func__);
+		break;
+	case CS6:
+		lData = LCR_BITS_6; mask = 0x3f;
+		dev_dbg(dev, "%s - data bits = 6\n", __func__);
+		break;
+	case CS7:
+		lData = LCR_BITS_7; mask = 0x7f;
+		dev_dbg(dev, "%s - data bits = 7\n", __func__);
+		break;
+	default:
+	case CS8:
+		lData = LCR_BITS_8;
+		dev_dbg(dev, "%s - data bits = 8\n", __func__);
+		break;
+	}
+
+	lParity = LCR_PAR_NONE;
+	if (cflag & PARENB) {
+		if (cflag & CMSPAR) {
+			if (cflag & PARODD) {
+				lParity = LCR_PAR_MARK;
+				dev_dbg(dev, "%s - parity = mark\n", __func__);
+			} else {
+				lParity = LCR_PAR_SPACE;
+				dev_dbg(dev, "%s - parity = space\n", __func__);
+			}
+		} else if (cflag & PARODD) {
+			lParity = LCR_PAR_ODD;
+			dev_dbg(dev, "%s - parity = odd\n", __func__);
+		} else {
+			lParity = LCR_PAR_EVEN;
+			dev_dbg(dev, "%s - parity = even\n", __func__);
+		}
+	} else {
+		dev_dbg(dev, "%s - parity = none\n", __func__);
+	}
+
+	if (cflag & CSTOPB) {
+		lStop = LCR_STOP_2;
+		dev_dbg(dev, "%s - stop bits = 2\n", __func__);
+	} else {
+		lStop = LCR_STOP_1;
+		dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+	}
+
+	/* figure out the flow control settings */
+	rxFlow = txFlow = 0x00;
+	if (cflag & CRTSCTS) {
+		rxFlow |= IOSP_RX_FLOW_RTS;
+		txFlow |= IOSP_TX_FLOW_CTS;
+		dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
+	} else {
+		dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
+	}
+
+	/* if we are implementing XON/XOFF, set the start and stop character
+	   in the device */
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		unsigned char stop_char  = STOP_CHAR(tty);
+		unsigned char start_char = START_CHAR(tty);
+
+		if (!edge_serial->is_epic ||
+		    edge_serial->epic_descriptor.Supports.IOSPSetXChar) {
+			send_iosp_ext_cmd(edge_port,
+					IOSP_CMD_SET_XON_CHAR, start_char);
+			send_iosp_ext_cmd(edge_port,
+					IOSP_CMD_SET_XOFF_CHAR, stop_char);
+		}
+
+		/* if we are implementing INBOUND XON/XOFF */
+		if (I_IXOFF(tty)) {
+			rxFlow |= IOSP_RX_FLOW_XON_XOFF;
+			dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+				__func__, start_char, stop_char);
+		} else {
+			dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
+		}
+
+		/* if we are implementing OUTBOUND XON/XOFF */
+		if (I_IXON(tty)) {
+			txFlow |= IOSP_TX_FLOW_XON_XOFF;
+			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+				__func__, start_char, stop_char);
+		} else {
+			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
+		}
+	}
+
+	/* Set flow control to the configured value */
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)
+		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
+	if (!edge_serial->is_epic ||
+	    edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)
+		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
+
+
+	edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+	edge_port->shadowLCR |= (lData | lParity | lStop);
+
+	edge_port->validDataMask = mask;
+
+	/* Send the updated LCR value to the EdgePort */
+	status = send_cmd_write_uart_register(edge_port, LCR,
+							edge_port->shadowLCR);
+	if (status != 0)
+		return;
+
+	/* set up the MCR register and send it to the EdgePort */
+	edge_port->shadowMCR = MCR_MASTER_IE;
+	if (cflag & CBAUD)
+		edge_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+
+	status = send_cmd_write_uart_register(edge_port, MCR,
+						edge_port->shadowMCR);
+	if (status != 0)
+		return;
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (!baud) {
+		/* pick a default, any default... */
+		baud = 9600;
+	}
+
+	dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
+	status = send_cmd_write_baud_rate(edge_port, baud);
+	if (status == -1) {
+		/* Speed change was not possible - put back the old speed */
+		baud = tty_termios_baud_rate(old_termios);
+		tty_encode_baud_rate(tty, baud, baud);
+	}
+}
+
+
+/****************************************************************************
+ * unicode_to_ascii
+ *	Turns a string from Unicode into ASCII.
+ *	Doesn't do a good job with any characters that are outside the normal
+ *	ASCII range, but it's only for debugging...
+ *	NOTE: expects the unicode in LE format
+ ****************************************************************************/
+static void unicode_to_ascii(char *string, int buflen,
+					__le16 *unicode, int unicode_size)
+{
+	int i;
+
+	if (buflen <= 0)	/* never happens, but... */
+		return;
+	--buflen;		/* space for nul */
+
+	for (i = 0; i < unicode_size; i++) {
+		if (i >= buflen)
+			break;
+		string[i] = (char)(le16_to_cpu(unicode[i]));
+	}
+	string[i] = 0x00;
+}
+
+
+/****************************************************************************
+ * get_manufacturing_desc
+ *	reads in the manufacturing descriptor and stores it into the serial
+ *	structure.
+ ****************************************************************************/
+static void get_manufacturing_desc(struct edgeport_serial *edge_serial)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+	int response;
+
+	dev_dbg(dev, "getting manufacturer descriptor\n");
+
+	response = rom_read(edge_serial->serial,
+				(EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16,
+				(__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff),
+				EDGE_MANUF_DESC_LEN,
+				(__u8 *)(&edge_serial->manuf_descriptor));
+
+	if (response < 0) {
+		dev_err(dev, "error in getting manufacturer descriptor: %d\n",
+				response);
+	} else {
+		char string[30];
+		dev_dbg(dev, "**Manufacturer Descriptor\n");
+		dev_dbg(dev, "  RomSize:        %dK\n",
+			edge_serial->manuf_descriptor.RomSize);
+		dev_dbg(dev, "  RamSize:        %dK\n",
+			edge_serial->manuf_descriptor.RamSize);
+		dev_dbg(dev, "  CpuRev:         %d\n",
+			edge_serial->manuf_descriptor.CpuRev);
+		dev_dbg(dev, "  BoardRev:       %d\n",
+			edge_serial->manuf_descriptor.BoardRev);
+		dev_dbg(dev, "  NumPorts:       %d\n",
+			edge_serial->manuf_descriptor.NumPorts);
+		dev_dbg(dev, "  DescDate:       %d/%d/%d\n",
+			edge_serial->manuf_descriptor.DescDate[0],
+			edge_serial->manuf_descriptor.DescDate[1],
+			edge_serial->manuf_descriptor.DescDate[2]+1900);
+		unicode_to_ascii(string, sizeof(string),
+			edge_serial->manuf_descriptor.SerialNumber,
+			edge_serial->manuf_descriptor.SerNumLength/2);
+		dev_dbg(dev, "  SerialNumber: %s\n", string);
+		unicode_to_ascii(string, sizeof(string),
+			edge_serial->manuf_descriptor.AssemblyNumber,
+			edge_serial->manuf_descriptor.AssemblyNumLength/2);
+		dev_dbg(dev, "  AssemblyNumber: %s\n", string);
+		unicode_to_ascii(string, sizeof(string),
+		    edge_serial->manuf_descriptor.OemAssyNumber,
+		    edge_serial->manuf_descriptor.OemAssyNumLength/2);
+		dev_dbg(dev, "  OemAssyNumber:  %s\n", string);
+		dev_dbg(dev, "  UartType:       %d\n",
+			edge_serial->manuf_descriptor.UartType);
+		dev_dbg(dev, "  IonPid:         %d\n",
+			edge_serial->manuf_descriptor.IonPid);
+		dev_dbg(dev, "  IonConfig:      %d\n",
+			edge_serial->manuf_descriptor.IonConfig);
+	}
+}
+
+
+/****************************************************************************
+ * get_boot_desc
+ *	reads in the bootloader descriptor and stores it into the serial
+ *	structure.
+ ****************************************************************************/
+static void get_boot_desc(struct edgeport_serial *edge_serial)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+	int response;
+
+	dev_dbg(dev, "getting boot descriptor\n");
+
+	response = rom_read(edge_serial->serial,
+				(EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16,
+				(__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff),
+				EDGE_BOOT_DESC_LEN,
+				(__u8 *)(&edge_serial->boot_descriptor));
+
+	if (response < 0) {
+		dev_err(dev, "error in getting boot descriptor: %d\n",
+				response);
+	} else {
+		dev_dbg(dev, "**Boot Descriptor:\n");
+		dev_dbg(dev, "  BootCodeLength: %d\n",
+			le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
+		dev_dbg(dev, "  MajorVersion:   %d\n",
+			edge_serial->boot_descriptor.MajorVersion);
+		dev_dbg(dev, "  MinorVersion:   %d\n",
+			edge_serial->boot_descriptor.MinorVersion);
+		dev_dbg(dev, "  BuildNumber:    %d\n",
+			le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
+		dev_dbg(dev, "  Capabilities:   0x%x\n",
+		      le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
+		dev_dbg(dev, "  UConfig0:       %d\n",
+			edge_serial->boot_descriptor.UConfig0);
+		dev_dbg(dev, "  UConfig1:       %d\n",
+			edge_serial->boot_descriptor.UConfig1);
+	}
+}
+
+
+/****************************************************************************
+ * load_application_firmware
+ *	This is called to load the application firmware to the device
+ ****************************************************************************/
+static void load_application_firmware(struct edgeport_serial *edge_serial)
+{
+	struct device *dev = &edge_serial->serial->dev->dev;
+	const struct ihex_binrec *rec;
+	const struct firmware *fw;
+	const char *fw_name;
+	const char *fw_info;
+	int response;
+	__u32 Operaddr;
+	__u16 build;
+
+	switch (edge_serial->product_info.iDownloadFile) {
+		case EDGE_DOWNLOAD_FILE_I930:
+			fw_info = "downloading firmware version (930)";
+			fw_name	= "edgeport/down.fw";
+			break;
+
+		case EDGE_DOWNLOAD_FILE_80251:
+			fw_info = "downloading firmware version (80251)";
+			fw_name	= "edgeport/down2.fw";
+			break;
+
+		case EDGE_DOWNLOAD_FILE_NONE:
+			dev_dbg(dev, "No download file specified, skipping download\n");
+			return;
+
+		default:
+			return;
+	}
+
+	response = request_ihex_firmware(&fw, fw_name,
+				    &edge_serial->serial->dev->dev);
+	if (response) {
+		dev_err(dev, "Failed to load image \"%s\" err %d\n",
+		       fw_name, response);
+		return;
+	}
+
+	rec = (const struct ihex_binrec *)fw->data;
+	build = (rec->data[2] << 8) | rec->data[3];
+
+	dev_dbg(dev, "%s %d.%d.%d\n", fw_info, rec->data[0], rec->data[1], build);
+
+	edge_serial->product_info.FirmwareMajorVersion = rec->data[0];
+	edge_serial->product_info.FirmwareMinorVersion = rec->data[1];
+	edge_serial->product_info.FirmwareBuildNumber = cpu_to_le16(build);
+
+	for (rec = ihex_next_binrec(rec); rec;
+	     rec = ihex_next_binrec(rec)) {
+		Operaddr = be32_to_cpu(rec->addr);
+		response = sram_write(edge_serial->serial,
+				     Operaddr >> 16,
+				     Operaddr & 0xFFFF,
+				     be16_to_cpu(rec->len),
+				     &rec->data[0]);
+		if (response < 0) {
+			dev_err(&edge_serial->serial->dev->dev,
+				"sram_write failed (%x, %x, %d)\n",
+				Operaddr >> 16, Operaddr & 0xFFFF,
+				be16_to_cpu(rec->len));
+			break;
+		}
+	}
+
+	dev_dbg(dev, "sending exec_dl_code\n");
+	response = usb_control_msg (edge_serial->serial->dev,
+				    usb_sndctrlpipe(edge_serial->serial->dev, 0),
+				    USB_REQUEST_ION_EXEC_DL_CODE,
+				    0x40, 0x4000, 0x0001, NULL, 0, 3000);
+
+	release_firmware(fw);
+}
+
+
+/****************************************************************************
+ * edge_startup
+ ****************************************************************************/
+static int edge_startup(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial;
+	struct usb_device *dev;
+	struct device *ddev = &serial->dev->dev;
+	int i;
+	int response;
+	bool interrupt_in_found;
+	bool bulk_in_found;
+	bool bulk_out_found;
+	static const __u32 descriptor[3] = {	EDGE_COMPATIBILITY_MASK0,
+						EDGE_COMPATIBILITY_MASK1,
+						EDGE_COMPATIBILITY_MASK2 };
+
+	dev = serial->dev;
+
+	/* create our private serial structure */
+	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
+	if (!edge_serial)
+		return -ENOMEM;
+
+	spin_lock_init(&edge_serial->es_lock);
+	edge_serial->serial = serial;
+	usb_set_serial_data(serial, edge_serial);
+
+	/* get the name for the device from the device */
+	i = usb_string(dev, dev->descriptor.iManufacturer,
+	    &edge_serial->name[0], MAX_NAME_LEN+1);
+	if (i < 0)
+		i = 0;
+	edge_serial->name[i++] = ' ';
+	usb_string(dev, dev->descriptor.iProduct,
+	    &edge_serial->name[i], MAX_NAME_LEN+2 - i);
+
+	dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
+
+	/* Read the epic descriptor */
+	if (get_epic_descriptor(edge_serial) < 0) {
+		/* memcpy descriptor to Supports structures */
+		memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
+		       sizeof(struct edge_compatibility_bits));
+
+		/* get the manufacturing descriptor for this device */
+		get_manufacturing_desc(edge_serial);
+
+		/* get the boot descriptor */
+		get_boot_desc(edge_serial);
+
+		get_product_info(edge_serial);
+	}
+
+	/* set the number of ports from the manufacturing description */
+	/* serial->num_ports = serial->product_info.NumPorts; */
+	if ((!edge_serial->is_epic) &&
+	    (edge_serial->product_info.NumPorts != serial->num_ports)) {
+		dev_warn(ddev,
+			"Device Reported %d serial ports vs. core thinking we have %d ports, email greg@kroah.com this information.\n",
+			 edge_serial->product_info.NumPorts,
+			 serial->num_ports);
+	}
+
+	dev_dbg(ddev, "%s - time 1 %ld\n", __func__, jiffies);
+
+	/* If not an EPiC device */
+	if (!edge_serial->is_epic) {
+		/* now load the application firmware into this device */
+		load_application_firmware(edge_serial);
+
+		dev_dbg(ddev, "%s - time 2 %ld\n", __func__, jiffies);
+
+		/* Check current Edgeport EEPROM and update if necessary */
+		update_edgeport_E2PROM(edge_serial);
+
+		dev_dbg(ddev, "%s - time 3 %ld\n", __func__, jiffies);
+
+		/* set the configuration to use #1 */
+/*		dev_dbg(ddev, "set_configuration 1\n"); */
+/*		usb_set_configuration (dev, 1); */
+	}
+	dev_dbg(ddev, "  FirmwareMajorVersion  %d.%d.%d\n",
+	    edge_serial->product_info.FirmwareMajorVersion,
+	    edge_serial->product_info.FirmwareMinorVersion,
+	    le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber));
+
+	/* we set up the pointers to the endpoints in the edge_open function,
+	 * as the structures aren't created yet. */
+
+	response = 0;
+
+	if (edge_serial->is_epic) {
+		/* EPIC thing, set up our interrupt polling now and our read
+		 * urb, so that the device knows it really is connected. */
+		interrupt_in_found = bulk_in_found = bulk_out_found = false;
+		for (i = 0; i < serial->interface->altsetting[0]
+						.desc.bNumEndpoints; ++i) {
+			struct usb_endpoint_descriptor *endpoint;
+			int buffer_size;
+
+			endpoint = &serial->interface->altsetting[0].
+							endpoint[i].desc;
+			buffer_size = usb_endpoint_maxp(endpoint);
+			if (!interrupt_in_found &&
+			    (usb_endpoint_is_int_in(endpoint))) {
+				/* we found a interrupt in endpoint */
+				dev_dbg(ddev, "found interrupt in\n");
+
+				/* not set up yet, so do it now */
+				edge_serial->interrupt_read_urb =
+						usb_alloc_urb(0, GFP_KERNEL);
+				if (!edge_serial->interrupt_read_urb) {
+					response = -ENOMEM;
+					break;
+				}
+
+				edge_serial->interrupt_in_buffer =
+					kmalloc(buffer_size, GFP_KERNEL);
+				if (!edge_serial->interrupt_in_buffer) {
+					response = -ENOMEM;
+					break;
+				}
+				edge_serial->interrupt_in_endpoint =
+						endpoint->bEndpointAddress;
+
+				/* set up our interrupt urb */
+				usb_fill_int_urb(
+					edge_serial->interrupt_read_urb,
+					dev,
+					usb_rcvintpipe(dev,
+						endpoint->bEndpointAddress),
+					edge_serial->interrupt_in_buffer,
+					buffer_size,
+					edge_interrupt_callback,
+					edge_serial,
+					endpoint->bInterval);
+
+				interrupt_in_found = true;
+			}
+
+			if (!bulk_in_found &&
+				(usb_endpoint_is_bulk_in(endpoint))) {
+				/* we found a bulk in endpoint */
+				dev_dbg(ddev, "found bulk in\n");
+
+				/* not set up yet, so do it now */
+				edge_serial->read_urb =
+						usb_alloc_urb(0, GFP_KERNEL);
+				if (!edge_serial->read_urb) {
+					response = -ENOMEM;
+					break;
+				}
+
+				edge_serial->bulk_in_buffer =
+					kmalloc(buffer_size, GFP_KERNEL);
+				if (!edge_serial->bulk_in_buffer) {
+					response = -ENOMEM;
+					break;
+				}
+				edge_serial->bulk_in_endpoint =
+						endpoint->bEndpointAddress;
+
+				/* set up our bulk in urb */
+				usb_fill_bulk_urb(edge_serial->read_urb, dev,
+					usb_rcvbulkpipe(dev,
+						endpoint->bEndpointAddress),
+					edge_serial->bulk_in_buffer,
+					usb_endpoint_maxp(endpoint),
+					edge_bulk_in_callback,
+					edge_serial);
+				bulk_in_found = true;
+			}
+
+			if (!bulk_out_found &&
+			    (usb_endpoint_is_bulk_out(endpoint))) {
+				/* we found a bulk out endpoint */
+				dev_dbg(ddev, "found bulk out\n");
+				edge_serial->bulk_out_endpoint =
+						endpoint->bEndpointAddress;
+				bulk_out_found = true;
+			}
+		}
+
+		if (response || !interrupt_in_found || !bulk_in_found ||
+							!bulk_out_found) {
+			if (!response) {
+				dev_err(ddev, "expected endpoints not found\n");
+				response = -ENODEV;
+			}
+
+			usb_free_urb(edge_serial->interrupt_read_urb);
+			kfree(edge_serial->interrupt_in_buffer);
+
+			usb_free_urb(edge_serial->read_urb);
+			kfree(edge_serial->bulk_in_buffer);
+
+			kfree(edge_serial);
+
+			return response;
+		}
+
+		/* start interrupt read for this edgeport this interrupt will
+		 * continue as long as the edgeport is connected */
+		response = usb_submit_urb(edge_serial->interrupt_read_urb,
+								GFP_KERNEL);
+		if (response)
+			dev_err(ddev, "%s - Error %d submitting control urb\n",
+				__func__, response);
+	}
+	return response;
+}
+
+
+/****************************************************************************
+ * edge_disconnect
+ *	This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+static void edge_disconnect(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+	if (edge_serial->is_epic) {
+		usb_kill_urb(edge_serial->interrupt_read_urb);
+		usb_kill_urb(edge_serial->read_urb);
+	}
+}
+
+
+/****************************************************************************
+ * edge_release
+ *	This function is called when the device structure is deallocated.
+ ****************************************************************************/
+static void edge_release(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+	if (edge_serial->is_epic) {
+		usb_kill_urb(edge_serial->interrupt_read_urb);
+		usb_free_urb(edge_serial->interrupt_read_urb);
+		kfree(edge_serial->interrupt_in_buffer);
+
+		usb_kill_urb(edge_serial->read_urb);
+		usb_free_urb(edge_serial->read_urb);
+		kfree(edge_serial->bulk_in_buffer);
+	}
+
+	kfree(edge_serial);
+}
+
+static int edge_port_probe(struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port;
+
+	edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
+	if (!edge_port)
+		return -ENOMEM;
+
+	spin_lock_init(&edge_port->ep_lock);
+	edge_port->port = port;
+
+	usb_set_serial_port_data(port, edge_port);
+
+	return 0;
+}
+
+static int edge_port_remove(struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port;
+
+	edge_port = usb_get_serial_port_data(port);
+	kfree(edge_port);
+
+	return 0;
+}
+
+static struct usb_serial_driver edgeport_2port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "edgeport_2",
+	},
+	.description		= "Edgeport 2 port adapter",
+	.id_table		= edgeport_2port_id_table,
+	.num_ports		= 2,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.num_interrupt_in	= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver edgeport_4port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "edgeport_4",
+	},
+	.description		= "Edgeport 4 port adapter",
+	.id_table		= edgeport_4port_id_table,
+	.num_ports		= 4,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.num_interrupt_in	= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver edgeport_8port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "edgeport_8",
+	},
+	.description		= "Edgeport 8 port adapter",
+	.id_table		= edgeport_8port_id_table,
+	.num_ports		= 8,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.num_interrupt_in	= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver epic_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "epic",
+	},
+	.description		= "EPiC device",
+	.id_table		= Epic_port_id_table,
+	.num_ports		= 1,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.num_interrupt_in	= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&edgeport_2port_device, &edgeport_4port_device,
+	&edgeport_8port_device, &epic_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("edgeport/boot.fw");
+MODULE_FIRMWARE("edgeport/boot2.fw");
+MODULE_FIRMWARE("edgeport/down.fw");
+MODULE_FIRMWARE("edgeport/down2.fw");
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
new file mode 100644
index 0000000..2e7fedb
--- /dev/null
+++ b/drivers/usb/serial/io_edgeport.h
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0+
+/************************************************************************
+ *
+ *	io_edgeport.h	Edgeport Linux Interface definitions
+ *
+ *	Copyright (C) 2000 Inside Out Networks, Inc.
+ *
+ ************************************************************************/
+
+#if !defined(_IO_EDGEPORT_H_)
+#define	_IO_EDGEPORT_H_
+
+
+#define MAX_RS232_PORTS		8	/* Max # of RS-232 ports per device */
+
+/* typedefs that the insideout headers need */
+#ifndef LOW8
+	#define LOW8(a)		((unsigned char)(a & 0xff))
+#endif
+#ifndef HIGH8
+	#define HIGH8(a)	((unsigned char)((a & 0xff00) >> 8))
+#endif
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#include "io_usbvend.h"
+
+
+
+/* The following table is used to map the USBx port number to
+ * the device serial number (or physical USB path), */
+#define MAX_EDGEPORTS	64
+
+struct comMapper {
+	char	SerialNumber[MAX_SERIALNUMBER_LEN+1];	/* Serial number/usb path */
+	int	numPorts;				/* Number of ports */
+	int	Original[MAX_RS232_PORTS];		/* Port numbers set by IOCTL */
+	int	Port[MAX_RS232_PORTS];			/* Actual used port numbers */
+};
+
+
+#define EDGEPORT_CONFIG_DEVICE "/proc/edgeport"
+
+/* /proc/edgeport Interface
+ * This interface uses read/write/lseek interface to talk to the edgeport driver
+ * the following read functions are supported: */
+#define PROC_GET_MAPPING_TO_PATH	1
+#define PROC_GET_COM_ENTRY		2
+#define PROC_GET_EDGE_MANUF_DESCRIPTOR	3
+#define PROC_GET_BOOT_DESCRIPTOR	4
+#define PROC_GET_PRODUCT_INFO		5
+#define PROC_GET_STRINGS		6
+#define PROC_GET_CURRENT_COM_MAPPING	7
+
+/* The parameters to the lseek() for the read is: */
+#define PROC_READ_SETUP(Command, Argument)	((Command) + ((Argument)<<8))
+
+
+/* the following write functions are supported: */
+#define PROC_SET_COM_MAPPING		1
+#define PROC_SET_COM_ENTRY		2
+
+
+/* The following structure is passed to the write */
+struct procWrite {
+	int	Command;
+	union {
+		struct comMapper	Entry;
+		int			ComMappingBasedOnUSBPort;	/* Boolean value */
+	} u;
+};
+
+/*
+ *	Product information read from the Edgeport
+ */
+struct edgeport_product_info {
+	__u16	ProductId;			/* Product Identifier */
+	__u8	NumPorts;			/* Number of ports on edgeport */
+	__u8	ProdInfoVer;			/* What version of structure is this? */
+
+	__u32	IsServer        :1;		/* Set if Server */
+	__u32	IsRS232         :1;		/* Set if RS-232 ports exist */
+	__u32	IsRS422         :1;		/* Set if RS-422 ports exist */
+	__u32	IsRS485         :1;		/* Set if RS-485 ports exist */
+	__u32	IsReserved      :28;		/* Reserved for later expansion */
+
+	__u8	RomSize;			/* Size of ROM/E2PROM in K */
+	__u8	RamSize;			/* Size of external RAM in K */
+	__u8	CpuRev;				/* CPU revision level (chg only if s/w visible) */
+	__u8	BoardRev;			/* PCB revision level (chg only if s/w visible) */
+
+	__u8	BootMajorVersion;		/* Boot Firmware version: xx. */
+	__u8	BootMinorVersion;		/*			  yy. */
+	__le16	BootBuildNumber;		/*			  zzzz (LE format) */
+
+	__u8	FirmwareMajorVersion;		/* Operational Firmware version:xx. */
+	__u8	FirmwareMinorVersion;		/*				yy. */
+	__le16	FirmwareBuildNumber;		/*				zzzz (LE format) */
+
+	__u8	ManufactureDescDate[3];		/* MM/DD/YY when descriptor template was compiled */
+	__u8	HardwareType;
+
+	__u8	iDownloadFile;			/* What to download to EPiC device */
+	__u8	EpicVer;			/* What version of EPiC spec this device supports */
+
+	struct edge_compatibility_bits Epic;
+};
+
+/*
+ *	Edgeport Stringblock String locations
+ */
+#define EDGESTRING_MANUFNAME		1	/* Manufacture Name */
+#define EDGESTRING_PRODNAME		2	/* Product Name */
+#define EDGESTRING_SERIALNUM		3	/* Serial Number */
+#define EDGESTRING_ASSEMNUM		4	/* Assembly Number */
+#define EDGESTRING_OEMASSEMNUM		5	/* OEM Assembly Number */
+#define EDGESTRING_MANUFDATE		6	/* Manufacture Date */
+#define EDGESTRING_ORIGSERIALNUM	7	/* Serial Number */
+
+struct string_block {
+	__u16	NumStrings;			/* Number of strings in block */
+	__u16	Strings[1];			/* Start of string block */
+};
+
+
+
+#endif
diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h
new file mode 100644
index 0000000..4b8e482
--- /dev/null
+++ b/drivers/usb/serial/io_ionsp.h
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0+
+/************************************************************************
+ *
+ *	IONSP.H		Definitions for I/O Networks Serial Protocol
+ *
+ *	Copyright (C) 1997-1998 Inside Out Networks, Inc.
+ *
+ *	These definitions are used by both kernel-mode driver and the
+ *	peripheral firmware and MUST be kept in sync.
+ *
+ ************************************************************************/
+
+/************************************************************************
+
+The data to and from all ports on the peripheral is multiplexed
+through a single endpoint pair (EP1 since it supports 64-byte
+MaxPacketSize). Therefore, the data, commands, and status for
+each port must be preceded by a short header identifying the
+destination port. The header also identifies the bytes that follow
+as data or as command/status info.
+
+Header format, first byte:
+
+    CLLLLPPP
+    --------
+    | |	 |------ Port Number:	0-7
+    | |--------- Length:	MSB bits of length
+    |----------- Data/Command:	0 = Data header
+				1 = Cmd / Status (Cmd if OUT, Status if IN)
+
+This gives 2 possible formats:
+
+
+    Data header:		0LLLLPPP	LLLLLLLL
+    ============
+
+    Where (LLLL,LLLLLLL) is 12-bit length of data that follows for
+    port number (PPP). The length is 0-based (0-FFF means 0-4095
+    bytes). The ~4K limit allows the host driver (which deals in
+    transfer requests instead of individual packets) to write a
+    large chunk of data in a single request. Note, however, that
+    the length must always be <= the current TxCredits for a given
+    port due to buffering limitations on the peripheral.
+
+
+    Cmd/Status header:		1ccccPPP	[ CCCCCCCC,	 Params ]...
+    ==================
+
+    Where (cccc) or (cccc,CCCCCCCC) is the cmd or status identifier.
+    Frequently-used values are encoded as (cccc), longer ones using
+    (cccc,CCCCCCCC). Subsequent bytes are optional parameters and are
+    specific to the cmd or status code. This may include a length
+    for command and status codes that need variable-length parameters.
+
+
+In addition, we use another interrupt pipe (endpoint) which the host polls
+periodically for flow control information. The peripheral, when there has
+been a change, sends the following 10-byte packet:
+
+	RRRRRRRRRRRRRRRR
+	T0T0T0T0T0T0T0T0
+	T1T1T1T1T1T1T1T1
+	T2T2T2T2T2T2T2T2
+	T3T3T3T3T3T3T3T3
+
+The first field is the 16-bit RxBytesAvail field, which indicates the
+number of bytes which may be read by the host from EP1. This is necessary:
+(a) because OSR2.1 has a bug which causes data loss if the peripheral returns
+fewer bytes than the host expects to read, and (b) because, on Microsoft
+platforms at least, an outstanding read posted on EP1 consumes about 35% of
+the CPU just polling the device for data.
+
+The next 4 fields are the 16-bit TxCredits for each port, which indicate how
+many bytes the host is allowed to send on EP1 for transmit to a given port.
+After an OPEN_PORT command, the Edgeport sends the initial TxCredits for that
+port.
+
+All 16-bit fields are sent in little-endian (Intel) format.
+
+************************************************************************/
+
+//
+// Define format of InterruptStatus packet returned from the
+// Interrupt pipe
+//
+
+struct int_status_pkt {
+	__u16 RxBytesAvail;			// Additional bytes available to
+						// be read from Bulk IN pipe
+	__u16 TxCredits[MAX_RS232_PORTS];	// Additional space available in
+						// given port's TxBuffer
+};
+
+
+#define GET_INT_STATUS_SIZE(NumPorts) (sizeof(__u16) + (sizeof(__u16) * (NumPorts)))
+
+
+
+//
+// Define cmd/status header values and macros to extract them.
+//
+//	Data:		0LLLLPPP LLLLLLLL
+//	Cmd/Stat:	1ccccPPP CCCCCCCC
+
+#define	IOSP_DATA_HDR_SIZE		2
+#define	IOSP_CMD_HDR_SIZE		2
+
+#define	IOSP_MAX_DATA_LENGTH		0x0FFF		// 12 bits -> 4K
+
+#define	IOSP_PORT_MASK			0x07		// Mask to isolate port number
+#define	IOSP_CMD_STAT_BIT		0x80		// If set, this is command/status header
+
+#define IS_CMD_STAT_HDR(Byte1)		((Byte1) & IOSP_CMD_STAT_BIT)
+#define IS_DATA_HDR(Byte1)		(!IS_CMD_STAT_HDR(Byte1))
+
+#define	IOSP_GET_HDR_PORT(Byte1)		((__u8) ((Byte1) & IOSP_PORT_MASK))
+#define	IOSP_GET_HDR_DATA_LEN(Byte1, Byte2)	((__u16) (((__u16)((Byte1) & 0x78)) << 5) | (Byte2))
+#define	IOSP_GET_STATUS_CODE(Byte1)		((__u8) (((Byte1) &  0x78) >> 3))
+
+
+//
+// These macros build the 1st and 2nd bytes for a data header
+//
+#define	IOSP_BUILD_DATA_HDR1(Port, Len)		((__u8) (((Port) | ((__u8) (((__u16) (Len)) >> 5) & 0x78))))
+#define	IOSP_BUILD_DATA_HDR2(Port, Len)		((__u8) (Len))
+
+
+//
+// These macros build the 1st and 2nd bytes for a command header
+//
+#define	IOSP_BUILD_CMD_HDR1(Port, Cmd)		((__u8) (IOSP_CMD_STAT_BIT | (Port) | ((__u8) ((Cmd) << 3))))
+
+
+//--------------------------------------------------------------
+//
+//	Define values for commands and command parameters
+//	(sent from Host to Edgeport)
+//
+//	1ccccPPP P1P1P1P1 [ P2P2P2P2P2 ]...
+//
+//	cccc:	00-07	2-byte commands. Write UART register 0-7 with
+//					value in P1. See 16650.H for definitions of
+//					UART register numbers and contents.
+//
+//		08-0B	3-byte commands:					==== P1 ====	==== P2 ====
+//					08	available for expansion
+//					09	1-param commands		Command Code	Param
+//					0A	available for expansion
+//					0B	available for expansion
+//
+//		0C-0D	4-byte commands.	P1 = extended cmd and P2,P3 = params
+//						Currently unimplemented.
+//
+//		0E-0F	N-byte commands:	P1 = num bytes after P1 (ie, TotalLen - 2)
+//						P2 = extended cmd, P3..Pn = parameters.
+//						Currently unimplemented.
+//
+
+#define	IOSP_WRITE_UART_REG(n)	((n) & 0x07)	// UartReg[ n ] := P1
+
+// Register numbers and contents
+// defined in 16554.H.
+
+//					0x08		// Available for expansion.
+#define	IOSP_EXT_CMD			0x09		// P1 = Command code (defined below)
+
+// P2 = Parameter
+
+//
+// Extended Command values, used with IOSP_EXT_CMD, may
+// or may not use parameter P2.
+//
+
+#define	IOSP_CMD_OPEN_PORT		0x00		// Enable ints, init UART. (NO PARAM)
+#define	IOSP_CMD_CLOSE_PORT		0x01		// Disable ints, flush buffers. (NO PARAM)
+#define	IOSP_CMD_CHASE_PORT		0x02		// Wait for Edgeport TX buffers to empty. (NO PARAM)
+#define IOSP_CMD_SET_RX_FLOW		0x03		// Set Rx Flow Control in Edgeport
+#define IOSP_CMD_SET_TX_FLOW		0x04		// Set Tx Flow Control in Edgeport
+#define IOSP_CMD_SET_XON_CHAR		0x05		// Set XON Character in Edgeport
+#define IOSP_CMD_SET_XOFF_CHAR		0x06		// Set XOFF Character in Edgeport
+#define IOSP_CMD_RX_CHECK_REQ		0x07		// Request Edgeport to insert a Checkpoint into
+
+// the receive data stream (Parameter = 1 byte sequence number)
+
+#define IOSP_CMD_SET_BREAK		0x08		// Turn on the BREAK (LCR bit 6)
+#define IOSP_CMD_CLEAR_BREAK		0x09		// Turn off the BREAK (LCR bit 6)
+
+
+//
+// Define macros to simplify building of IOSP cmds
+//
+
+#define MAKE_CMD_WRITE_REG(ppBuf, pLen, Port, Reg, Val)			\
+do {									\
+	(*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port),			\
+					    IOSP_WRITE_UART_REG(Reg));	\
+	(*(ppBuf))[1] = (Val);						\
+									\
+	*ppBuf += 2;							\
+	*pLen  += 2;							\
+} while (0)
+
+#define MAKE_CMD_EXT_CMD(ppBuf, pLen, Port, ExtCmd, Param)		\
+do {									\
+	(*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port), IOSP_EXT_CMD);	\
+	(*(ppBuf))[1] = (ExtCmd);					\
+	(*(ppBuf))[2] = (Param);					\
+									\
+	*ppBuf += 3;							\
+	*pLen  += 3;							\
+} while (0)
+
+
+
+//--------------------------------------------------------------
+//
+//	Define format of flow control commands
+//	(sent from Host to Edgeport)
+//
+//	11001PPP FlowCmd FlowTypes
+//
+//	Note that the 'FlowTypes' parameter is a bit mask; that is,
+//	more than one flow control type can be active at the same time.
+//	FlowTypes = 0 means 'no flow control'.
+//
+
+//
+//	IOSP_CMD_SET_RX_FLOW
+//
+//	Tells Edgeport how it can stop incoming UART data
+//
+//  Example for Port 0
+//	P0 = 11001000
+//  P1 = IOSP_CMD_SET_RX_FLOW
+//  P2 = Bit mask as follows:
+
+#define IOSP_RX_FLOW_RTS		0x01	// Edgeport drops RTS to stop incoming data
+#define IOSP_RX_FLOW_DTR		0x02	// Edgeport drops DTR to stop incoming data
+#define IOSP_RX_FLOW_DSR_SENSITIVITY	0x04	// Ignores Rx data unless DSR high
+
+// Not currently implemented by firmware.
+#define IOSP_RX_FLOW_XON_XOFF		0x08	// Edgeport sends XOFF char to stop incoming data.
+
+// Host must have previously programmed the
+// XON/XOFF values with SET_XON/SET_XOFF
+// before enabling this bit.
+
+//
+//	IOSP_CMD_SET_TX_FLOW
+//
+//	Tells Edgeport what signal(s) will stop it from transmitting UART data
+//
+//  Example for Port 0
+//	P0 = 11001000
+//  P1 = IOSP_CMD_SET_TX_FLOW
+//  P2 = Bit mask as follows:
+
+#define IOSP_TX_FLOW_CTS		0x01	// Edgeport stops Tx if CTS low
+#define IOSP_TX_FLOW_DSR		0x02	// Edgeport stops Tx if DSR low
+#define IOSP_TX_FLOW_DCD		0x04	// Edgeport stops Tx if DCD low
+#define IOSP_TX_FLOW_XON_XOFF		0x08	// Edgeport stops Tx upon receiving XOFF char.
+
+// Host must have previously programmed the
+// XON/XOFF values with SET_XON/SET_XOFF
+// before enabling this bit.
+#define IOSP_TX_FLOW_XOFF_CONTINUE	0x10	// If not set, Edgeport stops Tx when
+
+// sending XOFF in order to fix broken
+// systems that interpret the next
+// received char as XON.
+// If set, Edgeport continues Tx
+// normally after transmitting XOFF.
+// Not currently implemented by firmware.
+#define IOSP_TX_TOGGLE_RTS		0x20	// Edgeport drives RTS as a true half-duplex
+
+// Request-to-Send signal: it is raised before
+// beginning transmission and lowered after
+// the last Tx char leaves the UART.
+// Not currently implemented by firmware.
+
+//
+//	IOSP_CMD_SET_XON_CHAR
+//
+//	Sets the character which Edgeport transmits/interprets as XON.
+//	Note: This command MUST be sent before sending a SET_RX_FLOW or
+//	SET_TX_FLOW with the XON_XOFF bit set.
+//
+//  Example for Port 0
+//	P0 = 11001000
+//  P1 = IOSP_CMD_SET_XON_CHAR
+//  P2 = 0x11
+
+
+//
+//	IOSP_CMD_SET_XOFF_CHAR
+//
+//	Sets the character which Edgeport transmits/interprets as XOFF.
+//	Note: This command must be sent before sending a SET_RX_FLOW or
+//	SET_TX_FLOW with the XON_XOFF bit set.
+//
+//  Example for Port 0
+//	P0 = 11001000
+//  P1 = IOSP_CMD_SET_XOFF_CHAR
+//  P2 = 0x13
+
+
+//
+//	IOSP_CMD_RX_CHECK_REQ
+//
+//  This command is used to assist in the implementation of the
+//  IOCTL_SERIAL_PURGE Windows IOCTL.
+//  This IOSP command tries to place a marker at the end of the RX
+//  queue in the Edgeport. If the Edgeport RX queue is full then
+//  the Check will be discarded.
+//  It is up to the device driver to timeout waiting for the
+//  RX_CHECK_RSP.  If a RX_CHECK_RSP is received, the driver is
+//	sure that all data has been received from the edgeport and
+//	may now purge any internal RX buffers.
+//  Note tat the sequence numbers may be used to detect lost
+//  CHECK_REQs.
+
+//  Example for Port 0
+//	P0 = 11001000
+//  P1 = IOSP_CMD_RX_CHECK_REQ
+//  P2 = Sequence number
+
+
+//  Response will be:
+//  P1 = IOSP_EXT_RX_CHECK_RSP
+//  P2 = Request Sequence number
+
+
+
+//--------------------------------------------------------------
+//
+//	Define values for status and status parameters
+//	(received by Host from Edgeport)
+//
+//	1ssssPPP P1P1P1P1 [ P2P2P2P2P2 ]...
+//
+//	ssss:	00-07	2-byte status.	ssss identifies which UART register
+//					has changed value, and the new value is in P1.
+//					Note that the ssss values do not correspond to the
+//					16554 register numbers given in 16554.H. Instead,
+//					see below for definitions of the ssss numbers
+//					used in this status message.
+//
+//		08-0B	3-byte status:					==== P1 ====	==== P2 ====
+//					08	LSR_DATA:		New LSR		Errored byte
+//					09	1-param responses	Response Code	Param
+//					0A	OPEN_RSP:		InitialMsr	TxBufferSize
+//					0B	available for expansion
+//
+//		0C-0D	4-byte status.	P1 = extended status code and P2,P3 = params
+//					Not currently implemented.
+//
+//		0E-0F	N-byte status:	P1 = num bytes after P1 (ie, TotalLen - 2)
+//					P2 = extended status, P3..Pn = parameters.
+//					Not currently implemented.
+//
+
+/****************************************************
+ *	SSSS values for 2-byte status messages (0-8)
+ ****************************************************/
+
+#define	IOSP_STATUS_LSR			0x00	// P1 is new value of LSR register.
+
+// Bits defined in 16554.H. Edgeport
+// returns this in order to report
+// line status errors (overrun,
+// parity, framing, break). This form
+// is used when a errored receive data
+// character was NOT present in the
+// UART when the LSR error occurred
+// (ie, when LSR bit 0 = 0).
+
+#define	IOSP_STATUS_MSR			0x01	// P1 is new value of MSR register.
+
+// Bits defined in 16554.H. Edgeport
+// returns this in order to report
+// changes in modem status lines
+// (CTS, DSR, RI, CD)
+//
+
+//					0x02	// Available for future expansion
+//					0x03	//
+//					0x04	//
+//					0x05	//
+//					0x06	//
+//					0x07	//
+
+
+/****************************************************
+ *	SSSS values for 3-byte status messages (8-A)
+ ****************************************************/
+
+#define	IOSP_STATUS_LSR_DATA		0x08	// P1 is new value of LSR register (same as STATUS_LSR)
+
+// P2 is errored character read from
+//    RxFIFO after LSR reported an error.
+
+#define	IOSP_EXT_STATUS			0x09	// P1 is status/response code, param in P2.
+
+
+// Response Codes (P1 values) for 3-byte status messages
+
+#define	IOSP_EXT_STATUS_CHASE_RSP	0	// Reply to CHASE_PORT cmd. P2 is outcome:
+#define	IOSP_EXT_STATUS_CHASE_PASS	0	//	P2 = 0: All Tx data drained successfully
+#define	IOSP_EXT_STATUS_CHASE_FAIL	1	//	P2 = 1: Timed out (stuck due to flow
+
+//			control from remote device).
+
+#define	IOSP_EXT_STATUS_RX_CHECK_RSP	1	// Reply to RX_CHECK cmd. P2 is sequence number
+
+
+#define IOSP_STATUS_OPEN_RSP		0x0A	// Reply to OPEN_PORT cmd.
+
+// P1 is Initial MSR value
+// P2 is encoded TxBuffer Size:
+//	TxBufferSize = (P2 + 1) * 64
+
+//					0x0B	// Available for future expansion
+
+#define GET_TX_BUFFER_SIZE(P2) (((P2) + 1) * 64)
+
+
+
+
+/****************************************************
+ *	SSSS values for 4-byte status messages
+ ****************************************************/
+
+#define IOSP_EXT4_STATUS		0x0C	// Extended status code in P1,
+
+// Params in P2, P3
+// Currently unimplemented.
+
+//					0x0D	// Currently unused, available.
+
+
+
+//
+// Macros to parse status messages
+//
+
+#define	IOSP_GET_STATUS_LEN(code)	((code) < 8 ? 2 : ((code) < 0x0A ? 3 : 4))
+
+#define	IOSP_STATUS_IS_2BYTE(code)	((code) < 0x08)
+#define	IOSP_STATUS_IS_3BYTE(code)	(((code) >= 0x08) && ((code) <= 0x0B))
+#define	IOSP_STATUS_IS_4BYTE(code)	(((code) >= 0x0C) && ((code) <= 0x0D))
+
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
new file mode 100644
index 0000000..6d1d6ef
--- /dev/null
+++ b/drivers/usb/serial/io_ti.c
@@ -0,0 +1,2819 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Edgeport USB Serial Converter driver
+ *
+ * Copyright (C) 2000-2002 Inside Out Networks, All rights reserved.
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Supports the following devices:
+ *	EP/1 EP/2 EP/4 EP/21 EP/22 EP/221 EP/42 EP/421 WATCHPORT
+ *
+ * For questions or problems with this driver, contact Inside Out
+ * Networks technical support, or Peter Berger <pberger@brimson.com>,
+ * or Al Borchers <alborchers@steinerpoint.com>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/serial.h>
+#include <linux/swab.h>
+#include <linux/kfifo.h>
+#include <linux/ioctl.h>
+#include <linux/firmware.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#include "io_16654.h"
+#include "io_usbvend.h"
+#include "io_ti.h"
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
+#define DRIVER_DESC "Edgeport USB Serial Driver"
+
+#define EPROM_PAGE_SIZE		64
+
+
+/* different hardware types */
+#define HARDWARE_TYPE_930	0
+#define HARDWARE_TYPE_TIUMP	1
+
+/* IOCTL_PRIVATE_TI_GET_MODE Definitions */
+#define	TI_MODE_CONFIGURING	0   /* Device has not entered start device */
+#define	TI_MODE_BOOT		1   /* Staying in boot mode		   */
+#define TI_MODE_DOWNLOAD	2   /* Made it to download mode		   */
+#define TI_MODE_TRANSITIONING	3   /*
+				     * Currently in boot mode but
+				     * transitioning to download mode
+				     */
+
+/* read urb state */
+#define EDGE_READ_URB_RUNNING	0
+#define EDGE_READ_URB_STOPPING	1
+#define EDGE_READ_URB_STOPPED	2
+
+#define EDGE_CLOSING_WAIT	4000	/* in .01 sec */
+
+
+/* Product information read from the Edgeport */
+struct product_info {
+	int	TiMode;			/* Current TI Mode  */
+	__u8	hardware_type;		/* Type of hardware */
+} __attribute__((packed));
+
+/*
+ * Edgeport firmware header
+ *
+ * "build_number" has been set to 0 in all three of the images I have
+ * seen, and Digi Tech Support suggests that it is safe to ignore it.
+ *
+ * "length" is the number of bytes of actual data following the header.
+ *
+ * "checksum" is the low order byte resulting from adding the values of
+ * all the data bytes.
+ */
+struct edgeport_fw_hdr {
+	u8 major_version;
+	u8 minor_version;
+	__le16 build_number;
+	__le16 length;
+	u8 checksum;
+} __packed;
+
+struct edgeport_port {
+	__u16 uart_base;
+	__u16 dma_address;
+	__u8 shadow_msr;
+	__u8 shadow_mcr;
+	__u8 shadow_lsr;
+	__u8 lsr_mask;
+	__u32 ump_read_timeout;		/*
+					 * Number of milliseconds the UMP will
+					 * wait without data before completing
+					 * a read short
+					 */
+	int baud_rate;
+	int close_pending;
+	int lsr_event;
+
+	struct edgeport_serial	*edge_serial;
+	struct usb_serial_port	*port;
+	__u8 bUartMode;		/* Port type, 0: RS232, etc. */
+	spinlock_t ep_lock;
+	int ep_read_urb_state;
+	int ep_write_urb_in_use;
+};
+
+struct edgeport_serial {
+	struct product_info product_info;
+	u8 TI_I2C_Type;			/* Type of I2C in UMP */
+	u8 TiReadI2C;			/*
+					 * Set to TRUE if we have read the
+					 * I2c in Boot Mode
+					 */
+	struct mutex es_lock;
+	int num_ports_open;
+	struct usb_serial *serial;
+	struct delayed_work heartbeat_work;
+	int fw_version;
+	bool use_heartbeat;
+};
+
+
+/* Devices that this driver supports */
+static const struct usb_device_id edgeport_1port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) },
+	{ }
+};
+
+static const struct usb_device_id edgeport_2port_id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
+	/* The 4, 8 and 16 port devices show up as multiple 2 port devices */
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) },
+	{ }
+};
+
+/* Devices that this driver supports */
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+static int closing_wait = EDGE_CLOSING_WAIT;
+static bool ignore_cpu_rev;
+static int default_uart_mode;		/* RS232 */
+
+static void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
+		int length);
+
+static void stop_read(struct edgeport_port *edge_port);
+static int restart_read(struct edgeport_port *edge_port);
+
+static void edge_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios);
+static void edge_send(struct usb_serial_port *port, struct tty_struct *tty);
+
+static int do_download_mode(struct edgeport_serial *serial,
+		const struct firmware *fw);
+static int do_boot_mode(struct edgeport_serial *serial,
+		const struct firmware *fw);
+
+/* sysfs attributes */
+static int edge_create_sysfs_attrs(struct usb_serial_port *port);
+static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
+
+/*
+ * Some release of Edgeport firmware "down3.bin" after version 4.80
+ * introduced code to automatically disconnect idle devices on some
+ * Edgeport models after periods of inactivity, typically ~60 seconds.
+ * This occurs without regard to whether ports on the device are open
+ * or not.  Digi International Tech Support suggested:
+ *
+ * 1.  Adding driver "heartbeat" code to reset the firmware timer by
+ *     requesting a descriptor record every 15 seconds, which should be
+ *     effective with newer firmware versions that require it, and benign
+ *     with older versions that do not. In practice 40 seconds seems often
+ *     enough.
+ * 2.  The heartbeat code is currently required only on Edgeport/416 models.
+ */
+#define FW_HEARTBEAT_VERSION_CUTOFF ((4 << 8) + 80)
+#define FW_HEARTBEAT_SECS 40
+
+/* Timeouts in msecs: firmware downloads take longer */
+#define TI_VSEND_TIMEOUT_DEFAULT 1000
+#define TI_VSEND_TIMEOUT_FW_DOWNLOAD 10000
+
+static int ti_vread_sync(struct usb_device *dev, __u8 request,
+				__u16 value, __u16 index, u8 *data, int size)
+{
+	int status;
+
+	status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+			(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN),
+			value, index, data, size, 1000);
+	if (status < 0)
+		return status;
+	if (status != size) {
+		dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n",
+			__func__, size, status);
+		return -ECOMM;
+	}
+	return 0;
+}
+
+static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value,
+		u16 index, u8 *data, int size, int timeout)
+{
+	int status;
+
+	status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+			(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
+			value, index, data, size, timeout);
+	if (status < 0)
+		return status;
+	if (status != size) {
+		dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n",
+			__func__, size, status);
+		return -ECOMM;
+	}
+	return 0;
+}
+
+static int send_cmd(struct usb_device *dev, __u8 command,
+				__u8 moduleid, __u16 value, u8 *data,
+				int size)
+{
+	return ti_vsend_sync(dev, command, value, moduleid, data, size,
+			TI_VSEND_TIMEOUT_DEFAULT);
+}
+
+/* clear tx/rx buffers and fifo in TI UMP */
+static int purge_port(struct usb_serial_port *port, __u16 mask)
+{
+	int port_number = port->port_number;
+
+	dev_dbg(&port->dev, "%s - port %d, mask %x\n", __func__, port_number, mask);
+
+	return send_cmd(port->serial->dev,
+					UMPC_PURGE_PORT,
+					(__u8)(UMPM_UART1_PORT + port_number),
+					mask,
+					NULL,
+					0);
+}
+
+/**
+ * read_download_mem - Read edgeport memory from TI chip
+ * @dev: usb device pointer
+ * @start_address: Device CPU address at which to read
+ * @length: Length of above data
+ * @address_type: Can read both XDATA and I2C
+ * @buffer: pointer to input data buffer
+ */
+static int read_download_mem(struct usb_device *dev, int start_address,
+				int length, __u8 address_type, __u8 *buffer)
+{
+	int status = 0;
+	__u8 read_length;
+	u16 be_start_address;
+
+	dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, length);
+
+	/*
+	 * Read in blocks of 64 bytes
+	 * (TI firmware can't handle more than 64 byte reads)
+	 */
+	while (length) {
+		if (length > 64)
+			read_length = 64;
+		else
+			read_length = (__u8)length;
+
+		if (read_length > 1) {
+			dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, read_length);
+		}
+		/*
+		 * NOTE: Must use swab as wIndex is sent in little-endian
+		 *       byte order regardless of host byte order.
+		 */
+		be_start_address = swab16((u16)start_address);
+		status = ti_vread_sync(dev, UMPC_MEMORY_READ,
+					(__u16)address_type,
+					be_start_address,
+					buffer, read_length);
+
+		if (status) {
+			dev_dbg(&dev->dev, "%s - ERROR %x\n", __func__, status);
+			return status;
+		}
+
+		if (read_length > 1)
+			usb_serial_debug_data(&dev->dev, __func__, read_length, buffer);
+
+		/* Update pointers/length */
+		start_address += read_length;
+		buffer += read_length;
+		length -= read_length;
+	}
+
+	return status;
+}
+
+static int read_ram(struct usb_device *dev, int start_address,
+						int length, __u8 *buffer)
+{
+	return read_download_mem(dev, start_address, length,
+					DTK_ADDR_SPACE_XDATA, buffer);
+}
+
+/* Read edgeport memory to a given block */
+static int read_boot_mem(struct edgeport_serial *serial,
+				int start_address, int length, __u8 *buffer)
+{
+	int status = 0;
+	int i;
+
+	for (i = 0; i < length; i++) {
+		status = ti_vread_sync(serial->serial->dev,
+				UMPC_MEMORY_READ, serial->TI_I2C_Type,
+				(__u16)(start_address+i), &buffer[i], 0x01);
+		if (status) {
+			dev_dbg(&serial->serial->dev->dev, "%s - ERROR %x\n", __func__, status);
+			return status;
+		}
+	}
+
+	dev_dbg(&serial->serial->dev->dev, "%s - start_address = %x, length = %d\n",
+		__func__, start_address, length);
+	usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer);
+
+	serial->TiReadI2C = 1;
+
+	return status;
+}
+
+/* Write given block to TI EPROM memory */
+static int write_boot_mem(struct edgeport_serial *serial,
+				int start_address, int length, __u8 *buffer)
+{
+	int status = 0;
+	int i;
+	u8 *temp;
+
+	/* Must do a read before write */
+	if (!serial->TiReadI2C) {
+		temp = kmalloc(1, GFP_KERNEL);
+		if (!temp)
+			return -ENOMEM;
+
+		status = read_boot_mem(serial, 0, 1, temp);
+		kfree(temp);
+		if (status)
+			return status;
+	}
+
+	for (i = 0; i < length; ++i) {
+		status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+				buffer[i], (u16)(i + start_address), NULL,
+				0, TI_VSEND_TIMEOUT_DEFAULT);
+		if (status)
+			return status;
+	}
+
+	dev_dbg(&serial->serial->dev->dev, "%s - start_sddr = %x, length = %d\n", __func__, start_address, length);
+	usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer);
+
+	return status;
+}
+
+/* Write edgeport I2C memory to TI chip	*/
+static int write_i2c_mem(struct edgeport_serial *serial,
+		int start_address, int length, __u8 address_type, __u8 *buffer)
+{
+	struct device *dev = &serial->serial->dev->dev;
+	int status = 0;
+	int write_length;
+	u16 be_start_address;
+
+	/* We can only send a maximum of 1 aligned byte page at a time */
+
+	/* calculate the number of bytes left in the first page */
+	write_length = EPROM_PAGE_SIZE -
+				(start_address & (EPROM_PAGE_SIZE - 1));
+
+	if (write_length > length)
+		write_length = length;
+
+	dev_dbg(dev, "%s - BytesInFirstPage Addr = %x, length = %d\n",
+		__func__, start_address, write_length);
+	usb_serial_debug_data(dev, __func__, write_length, buffer);
+
+	/*
+	 * Write first page.
+	 *
+	 * NOTE: Must use swab as wIndex is sent in little-endian byte order
+	 *       regardless of host byte order.
+	 */
+	be_start_address = swab16((u16)start_address);
+	status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+				(u16)address_type, be_start_address,
+				buffer,	write_length, TI_VSEND_TIMEOUT_DEFAULT);
+	if (status) {
+		dev_dbg(dev, "%s - ERROR %d\n", __func__, status);
+		return status;
+	}
+
+	length		-= write_length;
+	start_address	+= write_length;
+	buffer		+= write_length;
+
+	/*
+	 * We should be aligned now -- can write max page size bytes at a
+	 * time.
+	 */
+	while (length) {
+		if (length > EPROM_PAGE_SIZE)
+			write_length = EPROM_PAGE_SIZE;
+		else
+			write_length = length;
+
+		dev_dbg(dev, "%s - Page Write Addr = %x, length = %d\n",
+			__func__, start_address, write_length);
+		usb_serial_debug_data(dev, __func__, write_length, buffer);
+
+		/*
+		 * Write next page.
+		 *
+		 * NOTE: Must use swab as wIndex is sent in little-endian byte
+		 *       order regardless of host byte order.
+		 */
+		be_start_address = swab16((u16)start_address);
+		status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+				(u16)address_type, be_start_address, buffer,
+				write_length, TI_VSEND_TIMEOUT_DEFAULT);
+		if (status) {
+			dev_err(dev, "%s - ERROR %d\n", __func__, status);
+			return status;
+		}
+
+		length		-= write_length;
+		start_address	+= write_length;
+		buffer		+= write_length;
+	}
+	return status;
+}
+
+/*
+ * Examine the UMP DMA registers and LSR
+ *
+ * Check the MSBit of the X and Y DMA byte count registers.
+ * A zero in this bit indicates that the TX DMA buffers are empty
+ * then check the TX Empty bit in the UART.
+ */
+static int tx_active(struct edgeport_port *port)
+{
+	int status;
+	struct out_endpoint_desc_block *oedb;
+	__u8 *lsr;
+	int bytes_left = 0;
+
+	oedb = kmalloc(sizeof(*oedb), GFP_KERNEL);
+	if (!oedb)
+		return -ENOMEM;
+
+	/*
+	 * Sigh, that's right, just one byte, as not all platforms can
+	 * do DMA from stack
+	 */
+	lsr = kmalloc(1, GFP_KERNEL);
+	if (!lsr) {
+		kfree(oedb);
+		return -ENOMEM;
+	}
+	/* Read the DMA Count Registers */
+	status = read_ram(port->port->serial->dev, port->dma_address,
+						sizeof(*oedb), (void *)oedb);
+	if (status)
+		goto exit_is_tx_active;
+
+	dev_dbg(&port->port->dev, "%s - XByteCount    0x%X\n", __func__, oedb->XByteCount);
+
+	/* and the LSR */
+	status = read_ram(port->port->serial->dev,
+			port->uart_base + UMPMEM_OFFS_UART_LSR, 1, lsr);
+
+	if (status)
+		goto exit_is_tx_active;
+	dev_dbg(&port->port->dev, "%s - LSR = 0x%X\n", __func__, *lsr);
+
+	/* If either buffer has data or we are transmitting then return TRUE */
+	if ((oedb->XByteCount & 0x80) != 0)
+		bytes_left += 64;
+
+	if ((*lsr & UMP_UART_LSR_TX_MASK) == 0)
+		bytes_left += 1;
+
+	/* We return Not Active if we get any kind of error */
+exit_is_tx_active:
+	dev_dbg(&port->port->dev, "%s - return %d\n", __func__, bytes_left);
+
+	kfree(lsr);
+	kfree(oedb);
+	return bytes_left;
+}
+
+static int choose_config(struct usb_device *dev)
+{
+	/*
+	 * There may be multiple configurations on this device, in which case
+	 * we would need to read and parse all of them to find out which one
+	 * we want. However, we just support one config at this point,
+	 * configuration # 1, which is Config Descriptor 0.
+	 */
+
+	dev_dbg(&dev->dev, "%s - Number of Interfaces = %d\n",
+		__func__, dev->config->desc.bNumInterfaces);
+	dev_dbg(&dev->dev, "%s - MAX Power            = %d\n",
+		__func__, dev->config->desc.bMaxPower * 2);
+
+	if (dev->config->desc.bNumInterfaces != 1) {
+		dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int read_rom(struct edgeport_serial *serial,
+				int start_address, int length, __u8 *buffer)
+{
+	int status;
+
+	if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) {
+		status = read_download_mem(serial->serial->dev,
+					       start_address,
+					       length,
+					       serial->TI_I2C_Type,
+					       buffer);
+	} else {
+		status = read_boot_mem(serial, start_address, length,
+								buffer);
+	}
+	return status;
+}
+
+static int write_rom(struct edgeport_serial *serial, int start_address,
+						int length, __u8 *buffer)
+{
+	if (serial->product_info.TiMode == TI_MODE_BOOT)
+		return write_boot_mem(serial, start_address, length,
+								buffer);
+
+	if (serial->product_info.TiMode == TI_MODE_DOWNLOAD)
+		return write_i2c_mem(serial, start_address, length,
+						serial->TI_I2C_Type, buffer);
+	return -EINVAL;
+}
+
+/* Read a descriptor header from I2C based on type */
+static int get_descriptor_addr(struct edgeport_serial *serial,
+				int desc_type, struct ti_i2c_desc *rom_desc)
+{
+	int start_address;
+	int status;
+
+	/* Search for requested descriptor in I2C */
+	start_address = 2;
+	do {
+		status = read_rom(serial,
+				   start_address,
+				   sizeof(struct ti_i2c_desc),
+				   (__u8 *)rom_desc);
+		if (status)
+			return 0;
+
+		if (rom_desc->Type == desc_type)
+			return start_address;
+
+		start_address = start_address + sizeof(struct ti_i2c_desc) +
+						le16_to_cpu(rom_desc->Size);
+
+	} while ((start_address < TI_MAX_I2C_SIZE) && rom_desc->Type);
+
+	return 0;
+}
+
+/* Validate descriptor checksum */
+static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer)
+{
+	__u16 i;
+	__u8 cs = 0;
+
+	for (i = 0; i < le16_to_cpu(rom_desc->Size); i++)
+		cs = (__u8)(cs + buffer[i]);
+
+	if (cs != rom_desc->CheckSum) {
+		pr_debug("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* Make sure that the I2C image is good */
+static int check_i2c_image(struct edgeport_serial *serial)
+{
+	struct device *dev = &serial->serial->dev->dev;
+	int status = 0;
+	struct ti_i2c_desc *rom_desc;
+	int start_address = 2;
+	__u8 *buffer;
+	__u16 ttype;
+
+	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+	if (!rom_desc)
+		return -ENOMEM;
+
+	buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL);
+	if (!buffer) {
+		kfree(rom_desc);
+		return -ENOMEM;
+	}
+
+	/* Read the first byte (Signature0) must be 0x52 or 0x10 */
+	status = read_rom(serial, 0, 1, buffer);
+	if (status)
+		goto out;
+
+	if (*buffer != UMP5152 && *buffer != UMP3410) {
+		dev_err(dev, "%s - invalid buffer signature\n", __func__);
+		status = -ENODEV;
+		goto out;
+	}
+
+	do {
+		/* Validate the I2C */
+		status = read_rom(serial,
+				start_address,
+				sizeof(struct ti_i2c_desc),
+				(__u8 *)rom_desc);
+		if (status)
+			break;
+
+		if ((start_address + sizeof(struct ti_i2c_desc) +
+			le16_to_cpu(rom_desc->Size)) > TI_MAX_I2C_SIZE) {
+			status = -ENODEV;
+			dev_dbg(dev, "%s - structure too big, erroring out.\n", __func__);
+			break;
+		}
+
+		dev_dbg(dev, "%s Type = 0x%x\n", __func__, rom_desc->Type);
+
+		/* Skip type 2 record */
+		ttype = rom_desc->Type & 0x0f;
+		if (ttype != I2C_DESC_TYPE_FIRMWARE_BASIC
+			&& ttype != I2C_DESC_TYPE_FIRMWARE_AUTO) {
+			/* Read the descriptor data */
+			status = read_rom(serial, start_address +
+						sizeof(struct ti_i2c_desc),
+						le16_to_cpu(rom_desc->Size),
+						buffer);
+			if (status)
+				break;
+
+			status = valid_csum(rom_desc, buffer);
+			if (status)
+				break;
+		}
+		start_address = start_address + sizeof(struct ti_i2c_desc) +
+						le16_to_cpu(rom_desc->Size);
+
+	} while ((rom_desc->Type != I2C_DESC_TYPE_ION) &&
+				(start_address < TI_MAX_I2C_SIZE));
+
+	if ((rom_desc->Type != I2C_DESC_TYPE_ION) ||
+				(start_address > TI_MAX_I2C_SIZE))
+		status = -ENODEV;
+
+out:
+	kfree(buffer);
+	kfree(rom_desc);
+	return status;
+}
+
+static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer)
+{
+	int status;
+	int start_address;
+	struct ti_i2c_desc *rom_desc;
+	struct edge_ti_manuf_descriptor *desc;
+	struct device *dev = &serial->serial->dev->dev;
+
+	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+	if (!rom_desc)
+		return -ENOMEM;
+
+	start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
+								rom_desc);
+
+	if (!start_address) {
+		dev_dbg(dev, "%s - Edge Descriptor not found in I2C\n", __func__);
+		status = -ENODEV;
+		goto exit;
+	}
+
+	/* Read the descriptor data */
+	status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc),
+					le16_to_cpu(rom_desc->Size), buffer);
+	if (status)
+		goto exit;
+
+	status = valid_csum(rom_desc, buffer);
+
+	desc = (struct edge_ti_manuf_descriptor *)buffer;
+	dev_dbg(dev, "%s - IonConfig      0x%x\n", __func__, desc->IonConfig);
+	dev_dbg(dev, "%s - Version          %d\n", __func__, desc->Version);
+	dev_dbg(dev, "%s - Cpu/Board      0x%x\n", __func__, desc->CpuRev_BoardRev);
+	dev_dbg(dev, "%s - NumPorts         %d\n", __func__, desc->NumPorts);
+	dev_dbg(dev, "%s - NumVirtualPorts  %d\n", __func__, desc->NumVirtualPorts);
+	dev_dbg(dev, "%s - TotalPorts       %d\n", __func__, desc->TotalPorts);
+
+exit:
+	kfree(rom_desc);
+	return status;
+}
+
+/* Build firmware header used for firmware update */
+static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw)
+{
+	__u8 *buffer;
+	int buffer_size;
+	int i;
+	__u8 cs = 0;
+	struct ti_i2c_desc *i2c_header;
+	struct ti_i2c_image_header *img_header;
+	struct ti_i2c_firmware_rec *firmware_rec;
+	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+	/*
+	 * In order to update the I2C firmware we must change the type 2 record
+	 * to type 0xF2.  This will force the UMP to come up in Boot Mode.
+	 * Then while in boot mode, the driver will download the latest
+	 * firmware (padded to 15.5k) into the UMP ram.  And finally when the
+	 * device comes back up in download mode the driver will cause the new
+	 * firmware to be copied from the UMP Ram to I2C and the firmware will
+	 * update the record type from 0xf2 to 0x02.
+	 */
+
+	/*
+	 * Allocate a 15.5k buffer + 2 bytes for version number (Firmware
+	 * Record)
+	 */
+	buffer_size = (((1024 * 16) - 512 ) +
+			sizeof(struct ti_i2c_firmware_rec));
+
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Set entire image of 0xffs */
+	memset(buffer, 0xff, buffer_size);
+
+	/* Copy version number into firmware record */
+	firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
+
+	firmware_rec->Ver_Major	= fw_hdr->major_version;
+	firmware_rec->Ver_Minor	= fw_hdr->minor_version;
+
+	/* Pointer to fw_down memory image */
+	img_header = (struct ti_i2c_image_header *)&fw->data[4];
+
+	memcpy(buffer + sizeof(struct ti_i2c_firmware_rec),
+		&fw->data[4 + sizeof(struct ti_i2c_image_header)],
+		le16_to_cpu(img_header->Length));
+
+	for (i=0; i < buffer_size; i++) {
+		cs = (__u8)(cs + buffer[i]);
+	}
+
+	kfree(buffer);
+
+	/* Build new header */
+	i2c_header =  (struct ti_i2c_desc *)header;
+	firmware_rec =  (struct ti_i2c_firmware_rec*)i2c_header->Data;
+
+	i2c_header->Type	= I2C_DESC_TYPE_FIRMWARE_BLANK;
+	i2c_header->Size	= cpu_to_le16(buffer_size);
+	i2c_header->CheckSum	= cs;
+	firmware_rec->Ver_Major	= fw_hdr->major_version;
+	firmware_rec->Ver_Minor	= fw_hdr->minor_version;
+
+	return 0;
+}
+
+/* Try to figure out what type of I2c we have */
+static int i2c_type_bootmode(struct edgeport_serial *serial)
+{
+	struct device *dev = &serial->serial->dev->dev;
+	int status;
+	u8 *data;
+
+	data = kmalloc(1, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* Try to read type 2 */
+	status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
+				DTK_ADDR_SPACE_I2C_TYPE_II, 0, data, 0x01);
+	if (status)
+		dev_dbg(dev, "%s - read 2 status error = %d\n", __func__, status);
+	else
+		dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data);
+	if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
+		dev_dbg(dev, "%s - ROM_TYPE_II\n", __func__);
+		serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
+		goto out;
+	}
+
+	/* Try to read type 3 */
+	status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
+				DTK_ADDR_SPACE_I2C_TYPE_III, 0,	data, 0x01);
+	if (status)
+		dev_dbg(dev, "%s - read 3 status error = %d\n", __func__, status);
+	else
+		dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data);
+	if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
+		dev_dbg(dev, "%s - ROM_TYPE_III\n", __func__);
+		serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III;
+		goto out;
+	}
+
+	dev_dbg(dev, "%s - Unknown\n", __func__);
+	serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
+	status = -ENODEV;
+out:
+	kfree(data);
+	return status;
+}
+
+static int bulk_xfer(struct usb_serial *serial, void *buffer,
+						int length, int *num_sent)
+{
+	int status;
+
+	status = usb_bulk_msg(serial->dev,
+			usb_sndbulkpipe(serial->dev,
+				serial->port[0]->bulk_out_endpointAddress),
+			buffer, length, num_sent, 1000);
+	return status;
+}
+
+/* Download given firmware image to the device (IN BOOT MODE) */
+static int download_code(struct edgeport_serial *serial, __u8 *image,
+							int image_length)
+{
+	int status = 0;
+	int pos;
+	int transfer;
+	int done;
+
+	/* Transfer firmware image */
+	for (pos = 0; pos < image_length; ) {
+		/* Read the next buffer from file */
+		transfer = image_length - pos;
+		if (transfer > EDGE_FW_BULK_MAX_PACKET_SIZE)
+			transfer = EDGE_FW_BULK_MAX_PACKET_SIZE;
+
+		/* Transfer data */
+		status = bulk_xfer(serial->serial, &image[pos],
+							transfer, &done);
+		if (status)
+			break;
+		/* Advance buffer pointer */
+		pos += done;
+	}
+
+	return status;
+}
+
+/* FIXME!!! */
+static int config_boot_dev(struct usb_device *dev)
+{
+	return 0;
+}
+
+static int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc)
+{
+	return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev);
+}
+
+static int check_fw_sanity(struct edgeport_serial *serial,
+		const struct firmware *fw)
+{
+	u16 length_total;
+	u8 checksum = 0;
+	int pos;
+	struct device *dev = &serial->serial->interface->dev;
+	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+	if (fw->size < sizeof(struct edgeport_fw_hdr)) {
+		dev_err(dev, "incomplete fw header\n");
+		return -EINVAL;
+	}
+
+	length_total = le16_to_cpu(fw_hdr->length) +
+			sizeof(struct edgeport_fw_hdr);
+
+	if (fw->size != length_total) {
+		dev_err(dev, "bad fw size (expected: %u, got: %zu)\n",
+				length_total, fw->size);
+		return -EINVAL;
+	}
+
+	for (pos = sizeof(struct edgeport_fw_hdr); pos < fw->size; ++pos)
+		checksum += fw->data[pos];
+
+	if (checksum != fw_hdr->checksum) {
+		dev_err(dev, "bad fw checksum (expected: 0x%x, got: 0x%x)\n",
+				fw_hdr->checksum, checksum);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * DownloadTIFirmware - Download run-time operating firmware to the TI5052
+ *
+ * This routine downloads the main operating code into the TI5052, using the
+ * boot code already burned into E2PROM or ROM.
+ */
+static int download_fw(struct edgeport_serial *serial)
+{
+	struct device *dev = &serial->serial->interface->dev;
+	int status = 0;
+	struct usb_interface_descriptor *interface;
+	const struct firmware *fw;
+	const char *fw_name = "edgeport/down3.bin";
+	struct edgeport_fw_hdr *fw_hdr;
+
+	status = request_firmware(&fw, fw_name, dev);
+	if (status) {
+		dev_err(dev, "Failed to load image \"%s\" err %d\n",
+				fw_name, status);
+		return status;
+	}
+
+	if (check_fw_sanity(serial, fw)) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+	/* If on-board version is newer, "fw_version" will be updated later. */
+	serial->fw_version = (fw_hdr->major_version << 8) +
+			fw_hdr->minor_version;
+
+	/*
+	 * This routine is entered by both the BOOT mode and the Download mode
+	 * We can determine which code is running by the reading the config
+	 * descriptor and if we have only one bulk pipe it is in boot mode
+	 */
+	serial->product_info.hardware_type = HARDWARE_TYPE_TIUMP;
+
+	/* Default to type 2 i2c */
+	serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
+
+	status = choose_config(serial->serial->dev);
+	if (status)
+		goto out;
+
+	interface = &serial->serial->interface->cur_altsetting->desc;
+	if (!interface) {
+		dev_err(dev, "%s - no interface set, error!\n", __func__);
+		status = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING
+	 * if we have more than one endpoint we are definitely in download
+	 * mode
+	 */
+	if (interface->bNumEndpoints > 1) {
+		serial->product_info.TiMode = TI_MODE_DOWNLOAD;
+		status = do_download_mode(serial, fw);
+	} else {
+		/* Otherwise we will remain in configuring mode */
+		serial->product_info.TiMode = TI_MODE_CONFIGURING;
+		status = do_boot_mode(serial, fw);
+	}
+
+out:
+	release_firmware(fw);
+	return status;
+}
+
+static int do_download_mode(struct edgeport_serial *serial,
+		const struct firmware *fw)
+{
+	struct device *dev = &serial->serial->interface->dev;
+	int status = 0;
+	int start_address;
+	struct edge_ti_manuf_descriptor *ti_manuf_desc;
+	int download_cur_ver;
+	int download_new_ver;
+	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+	struct ti_i2c_desc *rom_desc;
+
+	dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__);
+
+	status = check_i2c_image(serial);
+	if (status) {
+		dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__);
+		return status;
+	}
+
+	/*
+	 * Validate Hardware version number
+	 * Read Manufacturing Descriptor from TI Based Edgeport
+	 */
+	ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
+	if (!ti_manuf_desc)
+		return -ENOMEM;
+
+	status = get_manuf_info(serial, (__u8 *)ti_manuf_desc);
+	if (status) {
+		kfree(ti_manuf_desc);
+		return status;
+	}
+
+	/* Check version number of ION descriptor */
+	if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
+		dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
+			__func__, ti_cpu_rev(ti_manuf_desc));
+		kfree(ti_manuf_desc);
+		return -EINVAL;
+	}
+
+	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+	if (!rom_desc) {
+		kfree(ti_manuf_desc);
+		return -ENOMEM;
+	}
+
+	/* Search for type 2 record (firmware record) */
+	start_address = get_descriptor_addr(serial,
+			I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc);
+	if (start_address != 0) {
+		struct ti_i2c_firmware_rec *firmware_version;
+		u8 *record;
+
+		dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n",
+				__func__);
+
+		firmware_version = kmalloc(sizeof(*firmware_version),
+							GFP_KERNEL);
+		if (!firmware_version) {
+			kfree(rom_desc);
+			kfree(ti_manuf_desc);
+			return -ENOMEM;
+		}
+
+		/*
+		 * Validate version number
+		 * Read the descriptor data
+		 */
+		status = read_rom(serial, start_address +
+				sizeof(struct ti_i2c_desc),
+				sizeof(struct ti_i2c_firmware_rec),
+				(__u8 *)firmware_version);
+		if (status) {
+			kfree(firmware_version);
+			kfree(rom_desc);
+			kfree(ti_manuf_desc);
+			return status;
+		}
+
+		/*
+		 * Check version number of download with current
+		 * version in I2c
+		 */
+		download_cur_ver = (firmware_version->Ver_Major << 8) +
+				   (firmware_version->Ver_Minor);
+		download_new_ver = (fw_hdr->major_version << 8) +
+				   (fw_hdr->minor_version);
+
+		dev_dbg(dev, "%s - >> FW Versions Device %d.%d  Driver %d.%d\n",
+			__func__, firmware_version->Ver_Major,
+			firmware_version->Ver_Minor,
+			fw_hdr->major_version, fw_hdr->minor_version);
+
+		/*
+		 * Check if we have an old version in the I2C and
+		 * update if necessary
+		 */
+		if (download_cur_ver < download_new_ver) {
+			dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n",
+				__func__,
+				firmware_version->Ver_Major,
+				firmware_version->Ver_Minor,
+				fw_hdr->major_version,
+				fw_hdr->minor_version);
+
+			record = kmalloc(1, GFP_KERNEL);
+			if (!record) {
+				kfree(firmware_version);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENOMEM;
+			}
+			/*
+			 * In order to update the I2C firmware we must
+			 * change the type 2 record to type 0xF2. This
+			 * will force the UMP to come up in Boot Mode.
+			 * Then while in boot mode, the driver will
+			 * download the latest firmware (padded to
+			 * 15.5k) into the UMP ram. Finally when the
+			 * device comes back up in download mode the
+			 * driver will cause the new firmware to be
+			 * copied from the UMP Ram to I2C and the
+			 * firmware will update the record type from
+			 * 0xf2 to 0x02.
+			 */
+			*record = I2C_DESC_TYPE_FIRMWARE_BLANK;
+
+			/*
+			 * Change the I2C Firmware record type to
+			 * 0xf2 to trigger an update
+			 */
+			status = write_rom(serial, start_address,
+					sizeof(*record), record);
+			if (status) {
+				kfree(record);
+				kfree(firmware_version);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return status;
+			}
+
+			/*
+			 * verify the write -- must do this in order
+			 * for write to complete before we do the
+			 * hardware reset
+			 */
+			status = read_rom(serial,
+						start_address,
+						sizeof(*record),
+						record);
+			if (status) {
+				kfree(record);
+				kfree(firmware_version);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return status;
+			}
+
+			if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) {
+				dev_err(dev, "%s - error resetting device\n",
+						__func__);
+				kfree(record);
+				kfree(firmware_version);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENODEV;
+			}
+
+			dev_dbg(dev, "%s - HARDWARE RESET\n", __func__);
+
+			/* Reset UMP -- Back to BOOT MODE */
+			status = ti_vsend_sync(serial->serial->dev,
+					UMPC_HARDWARE_RESET,
+					0, 0, NULL, 0,
+					TI_VSEND_TIMEOUT_DEFAULT);
+
+			dev_dbg(dev, "%s - HARDWARE RESET return %d\n",
+					__func__, status);
+
+			/* return an error on purpose. */
+			kfree(record);
+			kfree(firmware_version);
+			kfree(rom_desc);
+			kfree(ti_manuf_desc);
+			return -ENODEV;
+		}
+		/* Same or newer fw version is already loaded */
+		serial->fw_version = download_cur_ver;
+		kfree(firmware_version);
+	}
+	/* Search for type 0xF2 record (firmware blank record) */
+	else {
+		start_address = get_descriptor_addr(serial,
+				I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc);
+		if (start_address != 0) {
+#define HEADER_SIZE	(sizeof(struct ti_i2c_desc) + \
+				sizeof(struct ti_i2c_firmware_rec))
+			__u8 *header;
+			__u8 *vheader;
+
+			header = kmalloc(HEADER_SIZE, GFP_KERNEL);
+			if (!header) {
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENOMEM;
+			}
+
+			vheader = kmalloc(HEADER_SIZE, GFP_KERNEL);
+			if (!vheader) {
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENOMEM;
+			}
+
+			dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n",
+					__func__);
+
+			/*
+			 * In order to update the I2C firmware we must change
+			 * the type 2 record to type 0xF2. This will force the
+			 * UMP to come up in Boot Mode.  Then while in boot
+			 * mode, the driver will download the latest firmware
+			 * (padded to 15.5k) into the UMP ram. Finally when the
+			 * device comes back up in download mode the driver
+			 * will cause the new firmware to be copied from the
+			 * UMP Ram to I2C and the firmware will update the
+			 * record type from 0xf2 to 0x02.
+			 */
+			status = build_i2c_fw_hdr(header, fw);
+			if (status) {
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -EINVAL;
+			}
+
+			/*
+			 * Update I2C with type 0xf2 record with correct
+			 * size and checksum
+			 */
+			status = write_rom(serial,
+						start_address,
+						HEADER_SIZE,
+						header);
+			if (status) {
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -EINVAL;
+			}
+
+			/*
+			 * verify the write -- must do this in order for
+			 * write to complete before we do the hardware reset
+			 */
+			status = read_rom(serial, start_address,
+							HEADER_SIZE, vheader);
+
+			if (status) {
+				dev_dbg(dev, "%s - can't read header back\n",
+						__func__);
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return status;
+			}
+			if (memcmp(vheader, header, HEADER_SIZE)) {
+				dev_dbg(dev, "%s - write download record failed\n",
+						__func__);
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -EINVAL;
+			}
+
+			kfree(vheader);
+			kfree(header);
+
+			dev_dbg(dev, "%s - Start firmware update\n", __func__);
+
+			/* Tell firmware to copy download image into I2C */
+			status = ti_vsend_sync(serial->serial->dev,
+					UMPC_COPY_DNLD_TO_I2C,
+					0, 0, NULL, 0,
+					TI_VSEND_TIMEOUT_FW_DOWNLOAD);
+
+			dev_dbg(dev, "%s - Update complete 0x%x\n", __func__,
+					status);
+			if (status) {
+				dev_err(dev,
+					"%s - UMPC_COPY_DNLD_TO_I2C failed\n",
+					__func__);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return status;
+			}
+		}
+	}
+
+	/* The device is running the download code */
+	kfree(rom_desc);
+	kfree(ti_manuf_desc);
+	return 0;
+}
+
+static int do_boot_mode(struct edgeport_serial *serial,
+		const struct firmware *fw)
+{
+	struct device *dev = &serial->serial->interface->dev;
+	int status = 0;
+	struct edge_ti_manuf_descriptor *ti_manuf_desc;
+	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+	dev_dbg(dev, "%s - RUNNING IN BOOT MODE\n", __func__);
+
+	/* Configure the TI device so we can use the BULK pipes for download */
+	status = config_boot_dev(serial->serial->dev);
+	if (status)
+		return status;
+
+	if (le16_to_cpu(serial->serial->dev->descriptor.idVendor)
+							!= USB_VENDOR_ID_ION) {
+		dev_dbg(dev, "%s - VID = 0x%x\n", __func__,
+			le16_to_cpu(serial->serial->dev->descriptor.idVendor));
+		serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
+		goto stayinbootmode;
+	}
+
+	/*
+	 * We have an ION device (I2c Must be programmed)
+	 * Determine I2C image type
+	 */
+	if (i2c_type_bootmode(serial))
+		goto stayinbootmode;
+
+	/* Check for ION Vendor ID and that the I2C is valid */
+	if (!check_i2c_image(serial)) {
+		struct ti_i2c_image_header *header;
+		int i;
+		__u8 cs = 0;
+		__u8 *buffer;
+		int buffer_size;
+
+		/*
+		 * Validate Hardware version number
+		 * Read Manufacturing Descriptor from TI Based Edgeport
+		 */
+		ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
+		if (!ti_manuf_desc)
+			return -ENOMEM;
+
+		status = get_manuf_info(serial, (__u8 *)ti_manuf_desc);
+		if (status) {
+			kfree(ti_manuf_desc);
+			goto stayinbootmode;
+		}
+
+		/* Check for version 2 */
+		if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
+			dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
+				__func__, ti_cpu_rev(ti_manuf_desc));
+			kfree(ti_manuf_desc);
+			goto stayinbootmode;
+		}
+
+		kfree(ti_manuf_desc);
+
+		/*
+		 * In order to update the I2C firmware we must change the type
+		 * 2 record to type 0xF2. This will force the UMP to come up
+		 * in Boot Mode.  Then while in boot mode, the driver will
+		 * download the latest firmware (padded to 15.5k) into the
+		 * UMP ram. Finally when the device comes back up in download
+		 * mode the driver will cause the new firmware to be copied
+		 * from the UMP Ram to I2C and the firmware will update the
+		 * record type from 0xf2 to 0x02.
+		 *
+		 * Do we really have to copy the whole firmware image,
+		 * or could we do this in place!
+		 */
+
+		/* Allocate a 15.5k buffer + 3 byte header */
+		buffer_size = (((1024 * 16) - 512) +
+					sizeof(struct ti_i2c_image_header));
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (!buffer)
+			return -ENOMEM;
+
+		/* Initialize the buffer to 0xff (pad the buffer) */
+		memset(buffer, 0xff, buffer_size);
+		memcpy(buffer, &fw->data[4], fw->size - 4);
+
+		for (i = sizeof(struct ti_i2c_image_header);
+				i < buffer_size; i++) {
+			cs = (__u8)(cs + buffer[i]);
+		}
+
+		header = (struct ti_i2c_image_header *)buffer;
+
+		/* update length and checksum after padding */
+		header->Length 	 = cpu_to_le16((__u16)(buffer_size -
+					sizeof(struct ti_i2c_image_header)));
+		header->CheckSum = cs;
+
+		/* Download the operational code  */
+		dev_dbg(dev, "%s - Downloading operational code image version %d.%d (TI UMP)\n",
+				__func__,
+				fw_hdr->major_version, fw_hdr->minor_version);
+		status = download_code(serial, buffer, buffer_size);
+
+		kfree(buffer);
+
+		if (status) {
+			dev_dbg(dev, "%s - Error downloading operational code image\n", __func__);
+			return status;
+		}
+
+		/* Device will reboot */
+		serial->product_info.TiMode = TI_MODE_TRANSITIONING;
+
+		dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
+
+		return 1;
+	}
+
+stayinbootmode:
+	/* Eprom is invalid or blank stay in boot mode */
+	dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
+	serial->product_info.TiMode = TI_MODE_BOOT;
+
+	return 1;
+}
+
+static int ti_do_config(struct edgeport_port *port, int feature, int on)
+{
+	int port_number = port->port->port_number;
+
+	on = !!on;	/* 1 or 0 not bitmask */
+	return send_cmd(port->port->serial->dev,
+			feature, (__u8)(UMPM_UART1_PORT + port_number),
+			on, NULL, 0);
+}
+
+static int restore_mcr(struct edgeport_port *port, __u8 mcr)
+{
+	int status = 0;
+
+	dev_dbg(&port->port->dev, "%s - %x\n", __func__, mcr);
+
+	status = ti_do_config(port, UMPC_SET_CLR_DTR, mcr & MCR_DTR);
+	if (status)
+		return status;
+	status = ti_do_config(port, UMPC_SET_CLR_RTS, mcr & MCR_RTS);
+	if (status)
+		return status;
+	return ti_do_config(port, UMPC_SET_CLR_LOOPBACK, mcr & MCR_LOOPBACK);
+}
+
+/* Convert TI LSR to standard UART flags */
+static __u8 map_line_status(__u8 ti_lsr)
+{
+	__u8 lsr = 0;
+
+#define MAP_FLAG(flagUmp, flagUart)    \
+	if (ti_lsr & flagUmp) \
+		lsr |= flagUart;
+
+	MAP_FLAG(UMP_UART_LSR_OV_MASK, LSR_OVER_ERR)	/* overrun */
+	MAP_FLAG(UMP_UART_LSR_PE_MASK, LSR_PAR_ERR)	/* parity error */
+	MAP_FLAG(UMP_UART_LSR_FE_MASK, LSR_FRM_ERR)	/* framing error */
+	MAP_FLAG(UMP_UART_LSR_BR_MASK, LSR_BREAK)	/* break detected */
+	MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL)	/* rx data available */
+	MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY)	/* tx hold reg empty */
+
+#undef MAP_FLAG
+
+	return lsr;
+}
+
+static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)
+{
+	struct async_icount *icount;
+	struct tty_struct *tty;
+
+	dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, msr);
+
+	if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
+			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
+		icount = &edge_port->port->icount;
+
+		/* update input line counters */
+		if (msr & EDGEPORT_MSR_DELTA_CTS)
+			icount->cts++;
+		if (msr & EDGEPORT_MSR_DELTA_DSR)
+			icount->dsr++;
+		if (msr & EDGEPORT_MSR_DELTA_CD)
+			icount->dcd++;
+		if (msr & EDGEPORT_MSR_DELTA_RI)
+			icount->rng++;
+		wake_up_interruptible(&edge_port->port->port.delta_msr_wait);
+	}
+
+	/* Save the new modem status */
+	edge_port->shadow_msr = msr & 0xf0;
+
+	tty = tty_port_tty_get(&edge_port->port->port);
+	/* handle CTS flow control */
+	if (tty && C_CRTSCTS(tty)) {
+		if (msr & EDGEPORT_MSR_CTS)
+			tty_wakeup(tty);
+	}
+	tty_kref_put(tty);
+}
+
+static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data,
+							__u8 lsr, __u8 data)
+{
+	struct async_icount *icount;
+	__u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR |
+						LSR_FRM_ERR | LSR_BREAK));
+
+	dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, new_lsr);
+
+	edge_port->shadow_lsr = lsr;
+
+	if (new_lsr & LSR_BREAK)
+		/*
+		 * Parity and Framing errors only count if they
+		 * occur exclusive of a break being received.
+		 */
+		new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
+
+	/* Place LSR data byte into Rx buffer */
+	if (lsr_data)
+		edge_tty_recv(edge_port->port, &data, 1);
+
+	/* update input line counters */
+	icount = &edge_port->port->icount;
+	if (new_lsr & LSR_BREAK)
+		icount->brk++;
+	if (new_lsr & LSR_OVER_ERR)
+		icount->overrun++;
+	if (new_lsr & LSR_PAR_ERR)
+		icount->parity++;
+	if (new_lsr & LSR_FRM_ERR)
+		icount->frame++;
+}
+
+static void edge_interrupt_callback(struct urb *urb)
+{
+	struct edgeport_serial *edge_serial = urb->context;
+	struct usb_serial_port *port;
+	struct edgeport_port *edge_port;
+	struct device *dev;
+	unsigned char *data = urb->transfer_buffer;
+	int length = urb->actual_length;
+	int port_number;
+	int function;
+	int retval;
+	__u8 lsr;
+	__u8 msr;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+		    __func__, status);
+		return;
+	default:
+		dev_err(&urb->dev->dev, "%s - nonzero urb status received: "
+			"%d\n", __func__, status);
+		goto exit;
+	}
+
+	if (!length) {
+		dev_dbg(&urb->dev->dev, "%s - no data in urb\n", __func__);
+		goto exit;
+	}
+
+	dev = &edge_serial->serial->dev->dev;
+	usb_serial_debug_data(dev, __func__, length, data);
+
+	if (length != 2) {
+		dev_dbg(dev, "%s - expecting packet of size 2, got %d\n", __func__, length);
+		goto exit;
+	}
+
+	port_number = TIUMP_GET_PORT_FROM_CODE(data[0]);
+	function    = TIUMP_GET_FUNC_FROM_CODE(data[0]);
+	dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
+		port_number, function, data[1]);
+
+	if (port_number >= edge_serial->serial->num_ports) {
+		dev_err(dev, "bad port number %d\n", port_number);
+		goto exit;
+	}
+
+	port = edge_serial->serial->port[port_number];
+	edge_port = usb_get_serial_port_data(port);
+	if (!edge_port) {
+		dev_dbg(dev, "%s - edge_port not found\n", __func__);
+		return;
+	}
+	switch (function) {
+	case TIUMP_INTERRUPT_CODE_LSR:
+		lsr = map_line_status(data[1]);
+		if (lsr & UMP_UART_LSR_DATA_MASK) {
+			/*
+			 * Save the LSR event for bulk read completion routine
+			 */
+			dev_dbg(dev, "%s - LSR Event Port %u LSR Status = %02x\n",
+				__func__, port_number, lsr);
+			edge_port->lsr_event = 1;
+			edge_port->lsr_mask = lsr;
+		} else {
+			dev_dbg(dev, "%s - ===== Port %d LSR Status = %02x ======\n",
+				__func__, port_number, lsr);
+			handle_new_lsr(edge_port, 0, lsr, 0);
+		}
+		break;
+
+	case TIUMP_INTERRUPT_CODE_MSR:	/* MSR */
+		/* Copy MSR from UMP */
+		msr = data[1];
+		dev_dbg(dev, "%s - ===== Port %u MSR Status = %02x ======\n",
+			__func__, port_number, msr);
+		handle_new_msr(edge_port, msr);
+		break;
+
+	default:
+		dev_err(&urb->dev->dev,
+			"%s - Unknown Interrupt code from UMP %x\n",
+			__func__, data[1]);
+		break;
+
+	}
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&urb->dev->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			 __func__, retval);
+}
+
+static void edge_bulk_in_callback(struct urb *urb)
+{
+	struct edgeport_port *edge_port = urb->context;
+	struct device *dev = &edge_port->port->dev;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	int retval = 0;
+	int port_number;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		return;
+	default:
+		dev_err(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", __func__, status);
+	}
+
+	if (status == -EPIPE)
+		goto exit;
+
+	if (status) {
+		dev_err(&urb->dev->dev, "%s - stopping read!\n", __func__);
+		return;
+	}
+
+	port_number = edge_port->port->port_number;
+
+	if (urb->actual_length > 0 && edge_port->lsr_event) {
+		edge_port->lsr_event = 0;
+		dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
+			__func__, port_number, edge_port->lsr_mask, *data);
+		handle_new_lsr(edge_port, 1, edge_port->lsr_mask, *data);
+		/* Adjust buffer length/pointer */
+		--urb->actual_length;
+		++data;
+	}
+
+	if (urb->actual_length) {
+		usb_serial_debug_data(dev, __func__, urb->actual_length, data);
+		if (edge_port->close_pending)
+			dev_dbg(dev, "%s - close pending, dropping data on the floor\n",
+								__func__);
+		else
+			edge_tty_recv(edge_port->port, data,
+					urb->actual_length);
+		edge_port->port->icount.rx += urb->actual_length;
+	}
+
+exit:
+	/* continue read unless stopped */
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING)
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
+	else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING)
+		edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED;
+
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+	if (retval)
+		dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval);
+}
+
+static void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
+		int length)
+{
+	int queued;
+
+	queued = tty_insert_flip_string(&port->port, data, length);
+	if (queued < length)
+		dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
+			__func__, length - queued);
+	tty_flip_buffer_push(&port->port);
+}
+
+static void edge_bulk_out_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status = urb->status;
+	struct tty_struct *tty;
+
+	edge_port->ep_write_urb_in_use = 0;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+		    __func__, status);
+		return;
+	default:
+		dev_err_console(port, "%s - nonzero write bulk status "
+			"received: %d\n", __func__, status);
+	}
+
+	/* send any buffered data */
+	tty = tty_port_tty_get(&port->port);
+	edge_send(port, tty);
+	tty_kref_put(tty);
+}
+
+static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct edgeport_serial *edge_serial;
+	struct usb_device *dev;
+	struct urb *urb;
+	int port_number;
+	int status;
+	u16 open_settings;
+	u8 transaction_timeout;
+
+	if (edge_port == NULL)
+		return -ENODEV;
+
+	port_number = port->port_number;
+
+	dev = port->serial->dev;
+
+	/* turn off loopback */
+	status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0);
+	if (status) {
+		dev_err(&port->dev,
+				"%s - cannot send clear loopback command, %d\n",
+			__func__, status);
+		return status;
+	}
+
+	/* set up the port settings */
+	if (tty)
+		edge_set_termios(tty, port, &tty->termios);
+
+	/* open up the port */
+
+	/* milliseconds to timeout for DMA transfer */
+	transaction_timeout = 2;
+
+	edge_port->ump_read_timeout =
+				max(20, ((transaction_timeout * 3) / 2));
+
+	/* milliseconds to timeout for DMA transfer */
+	open_settings = (u8)(UMP_DMA_MODE_CONTINOUS |
+			     UMP_PIPE_TRANS_TIMEOUT_ENA |
+			     (transaction_timeout << 2));
+
+	dev_dbg(&port->dev, "%s - Sending UMPC_OPEN_PORT\n", __func__);
+
+	/* Tell TI to open and start the port */
+	status = send_cmd(dev, UMPC_OPEN_PORT,
+		(u8)(UMPM_UART1_PORT + port_number), open_settings, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send open command, %d\n",
+							__func__, status);
+		return status;
+	}
+
+	/* Start the DMA? */
+	status = send_cmd(dev, UMPC_START_PORT,
+		(u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send start DMA command, %d\n",
+							__func__, status);
+		return status;
+	}
+
+	/* Clear TX and RX buffers in UMP */
+	status = purge_port(port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN);
+	if (status) {
+		dev_err(&port->dev,
+			"%s - cannot send clear buffers command, %d\n",
+			__func__, status);
+		return status;
+	}
+
+	/* Read Initial MSR */
+	status = ti_vread_sync(dev, UMPC_READ_MSR, 0,
+				(__u16)(UMPM_UART1_PORT + port_number),
+				&edge_port->shadow_msr, 1);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send read MSR command, %d\n",
+							__func__, status);
+		return status;
+	}
+
+	dev_dbg(&port->dev, "ShadowMSR 0x%X\n", edge_port->shadow_msr);
+
+	/* Set Initial MCR */
+	edge_port->shadow_mcr = MCR_RTS | MCR_DTR;
+	dev_dbg(&port->dev, "ShadowMCR 0x%X\n", edge_port->shadow_mcr);
+
+	edge_serial = edge_port->edge_serial;
+	if (mutex_lock_interruptible(&edge_serial->es_lock))
+		return -ERESTARTSYS;
+	if (edge_serial->num_ports_open == 0) {
+		/* we are the first port to open, post the interrupt urb */
+		urb = edge_serial->serial->port[0]->interrupt_in_urb;
+		urb->context = edge_serial;
+		status = usb_submit_urb(urb, GFP_KERNEL);
+		if (status) {
+			dev_err(&port->dev,
+				"%s - usb_submit_urb failed with value %d\n",
+					__func__, status);
+			goto release_es_lock;
+		}
+	}
+
+	/*
+	 * reset the data toggle on the bulk endpoints to work around bug in
+	 * host controllers where things get out of sync some times
+	 */
+	usb_clear_halt(dev, port->write_urb->pipe);
+	usb_clear_halt(dev, port->read_urb->pipe);
+
+	/* start up our bulk read urb */
+	urb = port->read_urb;
+	edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING;
+	urb->context = edge_port;
+	status = usb_submit_urb(urb, GFP_KERNEL);
+	if (status) {
+		dev_err(&port->dev,
+			"%s - read bulk usb_submit_urb failed with value %d\n",
+				__func__, status);
+		goto unlink_int_urb;
+	}
+
+	++edge_serial->num_ports_open;
+
+	goto release_es_lock;
+
+unlink_int_urb:
+	if (edge_port->edge_serial->num_ports_open == 0)
+		usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+release_es_lock:
+	mutex_unlock(&edge_serial->es_lock);
+	return status;
+}
+
+static void edge_close(struct usb_serial_port *port)
+{
+	struct edgeport_serial *edge_serial;
+	struct edgeport_port *edge_port;
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+	int port_number;
+
+	edge_serial = usb_get_serial_data(port->serial);
+	edge_port = usb_get_serial_port_data(port);
+	if (edge_serial == NULL || edge_port == NULL)
+		return;
+
+	/*
+	 * The bulkreadcompletion routine will check
+	 * this flag and dump add read data
+	 */
+	edge_port->close_pending = 1;
+
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->write_urb);
+	edge_port->ep_write_urb_in_use = 0;
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	kfifo_reset_out(&port->write_fifo);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__);
+	port_number = port->port_number;
+	send_cmd(serial->dev, UMPC_CLOSE_PORT,
+		     (__u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0);
+
+	mutex_lock(&edge_serial->es_lock);
+	--edge_port->edge_serial->num_ports_open;
+	if (edge_port->edge_serial->num_ports_open <= 0) {
+		/* last port is now closed, let's shut down our interrupt urb */
+		usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+		edge_port->edge_serial->num_ports_open = 0;
+	}
+	mutex_unlock(&edge_serial->es_lock);
+	edge_port->close_pending = 0;
+}
+
+static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
+				const unsigned char *data, int count)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	if (count == 0) {
+		dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
+		return 0;
+	}
+
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->close_pending == 1)
+		return -ENODEV;
+
+	count = kfifo_in_locked(&port->write_fifo, data, count,
+							&edge_port->ep_lock);
+	edge_send(port, tty);
+
+	return count;
+}
+
+static void edge_send(struct usb_serial_port *port, struct tty_struct *tty)
+{
+	int count, result;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	if (edge_port->ep_write_urb_in_use) {
+		spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+		return;
+	}
+
+	count = kfifo_out(&port->write_fifo,
+				port->write_urb->transfer_buffer,
+				port->bulk_out_size);
+
+	if (count == 0) {
+		spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+		return;
+	}
+
+	edge_port->ep_write_urb_in_use = 1;
+
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	usb_serial_debug_data(&port->dev, __func__, count, port->write_urb->transfer_buffer);
+
+	/* set up our urb */
+	port->write_urb->transfer_buffer_length = count;
+
+	/* send the data out the bulk port */
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	if (result) {
+		dev_err_console(port,
+			"%s - failed submitting write urb, error %d\n",
+				__func__, result);
+		edge_port->ep_write_urb_in_use = 0;
+		/* TODO: reschedule edge_send */
+	} else
+		edge_port->port->icount.tx += count;
+
+	/*
+	 * wakeup any process waiting for writes to complete
+	 * there is now more room in the buffer for new writes
+	 */
+	if (tty)
+		tty_wakeup(tty);
+}
+
+static int edge_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int room = 0;
+	unsigned long flags;
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->close_pending == 1)
+		return 0;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	room = kfifo_avail(&port->write_fifo);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+static int edge_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int chars = 0;
+	unsigned long flags;
+	if (edge_port == NULL)
+		return 0;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	chars = kfifo_len(&port->write_fifo);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+}
+
+static bool edge_tx_empty(struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int ret;
+
+	ret = tx_active(edge_port);
+	if (ret > 0)
+		return false;
+
+	return true;
+}
+
+static void edge_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status;
+
+	if (edge_port == NULL)
+		return;
+
+	/* if we are implementing XON/XOFF, send the stop character */
+	if (I_IXOFF(tty)) {
+		unsigned char stop_char = STOP_CHAR(tty);
+		status = edge_write(tty, port, &stop_char, 1);
+		if (status <= 0) {
+			dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status);
+		}
+	}
+
+	/*
+	 * if we are implementing RTS/CTS, stop reads
+	 * and the Edgeport will clear the RTS line
+	 */
+	if (C_CRTSCTS(tty))
+		stop_read(edge_port);
+
+}
+
+static void edge_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status;
+
+	if (edge_port == NULL)
+		return;
+
+	/* if we are implementing XON/XOFF, send the start character */
+	if (I_IXOFF(tty)) {
+		unsigned char start_char = START_CHAR(tty);
+		status = edge_write(tty, port, &start_char, 1);
+		if (status <= 0) {
+			dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status);
+		}
+	}
+	/*
+	 * if we are implementing RTS/CTS, restart reads
+	 * are the Edgeport will assert the RTS line
+	 */
+	if (C_CRTSCTS(tty)) {
+		status = restart_read(edge_port);
+		if (status)
+			dev_err(&port->dev,
+				"%s - read bulk usb_submit_urb failed: %d\n",
+							__func__, status);
+	}
+
+}
+
+static void stop_read(struct edgeport_port *edge_port)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING)
+		edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPING;
+	edge_port->shadow_mcr &= ~MCR_RTS;
+
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+}
+
+static int restart_read(struct edgeport_port *edge_port)
+{
+	struct urb *urb;
+	int status = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPED) {
+		urb = edge_port->port->read_urb;
+		status = usb_submit_urb(urb, GFP_ATOMIC);
+	}
+	edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING;
+	edge_port->shadow_mcr |= MCR_RTS;
+
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	return status;
+}
+
+static void change_port_settings(struct tty_struct *tty,
+		struct edgeport_port *edge_port, struct ktermios *old_termios)
+{
+	struct device *dev = &edge_port->port->dev;
+	struct ump_uart_config *config;
+	int baud;
+	unsigned cflag;
+	int status;
+	int port_number = edge_port->port->port_number;
+
+	config = kmalloc (sizeof (*config), GFP_KERNEL);
+	if (!config) {
+		tty->termios = *old_termios;
+		return;
+	}
+
+	cflag = tty->termios.c_cflag;
+
+	config->wFlags = 0;
+
+	/* These flags must be set */
+	config->wFlags |= UMP_MASK_UART_FLAGS_RECEIVE_MS_INT;
+	config->wFlags |= UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR;
+	config->bUartMode = (__u8)(edge_port->bUartMode);
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		    config->bDataBits = UMP_UART_CHAR5BITS;
+		    dev_dbg(dev, "%s - data bits = 5\n", __func__);
+		    break;
+	case CS6:
+		    config->bDataBits = UMP_UART_CHAR6BITS;
+		    dev_dbg(dev, "%s - data bits = 6\n", __func__);
+		    break;
+	case CS7:
+		    config->bDataBits = UMP_UART_CHAR7BITS;
+		    dev_dbg(dev, "%s - data bits = 7\n", __func__);
+		    break;
+	default:
+	case CS8:
+		    config->bDataBits = UMP_UART_CHAR8BITS;
+		    dev_dbg(dev, "%s - data bits = 8\n", __func__);
+			    break;
+	}
+
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
+			config->bParity = UMP_UART_ODDPARITY;
+			dev_dbg(dev, "%s - parity = odd\n", __func__);
+		} else {
+			config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
+			config->bParity = UMP_UART_EVENPARITY;
+			dev_dbg(dev, "%s - parity = even\n", __func__);
+		}
+	} else {
+		config->bParity = UMP_UART_NOPARITY;
+		dev_dbg(dev, "%s - parity = none\n", __func__);
+	}
+
+	if (cflag & CSTOPB) {
+		config->bStopBits = UMP_UART_STOPBIT2;
+		dev_dbg(dev, "%s - stop bits = 2\n", __func__);
+	} else {
+		config->bStopBits = UMP_UART_STOPBIT1;
+		dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+	}
+
+	/* figure out the flow control settings */
+	if (cflag & CRTSCTS) {
+		config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X_CTS_FLOW;
+		config->wFlags |= UMP_MASK_UART_FLAGS_RTS_FLOW;
+		dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
+	} else {
+		dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
+		restart_read(edge_port);
+	}
+
+	/*
+	 * if we are implementing XON/XOFF, set the start and stop
+	 * character in the device
+	 */
+	config->cXon  = START_CHAR(tty);
+	config->cXoff = STOP_CHAR(tty);
+
+	/* if we are implementing INBOUND XON/XOFF */
+	if (I_IXOFF(tty)) {
+		config->wFlags |= UMP_MASK_UART_FLAGS_IN_X;
+		dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+			__func__, config->cXon, config->cXoff);
+	} else
+		dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
+
+	/* if we are implementing OUTBOUND XON/XOFF */
+	if (I_IXON(tty)) {
+		config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X;
+		dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+			__func__, config->cXon, config->cXoff);
+	} else
+		dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
+
+	tty->termios.c_cflag &= ~CMSPAR;
+
+	/* Round the baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (!baud) {
+		/* pick a default, any default... */
+		baud = 9600;
+	} else {
+		/* Avoid a zero divisor. */
+		baud = min(baud, 461550);
+		tty_encode_baud_rate(tty, baud, baud);
+	}
+
+	edge_port->baud_rate = baud;
+	config->wBaudRate = (__u16)((461550L + baud/2) / baud);
+
+	/* FIXME: Recompute actual baud from divisor here */
+
+	dev_dbg(dev, "%s - baud rate = %d, wBaudRate = %d\n", __func__, baud, config->wBaudRate);
+
+	dev_dbg(dev, "wBaudRate:   %d\n", (int)(461550L / config->wBaudRate));
+	dev_dbg(dev, "wFlags:    0x%x\n", config->wFlags);
+	dev_dbg(dev, "bDataBits:   %d\n", config->bDataBits);
+	dev_dbg(dev, "bParity:     %d\n", config->bParity);
+	dev_dbg(dev, "bStopBits:   %d\n", config->bStopBits);
+	dev_dbg(dev, "cXon:        %d\n", config->cXon);
+	dev_dbg(dev, "cXoff:       %d\n", config->cXoff);
+	dev_dbg(dev, "bUartMode:   %d\n", config->bUartMode);
+
+	/* move the word values into big endian mode */
+	cpu_to_be16s(&config->wFlags);
+	cpu_to_be16s(&config->wBaudRate);
+
+	status = send_cmd(edge_port->port->serial->dev, UMPC_SET_CONFIG,
+				(__u8)(UMPM_UART1_PORT + port_number),
+				0, (__u8 *)config, sizeof(*config));
+	if (status)
+		dev_dbg(dev, "%s - error %d when trying to write config to device\n",
+			__func__, status);
+	kfree(config);
+}
+
+static void edge_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	if (edge_port == NULL)
+		return;
+	/* change the port settings to the new ones specified */
+	change_port_settings(tty, edge_port, old_termios);
+}
+
+static int edge_tiocmset(struct tty_struct *tty,
+					unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int mcr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	mcr = edge_port->shadow_mcr;
+	if (set & TIOCM_RTS)
+		mcr |= MCR_RTS;
+	if (set & TIOCM_DTR)
+		mcr |= MCR_DTR;
+	if (set & TIOCM_LOOP)
+		mcr |= MCR_LOOPBACK;
+
+	if (clear & TIOCM_RTS)
+		mcr &= ~MCR_RTS;
+	if (clear & TIOCM_DTR)
+		mcr &= ~MCR_DTR;
+	if (clear & TIOCM_LOOP)
+		mcr &= ~MCR_LOOPBACK;
+
+	edge_port->shadow_mcr = mcr;
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	restore_mcr(edge_port, mcr);
+	return 0;
+}
+
+static int edge_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int result = 0;
+	unsigned int msr;
+	unsigned int mcr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	msr = edge_port->shadow_msr;
+	mcr = edge_port->shadow_mcr;
+	result = ((mcr & MCR_DTR)	? TIOCM_DTR: 0)	  /* 0x002 */
+		  | ((mcr & MCR_RTS)	? TIOCM_RTS: 0)   /* 0x004 */
+		  | ((msr & EDGEPORT_MSR_CTS)	? TIOCM_CTS: 0)   /* 0x020 */
+		  | ((msr & EDGEPORT_MSR_CD)	? TIOCM_CAR: 0)   /* 0x040 */
+		  | ((msr & EDGEPORT_MSR_RI)	? TIOCM_RI:  0)   /* 0x080 */
+		  | ((msr & EDGEPORT_MSR_DSR)	? TIOCM_DSR: 0);  /* 0x100 */
+
+
+	dev_dbg(&port->dev, "%s -- %x\n", __func__, result);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	return result;
+}
+
+static int get_serial_info(struct edgeport_port *edge_port,
+				struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+	unsigned cwait;
+
+	cwait = edge_port->port->port.closing_wait;
+	if (cwait != ASYNC_CLOSING_WAIT_NONE)
+		cwait = jiffies_to_msecs(cwait) / 10;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= edge_port->port->minor;
+	tmp.port		= edge_port->port->port_number;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= edge_port->port->bulk_out_size;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= cwait;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int edge_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
+		return get_serial_info(edge_port,
+				(struct serial_struct __user *) arg);
+	}
+	return -ENOIOCTLCMD;
+}
+
+static void edge_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status;
+	int bv = 0;	/* Off */
+
+	if (break_state == -1)
+		bv = 1;	/* On */
+	status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
+	if (status)
+		dev_dbg(&port->dev, "%s - error %d sending break set/clear command.\n",
+			__func__, status);
+}
+
+static void edge_heartbeat_schedule(struct edgeport_serial *edge_serial)
+{
+	if (!edge_serial->use_heartbeat)
+		return;
+
+	schedule_delayed_work(&edge_serial->heartbeat_work,
+			FW_HEARTBEAT_SECS * HZ);
+}
+
+static void edge_heartbeat_work(struct work_struct *work)
+{
+	struct edgeport_serial *serial;
+	struct ti_i2c_desc *rom_desc;
+
+	serial = container_of(work, struct edgeport_serial,
+			heartbeat_work.work);
+
+	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+
+	/* Descriptor address request is enough to reset the firmware timer */
+	if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
+			rom_desc)) {
+		dev_err(&serial->serial->interface->dev,
+				"%s - Incomplete heartbeat\n", __func__);
+	}
+	kfree(rom_desc);
+
+	edge_heartbeat_schedule(serial);
+}
+
+static int edge_calc_num_ports(struct usb_serial *serial,
+				struct usb_serial_endpoints *epds)
+{
+	struct device *dev = &serial->interface->dev;
+	unsigned char num_ports = serial->type->num_ports;
+
+	/* Make sure we have the required endpoints when in download mode. */
+	if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
+		if (epds->num_bulk_in < num_ports ||
+				epds->num_bulk_out < num_ports ||
+				epds->num_interrupt_in < 1) {
+			dev_err(dev, "required endpoints missing\n");
+			return -ENODEV;
+		}
+	}
+
+	return num_ports;
+}
+
+static int edge_startup(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial;
+	int status;
+	u16 product_id;
+
+	/* create our private serial structure */
+	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
+	if (!edge_serial)
+		return -ENOMEM;
+
+	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 < 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);
+
+	/* Currently only the EP/416 models require heartbeat support */
+	if (edge_serial->fw_version > FW_HEARTBEAT_VERSION_CUTOFF) {
+		if (product_id == ION_DEVICE_ID_TI_EDGEPORT_416 ||
+			product_id == ION_DEVICE_ID_TI_EDGEPORT_416B) {
+			edge_serial->use_heartbeat = true;
+		}
+	}
+
+	edge_heartbeat_schedule(edge_serial);
+
+	return 0;
+}
+
+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)
+{
+	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+	cancel_delayed_work_sync(&edge_serial->heartbeat_work);
+	kfree(edge_serial);
+}
+
+static int edge_port_probe(struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port;
+	int ret;
+
+	edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
+	if (!edge_port)
+		return -ENOMEM;
+
+	spin_lock_init(&edge_port->ep_lock);
+	edge_port->port = port;
+	edge_port->edge_serial = usb_get_serial_data(port->serial);
+	edge_port->bUartMode = default_uart_mode;
+
+	switch (port->port_number) {
+	case 0:
+		edge_port->uart_base = UMPMEM_BASE_UART1;
+		edge_port->dma_address = UMPD_OEDB1_ADDRESS;
+		break;
+	case 1:
+		edge_port->uart_base = UMPMEM_BASE_UART2;
+		edge_port->dma_address = UMPD_OEDB2_ADDRESS;
+		break;
+	default:
+		dev_err(&port->dev, "unknown port number\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	dev_dbg(&port->dev,
+		"%s - port_number = %d, uart_base = %04x, dma_address = %04x\n",
+		__func__, port->port_number, edge_port->uart_base,
+		edge_port->dma_address);
+
+	usb_set_serial_port_data(port, edge_port);
+
+	ret = edge_create_sysfs_attrs(port);
+	if (ret)
+		goto err;
+
+	port->port.closing_wait = msecs_to_jiffies(closing_wait * 10);
+	port->port.drain_delay = 1;
+
+	return 0;
+err:
+	kfree(edge_port);
+
+	return ret;
+}
+
+static int edge_port_remove(struct usb_serial_port *port)
+{
+	struct edgeport_port *edge_port;
+
+	edge_port = usb_get_serial_port_data(port);
+	edge_remove_sysfs_attrs(port);
+	kfree(edge_port);
+
+	return 0;
+}
+
+/* Sysfs Attributes */
+
+static ssize_t uart_mode_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	return sprintf(buf, "%d\n", edge_port->bUartMode);
+}
+
+static ssize_t uart_mode_store(struct device *dev,
+	struct device_attribute *attr, const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int v = simple_strtoul(valbuf, NULL, 0);
+
+	dev_dbg(dev, "%s: setting uart_mode = %d\n", __func__, v);
+
+	if (v < 256)
+		edge_port->bUartMode = v;
+	else
+		dev_err(dev, "%s - uart_mode %d is invalid\n", __func__, v);
+
+	return count;
+}
+static DEVICE_ATTR_RW(uart_mode);
+
+static int edge_create_sysfs_attrs(struct usb_serial_port *port)
+{
+	return device_create_file(&port->dev, &dev_attr_uart_mode);
+}
+
+static int edge_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+	device_remove_file(&port->dev, &dev_attr_uart_mode);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int edge_suspend(struct usb_serial *serial, pm_message_t message)
+{
+	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+	cancel_delayed_work_sync(&edge_serial->heartbeat_work);
+
+	return 0;
+}
+
+static int edge_resume(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+	edge_heartbeat_schedule(edge_serial);
+
+	return 0;
+}
+#endif
+
+static struct usb_serial_driver edgeport_1port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "edgeport_ti_1",
+	},
+	.description		= "Edgeport TI 1 port adapter",
+	.id_table		= edgeport_1port_id_table,
+	.num_ports		= 1,
+	.num_bulk_out		= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.calc_num_ports		= edge_calc_num_ports,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.tx_empty		= edge_tx_empty,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_callback,
+#ifdef CONFIG_PM
+	.suspend		= edge_suspend,
+	.resume			= edge_resume,
+#endif
+};
+
+static struct usb_serial_driver edgeport_2port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "edgeport_ti_2",
+	},
+	.description		= "Edgeport TI 2 port adapter",
+	.id_table		= edgeport_2port_id_table,
+	.num_ports		= 2,
+	.num_bulk_out		= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.calc_num_ports		= edge_calc_num_ports,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_port_probe,
+	.port_remove		= edge_port_remove,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.tx_empty		= edge_tx_empty,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_callback,
+#ifdef CONFIG_PM
+	.suspend		= edge_suspend,
+	.resume			= edge_resume,
+#endif
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&edgeport_1port_device, &edgeport_2port_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("edgeport/down3.bin");
+
+module_param(closing_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs");
+
+module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ignore_cpu_rev,
+			"Ignore the cpu revision when connecting to a device");
+
+module_param(default_uart_mode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ...");
diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h
new file mode 100644
index 0000000..9bbcee3
--- /dev/null
+++ b/drivers/usb/serial/io_ti.h
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*****************************************************************************
+ *
+ *	Copyright (C) 1997-2002 Inside Out Networks, Inc.
+ *
+ *	Feb-16-2001	DMI	Added I2C structure definitions
+ *	May-29-2002	gkh	Ported to Linux
+ *
+ *
+ ******************************************************************************/
+
+#ifndef _IO_TI_H_
+#define _IO_TI_H_
+
+/* Address Space */
+#define DTK_ADDR_SPACE_XDATA		0x03	/* Addr is placed in XDATA space */
+#define DTK_ADDR_SPACE_I2C_TYPE_II	0x82	/* Addr is placed in I2C area */
+#define DTK_ADDR_SPACE_I2C_TYPE_III	0x83	/* Addr is placed in I2C area */
+
+/* UART Defines */
+#define UMPMEM_BASE_UART1		0xFFA0	/* UMP UART1 base address */
+#define UMPMEM_BASE_UART2		0xFFB0	/* UMP UART2 base address */
+#define UMPMEM_OFFS_UART_LSR		0x05	/* UMP UART LSR register offset */
+
+/* Bits per character */
+#define UMP_UART_CHAR5BITS		0x00
+#define UMP_UART_CHAR6BITS		0x01
+#define UMP_UART_CHAR7BITS		0x02
+#define UMP_UART_CHAR8BITS		0x03
+
+/* Parity */
+#define UMP_UART_NOPARITY		0x00
+#define UMP_UART_ODDPARITY		0x01
+#define UMP_UART_EVENPARITY		0x02
+#define UMP_UART_MARKPARITY		0x03
+#define UMP_UART_SPACEPARITY		0x04
+
+/* Stop bits */
+#define UMP_UART_STOPBIT1		0x00
+#define UMP_UART_STOPBIT15		0x01
+#define UMP_UART_STOPBIT2		0x02
+
+/* Line status register masks */
+#define UMP_UART_LSR_OV_MASK		0x01
+#define UMP_UART_LSR_PE_MASK		0x02
+#define UMP_UART_LSR_FE_MASK		0x04
+#define UMP_UART_LSR_BR_MASK		0x08
+#define UMP_UART_LSR_ER_MASK		0x0F
+#define UMP_UART_LSR_RX_MASK		0x10
+#define UMP_UART_LSR_TX_MASK		0x20
+
+#define UMP_UART_LSR_DATA_MASK		(LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)
+
+/* Port Settings Constants) */
+#define UMP_MASK_UART_FLAGS_RTS_FLOW		0x0001
+#define UMP_MASK_UART_FLAGS_RTS_DISABLE		0x0002
+#define UMP_MASK_UART_FLAGS_PARITY		0x0008
+#define UMP_MASK_UART_FLAGS_OUT_X_DSR_FLOW	0x0010
+#define UMP_MASK_UART_FLAGS_OUT_X_CTS_FLOW	0x0020
+#define UMP_MASK_UART_FLAGS_OUT_X		0x0040
+#define UMP_MASK_UART_FLAGS_OUT_XA		0x0080
+#define UMP_MASK_UART_FLAGS_IN_X		0x0100
+#define UMP_MASK_UART_FLAGS_DTR_FLOW		0x0800
+#define UMP_MASK_UART_FLAGS_DTR_DISABLE		0x1000
+#define UMP_MASK_UART_FLAGS_RECEIVE_MS_INT	0x2000
+#define UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR	0x4000
+
+#define UMP_DMA_MODE_CONTINOUS			0x01
+#define UMP_PIPE_TRANS_TIMEOUT_ENA		0x80
+#define UMP_PIPE_TRANSFER_MODE_MASK		0x03
+#define UMP_PIPE_TRANS_TIMEOUT_MASK		0x7C
+
+/* Purge port Direction Mask Bits */
+#define UMP_PORT_DIR_OUT			0x01
+#define UMP_PORT_DIR_IN				0x02
+
+/* Address of Port 0 */
+#define UMPM_UART1_PORT				0x03
+
+/* Commands */
+#define	UMPC_SET_CONFIG			0x05
+#define	UMPC_OPEN_PORT			0x06
+#define	UMPC_CLOSE_PORT			0x07
+#define	UMPC_START_PORT			0x08
+#define	UMPC_STOP_PORT			0x09
+#define	UMPC_TEST_PORT			0x0A
+#define	UMPC_PURGE_PORT			0x0B
+
+/* Force the Firmware to complete the current Read */
+#define	UMPC_COMPLETE_READ		0x80
+/* Force UMP back into BOOT Mode */
+#define	UMPC_HARDWARE_RESET		0x81
+/*
+ * Copy current download image to type 0xf2 record in 16k I2C
+ * firmware will change 0xff record to type 2 record when complete
+ */
+#define	UMPC_COPY_DNLD_TO_I2C		0x82
+
+/*
+ * Special function register commands
+ * wIndex is register address
+ * wValue is MSB/LSB mask/data
+ */
+#define	UMPC_WRITE_SFR			0x83	/* Write SFR Register */
+
+/* wIndex is register address */
+#define	UMPC_READ_SFR			0x84	/* Read SRF Register */
+
+/* Set or Clear DTR (wValue bit 0 Set/Clear)	wIndex ModuleID (port) */
+#define	UMPC_SET_CLR_DTR		0x85
+
+/* Set or Clear RTS (wValue bit 0 Set/Clear)	wIndex ModuleID (port) */
+#define	UMPC_SET_CLR_RTS		0x86
+
+/* Set or Clear LOOPBACK (wValue bit 0 Set/Clear) wIndex ModuleID (port) */
+#define	UMPC_SET_CLR_LOOPBACK		0x87
+
+/* Set or Clear BREAK (wValue bit 0 Set/Clear)	wIndex ModuleID (port) */
+#define	UMPC_SET_CLR_BREAK		0x88
+
+/* Read MSR wIndex ModuleID (port) */
+#define	UMPC_READ_MSR			0x89
+
+/* Toolkit commands */
+/* Read-write group */
+#define	UMPC_MEMORY_READ		0x92
+#define	UMPC_MEMORY_WRITE		0x93
+
+/*
+ *	UMP DMA Definitions
+ */
+#define UMPD_OEDB1_ADDRESS		0xFF08
+#define UMPD_OEDB2_ADDRESS		0xFF10
+
+struct out_endpoint_desc_block {
+	__u8 Configuration;
+	__u8 XBufAddr;
+	__u8 XByteCount;
+	__u8 Unused1;
+	__u8 Unused2;
+	__u8 YBufAddr;
+	__u8 YByteCount;
+	__u8 BufferSize;
+} __attribute__((packed));
+
+
+/*
+ * TYPE DEFINITIONS
+ * Structures for Firmware commands
+ */
+/* UART settings */
+struct ump_uart_config {
+	__u16 wBaudRate;	/* Baud rate                        */
+	__u16 wFlags;		/* Bitmap mask of flags             */
+	__u8 bDataBits;		/* 5..8 - data bits per character   */
+	__u8 bParity;		/* Parity settings                  */
+	__u8 bStopBits;		/* Stop bits settings               */
+	char cXon;		/* XON character                    */
+	char cXoff;		/* XOFF character                   */
+	__u8 bUartMode;		/* Will be updated when a user      */
+				/* interface is defined             */
+} __attribute__((packed));
+
+
+/*
+ * TYPE DEFINITIONS
+ * Structures for USB interrupts
+ */
+/* Interrupt packet structure */
+struct ump_interrupt {
+	__u8 bICode;			/* Interrupt code (interrupt num)   */
+	__u8 bIInfo;			/* Interrupt information            */
+}  __attribute__((packed));
+
+
+#define TIUMP_GET_PORT_FROM_CODE(c)	(((c) >> 6) & 0x01)
+#define TIUMP_GET_FUNC_FROM_CODE(c)	((c) & 0x0f)
+#define TIUMP_INTERRUPT_CODE_LSR	0x03
+#define TIUMP_INTERRUPT_CODE_MSR	0x04
+
+#endif
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
new file mode 100644
index 0000000..c38e87a
--- /dev/null
+++ b/drivers/usb/serial/io_usbvend.h
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0+
+/************************************************************************
+ *
+ *	USBVEND.H		Vendor-specific USB definitions
+ *
+ *	NOTE: This must be kept in sync with the Edgeport firmware and
+ *	must be kept backward-compatible with older firmware.
+ *
+ ************************************************************************
+ *
+ *	Copyright (C) 1998 Inside Out Networks, Inc.
+ *
+ ************************************************************************/
+
+#if !defined(_USBVEND_H)
+#define	_USBVEND_H
+
+/************************************************************************
+ *
+ *		D e f i n e s   /   T y p e d e f s
+ *
+ ************************************************************************/
+
+//
+// Definitions of USB product IDs
+//
+
+#define	USB_VENDOR_ID_ION	0x1608		// Our VID
+#define	USB_VENDOR_ID_TI	0x0451		// TI VID
+#define USB_VENDOR_ID_AXIOHM	0x05D9		/* Axiohm VID */
+
+//
+// Definitions of USB product IDs (PID)
+// We break the USB-defined PID into an OEM Id field (upper 6 bits)
+// and a Device Id (bottom 10 bits). The Device Id defines what
+// device this actually is regardless of what the OEM wants to
+// call it.
+//
+
+// ION-device OEM IDs
+#define	ION_OEM_ID_ION		0		// 00h Inside Out Networks
+#define	ION_OEM_ID_NLYNX	1		// 01h NLynx Systems
+#define	ION_OEM_ID_GENERIC	2		// 02h Generic OEM
+#define	ION_OEM_ID_MAC		3		// 03h Mac Version
+#define	ION_OEM_ID_MEGAWOLF	4		// 04h Lupusb OEM Mac version (MegaWolf)
+#define	ION_OEM_ID_MULTITECH	5		// 05h Multitech Rapidports
+#define	ION_OEM_ID_AGILENT	6		// 06h AGILENT board
+
+
+// ION-device Device IDs
+// Product IDs - assigned to match middle digit of serial number (No longer true)
+
+#define ION_DEVICE_ID_80251_NETCHIP	0x020	// This bit is set in the PID if this edgeport hardware$
+						// is based on the 80251+Netchip.
+
+#define ION_DEVICE_ID_GENERATION_1	0x00	// Value for 930 based edgeports
+#define ION_DEVICE_ID_GENERATION_2	0x01	// Value for 80251+Netchip.
+#define ION_DEVICE_ID_GENERATION_3	0x02	// Value for Texas Instruments TUSB5052 chip
+#define ION_DEVICE_ID_GENERATION_4	0x03	// Watchport Family of products
+#define ION_GENERATION_MASK		0x03
+
+#define ION_DEVICE_ID_HUB_MASK		0x0080	// This bit in the PID designates a HUB device
+						// for example 8C would be a 421 4 port hub
+						// and 8D would be a 2 port embedded hub
+
+#define EDGEPORT_DEVICE_ID_MASK			0x0ff	// Not including OEM or GENERATION fields
+
+#define	ION_DEVICE_ID_UNCONFIGURED_EDGE_DEVICE	0x000	// In manufacturing only
+#define ION_DEVICE_ID_EDGEPORT_4		0x001	// Edgeport/4 RS232
+#define	ION_DEVICE_ID_EDGEPORT_8R		0x002	// Edgeport with RJ45 no Ring
+#define ION_DEVICE_ID_RAPIDPORT_4		0x003	// Rapidport/4
+#define ION_DEVICE_ID_EDGEPORT_4T		0x004	// Edgeport/4 RS232 for Telxon (aka "Fleetport")
+#define ION_DEVICE_ID_EDGEPORT_2		0x005	// Edgeport/2 RS232
+#define ION_DEVICE_ID_EDGEPORT_4I		0x006	// Edgeport/4 RS422
+#define ION_DEVICE_ID_EDGEPORT_2I		0x007	// Edgeport/2 RS422/RS485
+#define	ION_DEVICE_ID_EDGEPORT_8RR		0x008	// Edgeport with RJ45 with Data and RTS/CTS only
+//	ION_DEVICE_ID_EDGEPORT_8_HANDBUILT	0x009	// Hand-built Edgeport/8 (Placeholder, used in middle digit of serial number only!)
+//	ION_DEVICE_ID_MULTIMODEM_4X56		0x00A	// MultiTech version of RP/4 (Placeholder, used in middle digit of serial number only!)
+#define	ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT	0x00B	// Edgeport/(4)21 Parallel port (USS720)
+#define	ION_DEVICE_ID_EDGEPORT_421		0x00C	// Edgeport/421 Hub+RS232+Parallel
+#define	ION_DEVICE_ID_EDGEPORT_21		0x00D	// Edgeport/21  RS232+Parallel
+#define ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU	0x00E	// Half of an Edgeport/8 (the kind with 2 EP/4s on 1 PCB)
+#define ION_DEVICE_ID_EDGEPORT_8		0x00F	// Edgeport/8 (single-CPU)
+#define ION_DEVICE_ID_EDGEPORT_2_DIN		0x010	// Edgeport/2 RS232 with Apple DIN connector
+#define ION_DEVICE_ID_EDGEPORT_4_DIN		0x011	// Edgeport/4 RS232 with Apple DIN connector
+#define ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU	0x012	// Half of an Edgeport/16 (the kind with 2 EP/8s)
+#define ION_DEVICE_ID_EDGEPORT_COMPATIBLE	0x013	// Edgeport Compatible, for NCR, Axiohm etc. testing
+#define ION_DEVICE_ID_EDGEPORT_8I		0x014	// Edgeport/8 RS422 (single-CPU)
+#define ION_DEVICE_ID_EDGEPORT_1		0x015	// Edgeport/1 RS232
+#define ION_DEVICE_ID_EPOS44			0x016	// Half of an EPOS/44 (TIUMP BASED)
+#define ION_DEVICE_ID_EDGEPORT_42		0x017	// Edgeport/42
+#define ION_DEVICE_ID_EDGEPORT_412_8		0x018	// Edgeport/412 8 port part
+#define ION_DEVICE_ID_EDGEPORT_412_4		0x019	// Edgeport/412	4 port part
+#define ION_DEVICE_ID_EDGEPORT_22I		0x01A	// Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232
+
+// Compact Form factor TI based devices  2c, 21c, 22c, 221c
+#define ION_DEVICE_ID_EDGEPORT_2C		0x01B	// Edgeport/2c is a TI based Edgeport/2 - Small I2c
+#define ION_DEVICE_ID_EDGEPORT_221C		0x01C	// Edgeport/221c is a TI based Edgeport/2 with lucent chip and
+							// 2 external hub ports - Large I2C
+#define ION_DEVICE_ID_EDGEPORT_22C		0x01D	// Edgeport/22c is a TI based Edgeport/2 with
+							// 2 external hub ports - Large I2C
+#define ION_DEVICE_ID_EDGEPORT_21C		0x01E	// Edgeport/21c is a TI based Edgeport/2 with lucent chip
+							// Small I2C
+
+
+/*
+ *  DANGER DANGER The 0x20 bit was used to indicate a 8251/netchip GEN 2 device.
+ *  Since the MAC, Linux, and Optimal drivers still used the old code
+ *  I suggest that you skip the 0x20 bit when creating new PIDs
+ */
+
+
+// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C)
+#define ION_DEVICE_ID_TI3410_EDGEPORT_1		0x040	// Edgeport/1 RS232
+#define ION_DEVICE_ID_TI3410_EDGEPORT_1I	0x041	// Edgeport/1i- RS422 model
+
+// Ti based software switchable RS232/RS422/RS485 devices
+#define ION_DEVICE_ID_EDGEPORT_4S		0x042	// Edgeport/4s - software switchable model
+#define ION_DEVICE_ID_EDGEPORT_8S		0x043	// Edgeport/8s - software switchable model
+
+// Usb to Ethernet dongle
+#define ION_DEVICE_ID_EDGEPORT_E		0x0E0	// Edgeport/E Usb to Ethernet
+
+// Edgeport TI based devices
+#define ION_DEVICE_ID_TI_EDGEPORT_4		0x0201	// Edgeport/4 RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_2		0x0205	// Edgeport/2 RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_4I		0x0206	// Edgeport/4i RS422
+#define ION_DEVICE_ID_TI_EDGEPORT_2I		0x0207	// Edgeport/2i RS422/RS485
+#define ION_DEVICE_ID_TI_EDGEPORT_421		0x020C	// Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port)
+#define ION_DEVICE_ID_TI_EDGEPORT_21		0x020D	// Edgeport/21 2 RS232 + Parallel (lucent on a different hub port)
+#define ION_DEVICE_ID_TI_EDGEPORT_416		0x0212  // Edgeport/416
+#define ION_DEVICE_ID_TI_EDGEPORT_1		0x0215	// Edgeport/1 RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_42		0x0217	// Edgeport/42 4 hub 2 RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_22I		0x021A	// Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_2C		0x021B	// Edgeport/2c RS232
+#define ION_DEVICE_ID_TI_EDGEPORT_221C		0x021C	// Edgeport/221c is a TI based Edgeport/2 with lucent chip and
+							// 2 external hub ports - Large I2C
+#define ION_DEVICE_ID_TI_EDGEPORT_22C		0x021D	// Edgeport/22c is a TI based Edgeport/2 with
+							// 2 external hub ports - Large I2C
+#define ION_DEVICE_ID_TI_EDGEPORT_21C		0x021E	// Edgeport/21c is a TI based Edgeport/2 with lucent chip
+
+// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C)
+#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1	0x0240	// Edgeport/1 RS232
+#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I	0x0241	// Edgeport/1i- RS422 model
+
+// Ti based software switchable RS232/RS422/RS485 devices
+#define ION_DEVICE_ID_TI_EDGEPORT_4S		0x0242	// Edgeport/4s - software switchable model
+#define ION_DEVICE_ID_TI_EDGEPORT_8S		0x0243	// Edgeport/8s - software switchable model
+#define ION_DEVICE_ID_TI_EDGEPORT_8		0x0244	// Edgeport/8 (single-CPU)
+#define ION_DEVICE_ID_TI_EDGEPORT_416B		0x0247	// Edgeport/416
+
+
+/************************************************************************
+ *
+ *                        Generation 4 devices
+ *
+ ************************************************************************/
+
+// Watchport based on 3410 both 1-wire and binary products (16K I2C)
+#define ION_DEVICE_ID_WP_UNSERIALIZED		0x300	// Watchport based on 3410 both 1-wire and binary products
+#define ION_DEVICE_ID_WP_PROXIMITY		0x301	// Watchport/P Discontinued
+#define ION_DEVICE_ID_WP_MOTION			0x302	// Watchport/M
+#define ION_DEVICE_ID_WP_MOISTURE		0x303	// Watchport/W
+#define ION_DEVICE_ID_WP_TEMPERATURE		0x304	// Watchport/T
+#define ION_DEVICE_ID_WP_HUMIDITY		0x305	// Watchport/H
+
+#define ION_DEVICE_ID_WP_POWER			0x306	// Watchport
+#define ION_DEVICE_ID_WP_LIGHT			0x307	// Watchport
+#define ION_DEVICE_ID_WP_RADIATION		0x308	// Watchport
+#define ION_DEVICE_ID_WP_ACCELERATION		0x309	// Watchport/A
+#define ION_DEVICE_ID_WP_DISTANCE		0x30A	// Watchport/D Discontinued
+#define ION_DEVICE_ID_WP_PROX_DIST		0x30B	// Watchport/D uses distance sensor
+							// Default to /P function
+
+#define ION_DEVICE_ID_PLUS_PWR_HP4CD		0x30C	// 5052 Plus Power HubPort/4CD+ (for Dell)
+#define ION_DEVICE_ID_PLUS_PWR_HP4C		0x30D	// 5052 Plus Power HubPort/4C+
+#define ION_DEVICE_ID_PLUS_PWR_PCI		0x30E	// 3410 Plus Power PCI Host Controller 4 port
+
+
+//
+// Definitions for AXIOHM USB product IDs
+//
+#define	USB_VENDOR_ID_AXIOHM			0x05D9	// Axiohm VID
+
+#define AXIOHM_DEVICE_ID_MASK			0xffff
+#define AXIOHM_DEVICE_ID_EPIC_A758		0xA758
+#define AXIOHM_DEVICE_ID_EPIC_A794		0xA794
+#define AXIOHM_DEVICE_ID_EPIC_A225		0xA225
+
+
+//
+// Definitions for NCR USB product IDs
+//
+#define	USB_VENDOR_ID_NCR			0x0404	// NCR VID
+
+#define NCR_DEVICE_ID_MASK			0xffff
+#define NCR_DEVICE_ID_EPIC_0202			0x0202
+#define NCR_DEVICE_ID_EPIC_0203			0x0203
+#define NCR_DEVICE_ID_EPIC_0310			0x0310
+#define NCR_DEVICE_ID_EPIC_0311			0x0311
+#define NCR_DEVICE_ID_EPIC_0312			0x0312
+
+
+//
+// Definitions for SYMBOL USB product IDs
+//
+#define USB_VENDOR_ID_SYMBOL			0x05E0	// Symbol VID
+#define SYMBOL_DEVICE_ID_MASK			0xffff
+#define SYMBOL_DEVICE_ID_KEYFOB			0x0700
+
+
+//
+// Definitions for other product IDs
+#define ION_DEVICE_ID_MT4X56USB			0x1403	// OEM device
+
+
+#define	GENERATION_ID_FROM_USB_PRODUCT_ID(ProductId)				\
+			((__u16) ((ProductId >> 8) & (ION_GENERATION_MASK)))
+
+#define	MAKE_USB_PRODUCT_ID(OemId, DeviceId)					\
+			((__u16) (((OemId) << 10) || (DeviceId)))
+
+#define	DEVICE_ID_FROM_USB_PRODUCT_ID(ProductId)				\
+			((__u16) ((ProductId) & (EDGEPORT_DEVICE_ID_MASK)))
+
+#define	OEM_ID_FROM_USB_PRODUCT_ID(ProductId)					\
+			((__u16) (((ProductId) >> 10) & 0x3F))
+
+//
+// Definitions of parameters for download code. Note that these are
+// specific to a given version of download code and must change if the
+// corresponding download code changes.
+//
+
+// TxCredits value below which driver won't bother sending (to prevent too many small writes).
+// Send only if above 25%
+#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max(((InitialCredit) / 4), (MaxPacketSize)))
+
+#define	EDGE_FW_BULK_MAX_PACKET_SIZE		64	// Max Packet Size for Bulk In Endpoint (EP1)
+#define EDGE_FW_BULK_READ_BUFFER_SIZE		1024	// Size to use for Bulk reads
+
+#define	EDGE_FW_INT_MAX_PACKET_SIZE		32	// Max Packet Size for Interrupt In Endpoint
+							// Note that many units were shipped with MPS=16, we
+							// force an upgrade to this value).
+#define EDGE_FW_INT_INTERVAL			2	// 2ms polling on IntPipe
+
+
+//
+// Definitions of I/O Networks vendor-specific requests
+// for default endpoint
+//
+//	bmRequestType = 01000000	Set vendor-specific, to device
+//	bmRequestType = 11000000	Get vendor-specific, to device
+//
+// These are the definitions for the bRequest field for the
+// above bmRequestTypes.
+//
+// For the read/write Edgeport memory commands, the parameters
+// are as follows:
+//		wValue = 16-bit address
+//		wIndex = unused (though we could put segment 00: or FF: here)
+//		wLength = # bytes to read/write (max 64)
+//
+
+#define USB_REQUEST_ION_RESET_DEVICE	0	// Warm reboot Edgeport, retaining USB address
+#define USB_REQUEST_ION_GET_EPIC_DESC	1	// Get Edgeport Compatibility Descriptor
+// unused				2	// Unused, available
+#define USB_REQUEST_ION_READ_RAM	3	// Read  EdgePort RAM at specified addr
+#define USB_REQUEST_ION_WRITE_RAM	4	// Write EdgePort RAM at specified addr
+#define USB_REQUEST_ION_READ_ROM	5	// Read  EdgePort ROM at specified addr
+#define USB_REQUEST_ION_WRITE_ROM	6	// Write EdgePort ROM at specified addr
+#define USB_REQUEST_ION_EXEC_DL_CODE	7	// Begin execution of RAM-based download
+						// code by jumping to address in wIndex:wValue
+//					8	// Unused, available
+#define USB_REQUEST_ION_ENABLE_SUSPEND	9	// Enable/Disable suspend feature
+						// (wValue != 0: Enable; wValue = 0: Disable)
+
+#define USB_REQUEST_ION_SEND_IOSP	10	// Send an IOSP command to the edgeport over the control pipe
+#define USB_REQUEST_ION_RECV_IOSP	11	// Receive an IOSP command from the edgeport over the control pipe
+
+
+#define USB_REQUEST_ION_DIS_INT_TIMER	0x80	// Sent to Axiohm to enable/ disable
+						// interrupt token timer
+						// wValue = 1, enable (default)
+						// wValue = 0, disable
+
+//
+// Define parameter values for our vendor-specific commands
+//
+
+//
+// Edgeport Compatibility Descriptor
+//
+// This descriptor is only returned by Edgeport-compatible devices
+// supporting the EPiC spec. True ION devices do not return this
+// descriptor, but instead return STALL on receipt of the
+// GET_EPIC_DESC command. The driver interprets a STALL to mean that
+// this is a "real" Edgeport.
+//
+
+struct edge_compatibility_bits {
+	// This __u32 defines which Vendor-specific commands/functionality
+	// the device supports on the default EP0 pipe.
+
+	__u32	VendEnableSuspend	:  1;	// 0001 Set if device supports ION_ENABLE_SUSPEND
+	__u32	VendUnused		: 31;	// Available for future expansion, must be 0
+
+	// This __u32 defines which IOSP commands are supported over the
+	// bulk pipe EP1.
+
+											// xxxx Set if device supports:
+	__u32	IOSPOpen		:  1;	// 0001	OPEN / OPEN_RSP (Currently must be 1)
+	__u32	IOSPClose		:  1;	// 0002	CLOSE
+	__u32	IOSPChase		:  1;	// 0004	CHASE / CHASE_RSP
+	__u32	IOSPSetRxFlow		:  1;	// 0008	SET_RX_FLOW
+	__u32	IOSPSetTxFlow		:  1;	// 0010	SET_TX_FLOW
+	__u32	IOSPSetXChar		:  1;	// 0020	SET_XON_CHAR/SET_XOFF_CHAR
+	__u32	IOSPRxCheck		:  1;	// 0040	RX_CHECK_REQ/RX_CHECK_RSP
+	__u32	IOSPSetClrBreak		:  1;	// 0080	SET_BREAK/CLEAR_BREAK
+	__u32	IOSPWriteMCR		:  1;	// 0100	MCR register writes (set/clr DTR/RTS)
+	__u32	IOSPWriteLCR		:  1;	// 0200	LCR register writes (wordlen/stop/parity)
+	__u32	IOSPSetBaudRate		:  1;	// 0400	setting Baud rate (writes to LCR.80h and DLL/DLM register)
+	__u32	IOSPDisableIntPipe	:  1;	// 0800 Do not use the interrupt pipe for TxCredits or RxButesAvailable
+	__u32	IOSPRxDataAvail		:  1;   // 1000 Return status of RX Fifo (Data available in Fifo)
+	__u32	IOSPTxPurge		:  1;	// 2000 Purge TXBuffer and/or Fifo in Edgeport hardware
+	__u32	IOSPUnused		: 18;	// Available for future expansion, must be 0
+
+	// This __u32 defines which 'general' features are supported
+
+	__u32	TrueEdgeport		:  1;	// 0001	Set if device is a 'real' Edgeport
+											// (Used only by driver, NEVER set by an EPiC device)
+	__u32	GenUnused		: 31;	// Available for future expansion, must be 0
+};
+
+#define EDGE_COMPATIBILITY_MASK0	0x0001
+#define EDGE_COMPATIBILITY_MASK1	0x3FFF
+#define EDGE_COMPATIBILITY_MASK2	0x0001
+
+struct edge_compatibility_descriptor {
+	__u8	Length;				// Descriptor Length (per USB spec)
+	__u8	DescType;			// Descriptor Type (per USB spec, =DEVICE type)
+	__u8	EpicVer;			// Version of EPiC spec supported
+						// (Currently must be 1)
+	__u8	NumPorts;			// Number of serial ports supported
+	__u8	iDownloadFile;			// Index of string containing download code filename
+						// 0=no download, FF=download compiled into driver.
+	__u8	Unused[3];			// Available for future expansion, must be 0
+						// (Currently must be 0).
+	__u8	MajorVersion;			// Firmware version: xx.
+	__u8	MinorVersion;			//  yy.
+	__le16	BuildNumber;			//  zzzz (LE format)
+
+	// The following structure contains __u32s, with each bit
+	// specifying whether the EPiC device supports the given
+	// command or functionality.
+	struct edge_compatibility_bits	Supports;
+};
+
+// Values for iDownloadFile
+#define	EDGE_DOWNLOAD_FILE_NONE		0	// No download requested
+#define	EDGE_DOWNLOAD_FILE_INTERNAL	0xFF	// Download the file compiled into driver (930 version)
+#define	EDGE_DOWNLOAD_FILE_I930		0xFF	// Download the file compiled into driver (930 version)
+#define	EDGE_DOWNLOAD_FILE_80251	0xFE	// Download the file compiled into driver (80251 version)
+
+
+
+/*
+ *	Special addresses for READ/WRITE_RAM/ROM
+ */
+
+// Version 1 (original) format of DeviceParams
+#define	EDGE_MANUF_DESC_ADDR_V1		0x00FF7F00
+#define	EDGE_MANUF_DESC_LEN_V1		sizeof(EDGE_MANUF_DESCRIPTOR_V1)
+
+// Version 2 format of DeviceParams. This format is longer (3C0h)
+// and starts lower in memory, at the uppermost 1K in ROM.
+#define	EDGE_MANUF_DESC_ADDR		0x00FF7C00
+#define	EDGE_MANUF_DESC_LEN		sizeof(struct edge_manuf_descriptor)
+
+// Boot params descriptor
+#define	EDGE_BOOT_DESC_ADDR		0x00FF7FC0
+#define	EDGE_BOOT_DESC_LEN		sizeof(struct edge_boot_descriptor)
+
+// Define the max block size that may be read or written
+// in a read/write RAM/ROM command.
+#define	MAX_SIZE_REQ_ION_READ_MEM	((__u16)64)
+#define	MAX_SIZE_REQ_ION_WRITE_MEM	((__u16)64)
+
+
+//
+// Notes for the following two ION vendor-specific param descriptors:
+//
+//	1.	These have a standard USB descriptor header so they look like a
+//		normal descriptor.
+//	2.	Any strings in the structures are in USB-defined string
+//		descriptor format, so that they may be separately retrieved,
+//		if necessary, with a minimum of work on the 930. This also
+//		requires them to be in UNICODE format, which, for English at
+//		least, simply means extending each __u8 into a __u16.
+//	3.	For all fields, 00 means 'uninitialized'.
+//	4.	All unused areas should be set to 00 for future expansion.
+//
+
+// This structure is ver 2 format. It contains ALL USB descriptors as
+// well as the configuration parameters that were in the original V1
+// structure. It is NOT modified when new boot code is downloaded; rather,
+// these values are set or modified by manufacturing. It is located at
+// xC00-xFBF (length 3C0h) in the ROM.
+// This structure is a superset of the v1 structure and is arranged so
+// that all of the v1 fields remain at the same address. We are just
+// adding more room to the front of the structure to hold the descriptors.
+//
+// The actual contents of this structure are defined in a 930 assembly
+// file, converted to a binary image, and then written by the serialization
+// program. The C definition of this structure just defines a dummy
+// area for general USB descriptors and the descriptor tables (the root
+// descriptor starts at xC00). At the bottom of the structure are the
+// fields inherited from the v1 structure.
+
+#define MAX_SERIALNUMBER_LEN	12
+#define MAX_ASSEMBLYNUMBER_LEN	14
+
+struct edge_manuf_descriptor {
+
+	__u16	RootDescTable[0x10];			// C00 Root of descriptor tables (just a placeholder)
+	__u8	DescriptorArea[0x2E0];			// C20 Descriptors go here, up to 2E0h (just a placeholder)
+
+							//     Start of v1-compatible section
+	__u8	Length;					// F00 Desc length for what follows, per USB (= C0h )
+	__u8	DescType;				// F01 Desc type, per USB (=DEVICE type)
+	__u8	DescVer;				// F02 Desc version/format (currently 2)
+	__u8	NumRootDescEntries;			// F03 # entries in RootDescTable
+
+	__u8	RomSize;				// F04 Size of ROM/E2PROM in K
+	__u8	RamSize;				// F05 Size of external RAM in K
+	__u8	CpuRev;					// F06 CPU revision level (chg only if s/w visible)
+	__u8	BoardRev;				// F07 PCB revision level (chg only if s/w visible)
+
+	__u8	NumPorts;				// F08 Number of ports
+	__u8	DescDate[3];				// F09 MM/DD/YY when descriptor template was compiler,
+							//     so host can track changes to USB-only descriptors.
+
+	__u8	SerNumLength;				// F0C USB string descriptor len
+	__u8	SerNumDescType;				// F0D USB descriptor type (=STRING type)
+	__le16	SerialNumber[MAX_SERIALNUMBER_LEN];	// F0E "01-01-000100" Unicode Serial Number
+
+	__u8	AssemblyNumLength;			// F26 USB string descriptor len
+	__u8	AssemblyNumDescType;			// F27 USB descriptor type (=STRING type)
+	__le16	AssemblyNumber[MAX_ASSEMBLYNUMBER_LEN];	// F28 "350-1000-01-A " assembly number
+
+	__u8	OemAssyNumLength;			// F44 USB string descriptor len
+	__u8	OemAssyNumDescType;			// F45 USB descriptor type (=STRING type)
+	__le16	OemAssyNumber[MAX_ASSEMBLYNUMBER_LEN];	// F46 "xxxxxxxxxxxxxx" OEM assembly number
+
+	__u8	ManufDateLength;			// F62 USB string descriptor len
+	__u8	ManufDateDescType;			// F63 USB descriptor type (=STRING type)
+	__le16	ManufDate[6];				// F64 "MMDDYY" manufacturing date
+
+	__u8	Reserved3[0x4D];			// F70 -- unused, set to 0 --
+
+	__u8	UartType;				// FBD Uart Type
+	__u8	IonPid;					// FBE Product ID, == LSB of USB DevDesc.PID
+							//      (Note: Edgeport/4s before 11/98 will have
+							//       00 here instead of 01)
+	__u8	IonConfig;				// FBF Config byte for ION manufacturing use
+							// FBF end of structure, total len = 3C0h
+
+};
+
+
+#define MANUF_DESC_VER_1	1	// Original definition of MANUF_DESC
+#define MANUF_DESC_VER_2	2	// Ver 2, starts at xC00h len 3C0h
+
+
+// Uart Types
+// Note: Since this field was added only recently, all Edgeport/4 units
+// shipped before 11/98 will have 00 in this field. Therefore,
+// both 00 and 01 values mean '654.
+#define MANUF_UART_EXAR_654_EARLY	0	// Exar 16C654 in Edgeport/4s before 11/98
+#define MANUF_UART_EXAR_654		1	// Exar 16C654
+#define MANUF_UART_EXAR_2852		2	// Exar 16C2852
+
+//
+// Note: The CpuRev and BoardRev values do not conform to manufacturing
+// revisions; they are to be incremented only when the CPU or hardware
+// changes in a software-visible way, such that the 930 software or
+// the host driver needs to handle the hardware differently.
+//
+
+// Values of bottom 5 bits of CpuRev & BoardRev for
+// Implementation 0 (ie, 930-based)
+#define	MANUF_CPU_REV_AD4		1	// 930 AD4, with EP1 Rx bug (needs RXSPM)
+#define	MANUF_CPU_REV_AD5		2	// 930 AD5, with above bug (supposedly) fixed
+#define	MANUF_CPU_80251			0x20	// Intel 80251
+
+
+#define MANUF_BOARD_REV_A		1	// Original version, == Manuf Rev A
+#define MANUF_BOARD_REV_B		2	// Manuf Rev B, wakeup interrupt works
+#define MANUF_BOARD_REV_C		3	// Manuf Rev C, 2/4 ports, rs232/rs422
+#define MANUF_BOARD_REV_GENERATION_2	0x20	// Second generaiton edgeport
+
+
+// Values of bottom 5 bits of CpuRev & BoardRev for
+// Implementation 1 (ie, 251+Netchip-based)
+#define	MANUF_CPU_REV_1			1	// C251TB Rev 1 (Need actual Intel rev here)
+
+#define MANUF_BOARD_REV_A		1	// First rev of 251+Netchip design
+
+#define	MANUF_SERNUM_LENGTH		sizeof(((struct edge_manuf_descriptor *)0)->SerialNumber)
+#define	MANUF_ASSYNUM_LENGTH		sizeof(((struct edge_manuf_descriptor *)0)->AssemblyNumber)
+#define	MANUF_OEMASSYNUM_LENGTH		sizeof(((struct edge_manuf_descriptor *)0)->OemAssyNumber)
+#define	MANUF_MANUFDATE_LENGTH		sizeof(((struct edge_manuf_descriptor *)0)->ManufDate)
+
+#define	MANUF_ION_CONFIG_DIAG_NO_LOOP	0x20	// As below but no ext loopback test
+#define	MANUF_ION_CONFIG_DIAG		0x40	// 930 based device: 1=Run h/w diags, 0=norm
+						// TIUMP Device    : 1=IONSERIAL needs to run Final Test
+#define	MANUF_ION_CONFIG_MASTER		0x80	// 930 based device:  1=Master mode, 0=Normal
+						// TIUMP Device    :  1=First device on a multi TIUMP Device
+
+//
+// This structure describes parameters for the boot code, and
+// is programmed along with new boot code. These are values
+// which are specific to a given build of the boot code. It
+// is exactly 64 bytes long and is fixed at address FF:xFC0
+// - FF:xFFF. Note that the 930-mandated UCONFIG bytes are
+// included in this structure.
+//
+struct edge_boot_descriptor {
+	__u8		Length;			// C0 Desc length, per USB (= 40h)
+	__u8		DescType;		// C1 Desc type, per USB (= DEVICE type)
+	__u8		DescVer;		// C2 Desc version/format
+	__u8		Reserved1;		// C3 -- unused, set to 0 --
+
+	__le16		BootCodeLength;		// C4 Boot code goes from FF:0000 to FF:(len-1)
+						//	  (LE format)
+
+	__u8		MajorVersion;		// C6 Firmware version: xx.
+	__u8		MinorVersion;		// C7			yy.
+	__le16		BuildNumber;		// C8			zzzz (LE format)
+
+	__u16		EnumRootDescTable;	// CA Root of ROM-based descriptor table
+	__u8		NumDescTypes;		// CC Number of supported descriptor types
+
+	__u8		Reserved4;		// CD Fix Compiler Packing
+
+	__le16		Capabilities;		// CE-CF Capabilities flags (LE format)
+	__u8		Reserved2[0x28];	// D0 -- unused, set to 0 --
+	__u8		UConfig0;		// F8 930-defined CPU configuration byte 0
+	__u8		UConfig1;		// F9 930-defined CPU configuration byte 1
+	__u8		Reserved3[6];		// FA -- unused, set to 0 --
+						// FF end of structure, total len = 80
+};
+
+
+#define BOOT_DESC_VER_1		1	// Original definition of BOOT_PARAMS
+#define BOOT_DESC_VER_2		2	// 2nd definition, descriptors not included in boot
+
+
+	// Capabilities flags
+
+#define	BOOT_CAP_RESET_CMD	0x0001	// If set, boot correctly supports ION_RESET_DEVICE
+
+
+/************************************************************************
+                 T I   U M P   D E F I N I T I O N S
+ ***********************************************************************/
+
+// Chip definitions in I2C
+#define UMP5152			0x52
+#define UMP3410			0x10
+
+
+//************************************************************************
+//	TI I2C Format Definitions
+//************************************************************************
+#define I2C_DESC_TYPE_INFO_BASIC	0x01
+#define I2C_DESC_TYPE_FIRMWARE_BASIC	0x02
+#define I2C_DESC_TYPE_DEVICE		0x03
+#define I2C_DESC_TYPE_CONFIG		0x04
+#define I2C_DESC_TYPE_STRING		0x05
+#define I2C_DESC_TYPE_FIRMWARE_AUTO	0x07	// for 3410 download
+#define I2C_DESC_TYPE_CONFIG_KLUDGE	0x14	// for 3410
+#define I2C_DESC_TYPE_WATCHPORT_VERSION	0x15	// firmware version number for watchport
+#define I2C_DESC_TYPE_WATCHPORT_CALIBRATION_DATA 0x16	// Watchport Calibration Data
+
+#define I2C_DESC_TYPE_FIRMWARE_BLANK	0xf2
+
+// Special section defined by ION
+#define I2C_DESC_TYPE_ION		0	// Not defined by TI
+
+
+struct ti_i2c_desc {
+	__u8	Type;			// Type of descriptor
+	__le16	Size;			// Size of data only not including header
+	__u8	CheckSum;		// Checksum (8 bit sum of data only)
+	__u8	Data[0];		// Data starts here
+} __attribute__((packed));
+
+// for 5152 devices only (type 2 record)
+// for 3410 the version is stored in the WATCHPORT_FIRMWARE_VERSION descriptor
+struct ti_i2c_firmware_rec {
+	__u8	Ver_Major;		// Firmware Major version number
+	__u8	Ver_Minor;		// Firmware Minor version number
+	__u8	Data[0];		// Download starts here
+} __attribute__((packed));
+
+
+struct watchport_firmware_version {
+// Added 2 bytes for version number
+	__u8	Version_Major;		//  Download Version (for Watchport)
+	__u8	Version_Minor;
+} __attribute__((packed));
+
+
+// Structure of header of download image in fw_down.h
+struct ti_i2c_image_header {
+	__le16	Length;
+	__u8	CheckSum;
+} __attribute__((packed));
+
+struct ti_basic_descriptor {
+	__u8	Power;		// Self powered
+				// bit 7: 1 - power switching supported
+				//        0 - power switching not supported
+				//
+				// bit 0: 1 - self powered
+				//        0 - bus powered
+				//
+				//
+	__u16	HubVid;		// VID HUB
+	__u16	HubPid;		// PID HUB
+	__u16	DevPid;		// PID Edgeport
+	__u8	HubTime;	// Time for power on to power good
+	__u8	HubCurrent;	// HUB Current = 100ma
+} __attribute__((packed));
+
+
+// CPU / Board Rev Definitions
+#define TI_CPU_REV_5052			2	// 5052 based edgeports
+#define TI_CPU_REV_3410			3	// 3410 based edgeports
+
+#define TI_BOARD_REV_TI_EP		0	// Basic ti based edgeport
+#define TI_BOARD_REV_COMPACT		1	// Compact board
+#define TI_BOARD_REV_WATCHPORT		2	// Watchport
+
+
+#define TI_GET_CPU_REVISION(x)		(__u8)((((x)>>4)&0x0f))
+#define TI_GET_BOARD_REVISION(x)	(__u8)(((x)&0x0f))
+
+#define TI_I2C_SIZE_MASK		0x1f  // 5 bits
+#define TI_GET_I2C_SIZE(x)		((((x) & TI_I2C_SIZE_MASK)+1)*256)
+
+#define TI_MAX_I2C_SIZE			(16 * 1024)
+
+#define TI_MANUF_VERSION_0		0
+
+// IonConig2 flags
+#define TI_CONFIG2_RS232		0x01
+#define TI_CONFIG2_RS422		0x02
+#define TI_CONFIG2_RS485		0x04
+#define TI_CONFIG2_SWITCHABLE		0x08
+
+#define TI_CONFIG2_WATCHPORT		0x10
+
+
+struct edge_ti_manuf_descriptor {
+	__u8 IonConfig;		//  Config byte for ION manufacturing use
+	__u8 IonConfig2;	//  Expansion
+	__u8 Version;		//  Version
+	__u8 CpuRev_BoardRev;	//  CPU revision level (0xF0) and Board Rev Level (0x0F)
+	__u8 NumPorts;		//  Number of ports	for this UMP
+	__u8 NumVirtualPorts;	//  Number of Virtual ports
+	__u8 HubConfig1;	//  Used to configure the Hub
+	__u8 HubConfig2;	//  Used to configure the Hub
+	__u8 TotalPorts;	//  Total Number of Com Ports for the entire device (All UMPs)
+	__u8 Reserved;		//  Reserved
+} __attribute__((packed));
+
+
+#endif		// if !defined(_USBVEND_H)
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
new file mode 100644
index 0000000..f81746c
--- /dev/null
+++ b/drivers/usb/serial/ipaq.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001 - 2002
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define KP_RETRIES	100
+
+#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
+#define DRIVER_DESC "USB PocketPC PDA driver"
+
+static int connect_retries = KP_RETRIES;
+static int initial_wait;
+
+/* Function prototypes for an ipaq */
+static int  ipaq_open(struct tty_struct *tty,
+			struct usb_serial_port *port);
+static int ipaq_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds);
+static int  ipaq_startup(struct usb_serial *serial);
+
+static const struct usb_device_id ipaq_id_table[] = {
+	{ USB_DEVICE(0x0104, 0x00BE) }, /* Socket USB Sync */
+	{ USB_DEVICE(0x03F0, 0x1016) }, /* HP USB Sync */
+	{ USB_DEVICE(0x03F0, 0x1116) }, /* HP USB Sync 1611 */
+	{ USB_DEVICE(0x03F0, 0x1216) }, /* HP USB Sync 1612 */
+	{ USB_DEVICE(0x03F0, 0x2016) }, /* HP USB Sync 1620 */
+	{ USB_DEVICE(0x03F0, 0x2116) }, /* HP USB Sync 1621 */
+	{ USB_DEVICE(0x03F0, 0x2216) }, /* HP USB Sync 1622 */
+	{ USB_DEVICE(0x03F0, 0x3016) }, /* HP USB Sync 1630 */
+	{ USB_DEVICE(0x03F0, 0x3116) }, /* HP USB Sync 1631 */
+	{ USB_DEVICE(0x03F0, 0x3216) }, /* HP USB Sync 1632 */
+	{ USB_DEVICE(0x03F0, 0x4016) }, /* HP USB Sync 1640 */
+	{ USB_DEVICE(0x03F0, 0x4116) }, /* HP USB Sync 1641 */
+	{ USB_DEVICE(0x03F0, 0x4216) }, /* HP USB Sync 1642 */
+	{ USB_DEVICE(0x03F0, 0x5016) }, /* HP USB Sync 1650 */
+	{ USB_DEVICE(0x03F0, 0x5116) }, /* HP USB Sync 1651 */
+	{ USB_DEVICE(0x03F0, 0x5216) }, /* HP USB Sync 1652 */
+	{ USB_DEVICE(0x0409, 0x00D5) }, /* NEC USB Sync */
+	{ USB_DEVICE(0x0409, 0x00D6) }, /* NEC USB Sync */
+	{ USB_DEVICE(0x0409, 0x00D7) }, /* NEC USB Sync */
+	{ USB_DEVICE(0x0409, 0x8024) }, /* NEC USB Sync */
+	{ USB_DEVICE(0x0409, 0x8025) }, /* NEC USB Sync */
+	{ USB_DEVICE(0x043E, 0x9C01) }, /* LGE USB Sync */
+	{ USB_DEVICE(0x045E, 0x00CE) }, /* Microsoft USB Sync */
+	{ USB_DEVICE(0x045E, 0x0400) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0401) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0402) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0403) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0404) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0405) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0406) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0407) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0408) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0409) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040A) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040B) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040C) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040D) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040E) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x040F) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0410) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0411) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0412) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0413) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0414) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0415) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0416) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0417) }, /* Windows Powered Pocket PC 2002 */
+	{ USB_DEVICE(0x045E, 0x0432) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0433) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0434) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0435) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0436) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0437) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0438) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0439) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043A) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043B) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043C) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043D) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043E) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x043F) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0440) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0441) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0442) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0443) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0444) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0445) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0446) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0447) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0448) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0449) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044A) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044B) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044C) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044D) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044E) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x044F) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0450) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0451) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0452) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0453) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0454) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0455) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0456) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0457) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0458) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0459) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045A) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045B) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045C) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045D) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045E) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x045F) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0460) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0461) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0462) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0463) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0464) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0465) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0466) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0467) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0468) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0469) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046A) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046B) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046C) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046D) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046E) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x046F) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0470) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0471) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0472) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0473) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0474) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0475) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0476) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0477) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0478) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x0479) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x047A) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x047B) }, /* Windows Powered Pocket PC 2003 */
+	{ USB_DEVICE(0x045E, 0x04C8) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04C9) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04CA) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04CB) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04CC) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04CD) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04CE) }, /* Windows Powered Smartphone 2002 */
+	{ USB_DEVICE(0x045E, 0x04D7) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04D8) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04D9) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DA) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DB) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DC) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DD) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DE) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04DF) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E0) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E1) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E2) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E3) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E4) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E5) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E6) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E7) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E8) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04E9) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x045E, 0x04EA) }, /* Windows Powered Smartphone 2003 */
+	{ USB_DEVICE(0x049F, 0x0003) }, /* Compaq iPAQ USB Sync */
+	{ USB_DEVICE(0x049F, 0x0032) }, /* Compaq iPAQ USB Sync */
+	{ USB_DEVICE(0x04A4, 0x0014) }, /* Hitachi USB Sync */
+	{ USB_DEVICE(0x04AD, 0x0301) }, /* USB Sync 0301 */
+	{ USB_DEVICE(0x04AD, 0x0302) }, /* USB Sync 0302 */
+	{ USB_DEVICE(0x04AD, 0x0303) }, /* USB Sync 0303 */
+	{ USB_DEVICE(0x04AD, 0x0306) }, /* GPS Pocket PC USB Sync */
+	{ USB_DEVICE(0x04B7, 0x0531) }, /* MyGuide 7000 XL USB Sync */
+	{ USB_DEVICE(0x04C5, 0x1058) }, /* FUJITSU USB Sync */
+	{ USB_DEVICE(0x04C5, 0x1079) }, /* FUJITSU USB Sync */
+	{ USB_DEVICE(0x04DA, 0x2500) }, /* Panasonic USB Sync */
+	{ USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */
+	{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
+	{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
+	{ USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
+	{ USB_DEVICE(0x04DD, 0x91AC) }, /* SHARP WS011SH USB Modem */
+	{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
+	{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
+	{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
+	{ USB_DEVICE(0x04E8, 0x5F03) }, /* Samsung NEXiO USB Sync */
+	{ USB_DEVICE(0x04E8, 0x5F04) }, /* Samsung NEXiO USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6611) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6613) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6615) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6617) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6619) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x661B) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x662E) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6630) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04E8, 0x6632) }, /* Samsung MITs USB Sync */
+	{ USB_DEVICE(0x04f1, 0x3011) }, /* JVC USB Sync */
+	{ USB_DEVICE(0x04F1, 0x3012) }, /* JVC USB Sync */
+	{ USB_DEVICE(0x0502, 0x1631) }, /* c10 Series */
+	{ USB_DEVICE(0x0502, 0x1632) }, /* c20 Series */
+	{ USB_DEVICE(0x0502, 0x16E1) }, /* Acer n10 Handheld USB Sync */
+	{ USB_DEVICE(0x0502, 0x16E2) }, /* Acer n20 Handheld USB Sync */
+	{ USB_DEVICE(0x0502, 0x16E3) }, /* Acer n30 Handheld USB Sync */
+	{ USB_DEVICE(0x0536, 0x01A0) }, /* HHP PDT */
+	{ USB_DEVICE(0x0543, 0x0ED9) }, /* ViewSonic Color Pocket PC V35 */
+	{ USB_DEVICE(0x0543, 0x1527) }, /* ViewSonic Color Pocket PC V36 */
+	{ USB_DEVICE(0x0543, 0x1529) }, /* ViewSonic Color Pocket PC V37 */
+	{ USB_DEVICE(0x0543, 0x152B) }, /* ViewSonic Color Pocket PC V38 */
+	{ USB_DEVICE(0x0543, 0x152E) }, /* ViewSonic Pocket PC */
+	{ USB_DEVICE(0x0543, 0x1921) }, /* ViewSonic Communicator Pocket PC */
+	{ USB_DEVICE(0x0543, 0x1922) }, /* ViewSonic Smartphone */
+	{ USB_DEVICE(0x0543, 0x1923) }, /* ViewSonic Pocket PC V30 */
+	{ USB_DEVICE(0x05E0, 0x2000) }, /* Symbol USB Sync */
+	{ USB_DEVICE(0x05E0, 0x2001) }, /* Symbol USB Sync 0x2001 */
+	{ USB_DEVICE(0x05E0, 0x2002) }, /* Symbol USB Sync 0x2002 */
+	{ USB_DEVICE(0x05E0, 0x2003) }, /* Symbol USB Sync 0x2003 */
+	{ USB_DEVICE(0x05E0, 0x2004) }, /* Symbol USB Sync 0x2004 */
+	{ USB_DEVICE(0x05E0, 0x2005) }, /* Symbol USB Sync 0x2005 */
+	{ USB_DEVICE(0x05E0, 0x2006) }, /* Symbol USB Sync 0x2006 */
+	{ USB_DEVICE(0x05E0, 0x2007) }, /* Symbol USB Sync 0x2007 */
+	{ USB_DEVICE(0x05E0, 0x2008) }, /* Symbol USB Sync 0x2008 */
+	{ USB_DEVICE(0x05E0, 0x2009) }, /* Symbol USB Sync 0x2009 */
+	{ USB_DEVICE(0x05E0, 0x200A) }, /* Symbol USB Sync 0x200A */
+	{ USB_DEVICE(0x067E, 0x1001) }, /* Intermec Mobile Computer */
+	{ USB_DEVICE(0x07CF, 0x2001) }, /* CASIO USB Sync 2001 */
+	{ USB_DEVICE(0x07CF, 0x2002) }, /* CASIO USB Sync 2002 */
+	{ USB_DEVICE(0x07CF, 0x2003) }, /* CASIO USB Sync 2003 */
+	{ USB_DEVICE(0x0930, 0x0700) }, /* TOSHIBA USB Sync 0700 */
+	{ USB_DEVICE(0x0930, 0x0705) }, /* TOSHIBA Pocket PC e310 */
+	{ USB_DEVICE(0x0930, 0x0706) }, /* TOSHIBA Pocket PC e740 */
+	{ USB_DEVICE(0x0930, 0x0707) }, /* TOSHIBA Pocket PC e330 Series */
+	{ USB_DEVICE(0x0930, 0x0708) }, /* TOSHIBA Pocket PC e350 Series */
+	{ USB_DEVICE(0x0930, 0x0709) }, /* TOSHIBA Pocket PC e750 Series */
+	{ USB_DEVICE(0x0930, 0x070A) }, /* TOSHIBA Pocket PC e400 Series */
+	{ USB_DEVICE(0x0930, 0x070B) }, /* TOSHIBA Pocket PC e800 Series */
+	{ USB_DEVICE(0x094B, 0x0001) }, /* Linkup Systems USB Sync */
+	{ USB_DEVICE(0x0960, 0x0065) }, /* BCOM USB Sync 0065 */
+	{ USB_DEVICE(0x0960, 0x0066) }, /* BCOM USB Sync 0066 */
+	{ USB_DEVICE(0x0960, 0x0067) }, /* BCOM USB Sync 0067 */
+	{ USB_DEVICE(0x0961, 0x0010) }, /* Portatec USB Sync */
+	{ USB_DEVICE(0x099E, 0x0052) }, /* Trimble GeoExplorer */
+	{ USB_DEVICE(0x099E, 0x4000) }, /* TDS Data Collector */
+	{ USB_DEVICE(0x0B05, 0x4200) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0B05, 0x4201) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0B05, 0x4202) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0B05, 0x420F) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0B05, 0x9200) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0B05, 0x9202) }, /* ASUS USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x00CE) }, /* HTC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC USB Modem */
+	{ USB_DEVICE(0x0BB4, 0x0A01) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A02) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A03) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A04) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A05) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A06) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A07) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A08) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A09) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0A) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0B) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0C) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0D) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0E) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A0F) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A10) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A11) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A12) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A13) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A14) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A15) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A16) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A17) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A18) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A19) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1A) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1B) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1C) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1D) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1E) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A1F) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A20) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A21) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A22) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A23) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A24) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A25) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A26) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A27) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A28) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A29) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2A) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2B) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2C) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2D) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2E) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A2F) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A30) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A31) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A32) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A33) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A34) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A35) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A36) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A37) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A38) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A39) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3A) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3B) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3C) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3D) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3E) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A3F) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A40) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A41) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A42) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A43) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A44) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A45) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A46) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A47) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A48) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A49) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4A) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4B) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4C) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4D) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4E) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A4F) }, /* PocketPC USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A50) }, /* HTC SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A51) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A52) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A53) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A54) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A55) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A56) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A57) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A58) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A59) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5A) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5B) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5C) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5D) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5E) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A5F) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A60) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A61) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A62) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A63) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A64) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A65) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A66) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A67) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A68) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A69) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6A) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6B) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6C) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6D) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6E) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A6F) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A70) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A71) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A72) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A73) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A74) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A75) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A76) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A77) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A78) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A79) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7A) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7B) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7C) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7D) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7E) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A7F) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A80) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A81) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A82) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A83) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A84) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A85) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A86) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A87) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A88) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A89) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8A) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8B) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8C) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8D) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8E) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A8F) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A90) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A91) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A92) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A93) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A94) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A95) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A96) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A97) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A98) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A99) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9A) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9B) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9C) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */
+	{ USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */
+	{ USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */
+	{ USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */
+	{ USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */
+	{ USB_DEVICE(0x0CAD, 0x9001) }, /* Motorola PowerPad Pocket PC Device */
+	{ USB_DEVICE(0x0F4E, 0x0200) }, /* Freedom Scientific USB Sync */
+	{ USB_DEVICE(0x0F98, 0x0201) }, /* Cyberbank USB Sync */
+	{ USB_DEVICE(0x0FB8, 0x3001) }, /* Wistron USB Sync */
+	{ USB_DEVICE(0x0FB8, 0x3002) }, /* Wistron USB Sync */
+	{ USB_DEVICE(0x0FB8, 0x3003) }, /* Wistron USB Sync */
+	{ USB_DEVICE(0x0FB8, 0x4001) }, /* Wistron USB Sync */
+	{ USB_DEVICE(0x1066, 0x00CE) }, /* E-TEN USB Sync */
+	{ USB_DEVICE(0x1066, 0x0300) }, /* E-TEN P3XX Pocket PC */
+	{ USB_DEVICE(0x1066, 0x0500) }, /* E-TEN P5XX Pocket PC */
+	{ USB_DEVICE(0x1066, 0x0600) }, /* E-TEN P6XX Pocket PC */
+	{ USB_DEVICE(0x1066, 0x0700) }, /* E-TEN P7XX Pocket PC */
+	{ USB_DEVICE(0x1114, 0x0001) }, /* Psion Teklogix Sync 753x */
+	{ USB_DEVICE(0x1114, 0x0004) }, /* Psion Teklogix Sync netBookPro */
+	{ USB_DEVICE(0x1114, 0x0006) }, /* Psion Teklogix Sync 7525 */
+	{ USB_DEVICE(0x1182, 0x1388) }, /* VES USB Sync */
+	{ USB_DEVICE(0x11D9, 0x1002) }, /* Rugged Pocket PC 2003 */
+	{ USB_DEVICE(0x11D9, 0x1003) }, /* Rugged Pocket PC 2003 */
+	{ USB_DEVICE(0x1231, 0xCE01) }, /* USB Sync 03 */
+	{ USB_DEVICE(0x1231, 0xCE02) }, /* USB Sync 03 */
+	{ USB_DEVICE(0x1690, 0x0601) }, /* Askey USB Sync */
+	{ USB_DEVICE(0x22B8, 0x4204) }, /* Motorola MPx200 Smartphone */
+	{ USB_DEVICE(0x22B8, 0x4214) }, /* Motorola MPc GSM */
+	{ USB_DEVICE(0x22B8, 0x4224) }, /* Motorola MPx220 Smartphone */
+	{ USB_DEVICE(0x22B8, 0x4234) }, /* Motorola MPc CDMA */
+	{ USB_DEVICE(0x22B8, 0x4244) }, /* Motorola MPx100 Smartphone */
+	{ USB_DEVICE(0x3340, 0x011C) }, /* Mio DigiWalker PPC StrongARM */
+	{ USB_DEVICE(0x3340, 0x0326) }, /* Mio DigiWalker 338 */
+	{ USB_DEVICE(0x3340, 0x0426) }, /* Mio DigiWalker 338 */
+	{ USB_DEVICE(0x3340, 0x043A) }, /* Mio DigiWalker USB Sync */
+	{ USB_DEVICE(0x3340, 0x051C) }, /* MiTAC USB Sync 528 */
+	{ USB_DEVICE(0x3340, 0x053A) }, /* Mio DigiWalker SmartPhone USB Sync */
+	{ USB_DEVICE(0x3340, 0x071C) }, /* MiTAC USB Sync */
+	{ USB_DEVICE(0x3340, 0x0B1C) }, /* Generic PPC StrongARM */
+	{ USB_DEVICE(0x3340, 0x0E3A) }, /* Generic PPC USB Sync */
+	{ USB_DEVICE(0x3340, 0x0F1C) }, /* Itautec USB Sync */
+	{ USB_DEVICE(0x3340, 0x0F3A) }, /* Generic SmartPhone USB Sync */
+	{ USB_DEVICE(0x3340, 0x1326) }, /* Itautec USB Sync */
+	{ USB_DEVICE(0x3340, 0x191C) }, /* YAKUMO USB Sync */
+	{ USB_DEVICE(0x3340, 0x2326) }, /* Vobis USB Sync */
+	{ USB_DEVICE(0x3340, 0x3326) }, /* MEDION Winodws Moble USB Sync */
+	{ USB_DEVICE(0x3708, 0x20CE) }, /* Legend USB Sync */
+	{ USB_DEVICE(0x3708, 0x21CE) }, /* Lenovo USB Sync */
+	{ USB_DEVICE(0x4113, 0x0210) }, /* Mobile Media Technology USB Sync */
+	{ USB_DEVICE(0x4113, 0x0211) }, /* Mobile Media Technology USB Sync */
+	{ USB_DEVICE(0x4113, 0x0400) }, /* Mobile Media Technology USB Sync */
+	{ USB_DEVICE(0x4113, 0x0410) }, /* Mobile Media Technology USB Sync */
+	{ USB_DEVICE(0x413C, 0x4001) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4002) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4003) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4004) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4005) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4006) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4007) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4008) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */
+	{ USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */
+	{ USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */
+	{ }                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ipaq_id_table);
+
+
+/* All of the device info needed for the Compaq iPAQ */
+static struct usb_serial_driver ipaq_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"ipaq",
+	},
+	.description =		"PocketPC PDA",
+	.id_table =		ipaq_id_table,
+	.bulk_in_size =		256,
+	.bulk_out_size =	256,
+	.open =			ipaq_open,
+	.attach =		ipaq_startup,
+	.calc_num_ports =	ipaq_calc_num_ports,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ipaq_device, NULL
+};
+
+static int ipaq_open(struct tty_struct *tty,
+			struct usb_serial_port *port)
+{
+	struct usb_serial	*serial = port->serial;
+	int			result = 0;
+	int			retries = connect_retries;
+
+	msleep(1000*initial_wait);
+
+	/*
+	 * Send out control message observed in win98 sniffs. Not sure what
+	 * it does, but from empirical observations, it seems that the device
+	 * will start the chat sequence once one of these messages gets
+	 * through. Since this has a reasonably high failure rate, we retry
+	 * several times.
+	 */
+	while (retries) {
+		retries--;
+		result = usb_control_msg(serial->dev,
+				usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+				0x1, 0, NULL, 0, 100);
+		if (!result)
+			break;
+
+		msleep(1000);
+	}
+	if (!retries && result) {
+		dev_err(&port->dev, "%s - failed doing control urb, error %d\n",
+							__func__, result);
+		return result;
+	}
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static int ipaq_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	/*
+	 * Some of the devices in ipaq_id_table[] are composite, and we
+	 * shouldn't bind to all the interfaces. This test will rule out
+	 * some obviously invalid possibilities.
+	 */
+	if (epds->num_bulk_in == 0 || epds->num_bulk_out == 0)
+		return -ENODEV;
+
+	/*
+	 * A few devices have four endpoints, seemingly Yakuma devices, and
+	 * we need the second pair.
+	 */
+	if (epds->num_bulk_in > 1 && epds->num_bulk_out > 1) {
+		epds->bulk_in[0] = epds->bulk_in[1];
+		epds->bulk_out[0] = epds->bulk_out[1];
+	}
+
+	/*
+	 * Other devices have 3 endpoints, but we only use the first bulk in
+	 * and out endpoints.
+	 */
+	epds->num_bulk_in = 1;
+	epds->num_bulk_out = 1;
+
+	return 1;
+}
+
+static int ipaq_startup(struct usb_serial *serial)
+{
+	if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
+		/*
+		 * FIXME: HP iPaq rx3715, possibly others, have 1 config that
+		 * is labeled as 2
+		 */
+
+		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
+			serial->dev->actconfig->desc.bConfigurationValue);
+		return -ENODEV;
+	}
+
+	return usb_reset_configuration(serial->dev);
+}
+
+module_usb_serial_driver(serial_drivers, ipaq_id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(connect_retries, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(connect_retries,
+		"Maximum number of connect retries (one second each)");
+
+module_param(initial_wait, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(initial_wait,
+		"Time to wait before attempting a connection (in seconds)");
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
new file mode 100644
index 0000000..d04c7cc
--- /dev/null
+++ b/drivers/usb/serial/ipw.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IPWireless 3G UMTS TDD Modem driver (USB connected)
+ *
+ *   Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
+ *   Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * All information about the device was acquired using SnoopyPro
+ * on MSFT's O/S, and examing the MSFT drivers' debug output
+ * (insanely left _on_ in the enduser version)
+ *
+ * It was written out of frustration with the IPWireless USB modem
+ * supplied by Axity3G/Sentech South Africa not supporting
+ * Linux whatsoever.
+ *
+ * Nobody provided any proprietary information that was not already
+ * available for this device.
+ *
+ * The modem adheres to the "3GPP TS  27.007 AT command set for 3G
+ * User Equipment (UE)" standard, available from
+ * http://www.3gpp.org/ftp/Specs/html-info/27007.htm
+ *
+ * The code was only tested the IPWireless handheld modem distributed
+ * in South Africa by Sentech.
+ *
+ * It may work for Woosh Inc in .nz too, as it appears they use the
+ * same kit.
+ *
+ * There is still some work to be done in terms of handling
+ * DCD, DTR, RTS, CTS which are currently faked.
+ * It's good enough for PPP at this point. It's based off all kinds of
+ * code found in usb/serial and usb/class
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+#include "usb-wwan.h"
+
+#define DRIVER_AUTHOR	"Roelf Diedericks"
+#define DRIVER_DESC	"IPWireless tty driver"
+
+#define IPW_TTY_MAJOR	240	/* real device node major id, experimental range */
+#define IPW_TTY_MINORS	256	/* we support 256 devices, dunno why, it'd be insane :) */
+
+#define USB_IPW_MAGIC	0x6d02	/* magic number for ipw struct */
+
+
+/* Message sizes */
+#define EVENT_BUFFER_SIZE	0xFF
+#define CHAR2INT16(c1, c0)	(((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+
+/* vendor/product pairs that are known work with this driver*/
+#define IPW_VID		0x0bc3
+#define IPW_PID		0x0001
+
+
+/* Vendor commands: */
+
+/* baud rates */
+enum {
+	ipw_sio_b256000 = 0x000e,
+	ipw_sio_b128000 = 0x001d,
+	ipw_sio_b115200 = 0x0020,
+	ipw_sio_b57600  = 0x0040,
+	ipw_sio_b56000  = 0x0042,
+	ipw_sio_b38400  = 0x0060,
+	ipw_sio_b19200  = 0x00c0,
+	ipw_sio_b14400  = 0x0100,
+	ipw_sio_b9600   = 0x0180,
+	ipw_sio_b4800   = 0x0300,
+	ipw_sio_b2400   = 0x0600,
+	ipw_sio_b1200   = 0x0c00,
+	ipw_sio_b600    = 0x1800
+};
+
+/* data bits */
+#define ipw_dtb_7		0x700
+#define ipw_dtb_8		0x810	/* ok so the define is misleading, I know, but forces 8,n,1 */
+					/* I mean, is there a point to any other setting these days? :) */
+
+/* usb control request types : */
+#define IPW_SIO_RXCTL		0x00	/* control bulk rx channel transmissions, value=1/0 (on/off) */
+#define IPW_SIO_SET_BAUD	0x01	/* set baud, value=requested ipw_sio_bxxxx */
+#define IPW_SIO_SET_LINE	0x03	/* set databits, parity. value=ipw_dtb_x */
+#define IPW_SIO_SET_PIN		0x03	/* set/clear dtr/rts value=ipw_pin_xxx */
+#define IPW_SIO_POLL		0x08	/* get serial port status byte, call with value=0 */
+#define IPW_SIO_INIT		0x11	/* initializes ? value=0 (appears as first thing todo on open) */
+#define IPW_SIO_PURGE		0x12	/* purge all transmissions?, call with value=numchar_to_purge */
+#define IPW_SIO_HANDFLOW	0x13	/* set xon/xoff limits value=0, and a buffer of 0x10 bytes */
+#define IPW_SIO_SETCHARS	0x13	/* set the flowcontrol special chars, value=0, buf=6 bytes, */
+					/* last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 */
+
+/* values used for request IPW_SIO_SET_PIN */
+#define IPW_PIN_SETDTR		0x101
+#define IPW_PIN_SETRTS		0x202
+#define IPW_PIN_CLRDTR		0x100
+#define IPW_PIN_CLRRTS		0x200 /* unconfirmed */
+
+/* values used for request IPW_SIO_RXCTL */
+#define IPW_RXBULK_ON		1
+#define IPW_RXBULK_OFF		0
+
+/* various 16 byte hardcoded transferbuffers used by flow control */
+#define IPW_BYTES_FLOWINIT	{ 0x01, 0, 0, 0, 0x40, 0, 0, 0, \
+					0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define IPW_DSR			((1<<4) | (1<<5))
+#define IPW_CTS			((1<<5) | (1<<4))
+
+#define IPW_WANTS_TO_SEND	0x30
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(IPW_VID, IPW_PID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_device *udev = port->serial->dev;
+	struct device *dev = &port->dev;
+	u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
+	u8 *buf_flow_init;
+	int result;
+
+	buf_flow_init = kmemdup(buf_flow_static, 16, GFP_KERNEL);
+	if (!buf_flow_init)
+		return -ENOMEM;
+
+	/* --1: Tell the modem to initialize (we think) From sniffs this is
+	 *	always the first thing that gets sent to the modem during
+	 *	opening of the device */
+	dev_dbg(dev, "%s: Sending SIO_INIT (we guess)\n", __func__);
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_INIT,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 0,
+			 0, /* index */
+			 NULL,
+			 0,
+			 100000);
+	if (result < 0)
+		dev_err(dev, "Init of modem failed (error = %d)\n", result);
+
+	/* reset the bulk pipes */
+	usb_clear_halt(udev, usb_rcvbulkpipe(udev, port->bulk_in_endpointAddress));
+	usb_clear_halt(udev, usb_sndbulkpipe(udev, port->bulk_out_endpointAddress));
+
+	/*--2: Start reading from the device */
+	dev_dbg(dev, "%s: setting up bulk read callback\n", __func__);
+	usb_wwan_open(tty, port);
+
+	/*--3: Tell the modem to open the floodgates on the rx bulk channel */
+	dev_dbg(dev, "%s:asking modem for RxRead (RXBULK_ON)\n", __func__);
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_RXCTL,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 IPW_RXBULK_ON,
+			 0, /* index */
+			 NULL,
+			 0,
+			 100000);
+	if (result < 0)
+		dev_err(dev, "Enabling bulk RxRead failed (error = %d)\n", result);
+
+	/*--4: setup the initial flowcontrol */
+	dev_dbg(dev, "%s:setting init flowcontrol (%s)\n", __func__, buf_flow_init);
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_HANDFLOW,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 0,
+			 0,
+			 buf_flow_init,
+			 0x10,
+			 200000);
+	if (result < 0)
+		dev_err(dev, "initial flowcontrol failed (error = %d)\n", result);
+
+	kfree(buf_flow_init);
+	return 0;
+}
+
+static int ipw_attach(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *data;
+
+	data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	spin_lock_init(&data->susp_lock);
+	usb_set_serial_data(serial, data);
+	return 0;
+}
+
+static void ipw_release(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
+
+	usb_set_serial_data(serial, NULL);
+	kfree(data);
+}
+
+static void ipw_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_device *udev = port->serial->dev;
+	struct device *dev = &port->dev;
+	int result;
+
+	dev_dbg(dev, "%s: on = %d\n", __func__, on);
+
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_SET_PIN,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
+			 0,
+			 NULL,
+			 0,
+			 200000);
+	if (result < 0)
+		dev_err(dev, "setting dtr failed (error = %d)\n", result);
+
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
+					USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
+			 0,
+			 NULL,
+			 0,
+			 200000);
+	if (result < 0)
+		dev_err(dev, "setting rts failed (error = %d)\n", result);
+}
+
+static void ipw_close(struct usb_serial_port *port)
+{
+	struct usb_device *udev = port->serial->dev;
+	struct device *dev = &port->dev;
+	int result;
+
+	/*--3: purge */
+	dev_dbg(dev, "%s:sending purge\n", __func__);
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_PURGE, USB_TYPE_VENDOR |
+			 		USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 0x03,
+			 0,
+			 NULL,
+			 0,
+			 200000);
+	if (result < 0)
+		dev_err(dev, "purge failed (error = %d)\n", result);
+
+
+	/* send RXBULK_off (tell modem to stop transmitting bulk data on
+	   rx chan) */
+	result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 IPW_SIO_RXCTL,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 IPW_RXBULK_OFF,
+			 0, /* index */
+			 NULL,
+			 0,
+			 100000);
+
+	if (result < 0)
+		dev_err(dev, "Disabling bulk RxRead failed (error = %d)\n", result);
+
+	usb_wwan_close(port);
+}
+
+static struct usb_serial_driver ipw_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"ipw",
+	},
+	.description =		"IPWireless converter",
+	.id_table =		id_table,
+	.num_ports =		1,
+	.open =			ipw_open,
+	.close =		ipw_close,
+	.attach =		ipw_attach,
+	.release =		ipw_release,
+	.port_probe =		usb_wwan_port_probe,
+	.port_remove =		usb_wwan_port_remove,
+	.dtr_rts =		ipw_dtr_rts,
+	.write =		usb_wwan_write,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ipw_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+/* Module information */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
new file mode 100644
index 0000000..7643716
--- /dev/null
+++ b/drivers/usb/serial/ir-usb.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB IR Dongle driver
+ *
+ *	Copyright (C) 2001-2002	Greg Kroah-Hartman (greg@kroah.com)
+ *	Copyright (C) 2002	Gary Brubaker (xavyer@ix.netcom.com)
+ *	Copyright (C) 2010	Johan Hovold (jhovold@gmail.com)
+ *
+ * This driver allows a USB IrDA device to be used as a "dumb" serial device.
+ * This can be useful if you do not have access to a full IrDA stack on the
+ * other side of the connection.  If you do have an IrDA stack on both devices,
+ * please use the usb-irda driver, as it contains the proper error checking and
+ * other goodness of a full IrDA stack.
+ *
+ * Portions of this driver were taken from drivers/net/irda/irda-usb.c, which
+ * was written by Roman Weissgaerber <weissg@vienna.at>, Dag Brattli
+ * <dag@brattli.net>, and Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/usb/irda.h>
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "USB IR Dongle driver"
+
+/* if overridden by the user, then use their value for the size of the read and
+ * write urbs */
+static int buffer_size;
+
+/* if overridden by the user, then use the specified number of XBOFs */
+static int xbof = -1;
+
+static int  ir_startup (struct usb_serial *serial);
+static int  ir_open(struct tty_struct *tty, struct usb_serial_port *port);
+static int ir_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size);
+static void ir_process_read_urb(struct urb *urb);
+static void ir_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios);
+
+/* Not that this lot means you can only have one per system */
+static u8 ir_baud;
+static u8 ir_xbof;
+static u8 ir_add_bof;
+
+static const struct usb_device_id ir_id_table[] = {
+	{ USB_DEVICE(0x050f, 0x0180) },		/* KC Technology, KC-180 */
+	{ USB_DEVICE(0x08e9, 0x0100) },		/* XTNDAccess */
+	{ USB_DEVICE(0x09c4, 0x0011) },		/* ACTiSys ACT-IR2000U */
+	{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ir_id_table);
+
+static struct usb_serial_driver ir_device = {
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "ir-usb",
+	},
+	.description		= "IR Dongle",
+	.id_table		= ir_id_table,
+	.num_ports		= 1,
+	.set_termios		= ir_set_termios,
+	.attach			= ir_startup,
+	.open			= ir_open,
+	.prepare_write_buffer	= ir_prepare_write_buffer,
+	.process_read_urb	= ir_process_read_urb,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ir_device, NULL
+};
+
+static inline void irda_usb_dump_class_desc(struct usb_serial *serial,
+					    struct usb_irda_cs_descriptor *desc)
+{
+	struct device *dev = &serial->dev->dev;
+
+	dev_dbg(dev, "bLength=%x\n", desc->bLength);
+	dev_dbg(dev, "bDescriptorType=%x\n", desc->bDescriptorType);
+	dev_dbg(dev, "bcdSpecRevision=%x\n", __le16_to_cpu(desc->bcdSpecRevision));
+	dev_dbg(dev, "bmDataSize=%x\n", desc->bmDataSize);
+	dev_dbg(dev, "bmWindowSize=%x\n", desc->bmWindowSize);
+	dev_dbg(dev, "bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime);
+	dev_dbg(dev, "wBaudRate=%x\n", __le16_to_cpu(desc->wBaudRate));
+	dev_dbg(dev, "bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs);
+	dev_dbg(dev, "bIrdaRateSniff=%x\n", desc->bIrdaRateSniff);
+	dev_dbg(dev, "bMaxUnicastList=%x\n", desc->bMaxUnicastList);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_find_class_desc(dev, ifnum)
+ *
+ *    Returns instance of IrDA class descriptor, or NULL if not found
+ *
+ * The class descriptor is some extra info that IrDA USB devices will
+ * offer to us, describing their IrDA characteristics. We will use that in
+ * irda_usb_init_qos()
+ *
+ * Based on the same function in drivers/net/irda/irda-usb.c
+ */
+static struct usb_irda_cs_descriptor *
+irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum)
+{
+	struct usb_device *dev = serial->dev;
+	struct usb_irda_cs_descriptor *desc;
+	int ret;
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return NULL;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_CS_IRDA_GET_CLASS_DESC,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			0, ifnum, desc, sizeof(*desc), 1000);
+
+	dev_dbg(&serial->dev->dev, "%s -  ret=%d\n", __func__, ret);
+	if (ret < (int)sizeof(*desc)) {
+		dev_dbg(&serial->dev->dev,
+			"%s - class descriptor read %s (%d)\n", __func__,
+			(ret < 0) ? "failed" : "too short", ret);
+		goto error;
+	}
+	if (desc->bDescriptorType != USB_DT_CS_IRDA) {
+		dev_dbg(&serial->dev->dev, "%s - bad class descriptor type\n",
+			__func__);
+		goto error;
+	}
+
+	irda_usb_dump_class_desc(serial, desc);
+	return desc;
+
+error:
+	kfree(desc);
+	return NULL;
+}
+
+static u8 ir_xbof_change(u8 xbof)
+{
+	u8 result;
+
+	/* reference irda-usb.c */
+	switch (xbof) {
+	case 48:
+		result = 0x10;
+		break;
+	case 28:
+	case 24:
+		result = 0x20;
+		break;
+	default:
+	case 12:
+		result = 0x30;
+		break;
+	case  5:
+	case  6:
+		result = 0x40;
+		break;
+	case  3:
+		result = 0x50;
+		break;
+	case  2:
+		result = 0x60;
+		break;
+	case  1:
+		result = 0x70;
+		break;
+	case  0:
+		result = 0x80;
+		break;
+	}
+
+	return(result);
+}
+
+static int ir_startup(struct usb_serial *serial)
+{
+	struct usb_irda_cs_descriptor *irda_desc;
+	int rates;
+
+	irda_desc = irda_usb_find_class_desc(serial, 0);
+	if (!irda_desc) {
+		dev_err(&serial->dev->dev,
+			"IRDA class descriptor not found, device not bound\n");
+		return -ENODEV;
+	}
+
+	rates = le16_to_cpu(irda_desc->wBaudRate);
+
+	dev_dbg(&serial->dev->dev,
+		"%s - Baud rates supported:%s%s%s%s%s%s%s%s%s\n",
+		__func__,
+		(rates & USB_IRDA_BR_2400) ? " 2400" : "",
+		(rates & USB_IRDA_BR_9600) ? " 9600" : "",
+		(rates & USB_IRDA_BR_19200) ? " 19200" : "",
+		(rates & USB_IRDA_BR_38400) ? " 38400" : "",
+		(rates & USB_IRDA_BR_57600) ? " 57600" : "",
+		(rates & USB_IRDA_BR_115200) ? " 115200" : "",
+		(rates & USB_IRDA_BR_576000) ? " 576000" : "",
+		(rates & USB_IRDA_BR_1152000) ? " 1152000" : "",
+		(rates & USB_IRDA_BR_4000000) ? " 4000000" : "");
+
+	switch (irda_desc->bmAdditionalBOFs) {
+	case USB_IRDA_AB_48:
+		ir_add_bof = 48;
+		break;
+	case USB_IRDA_AB_24:
+		ir_add_bof = 24;
+		break;
+	case USB_IRDA_AB_12:
+		ir_add_bof = 12;
+		break;
+	case USB_IRDA_AB_6:
+		ir_add_bof = 6;
+		break;
+	case USB_IRDA_AB_3:
+		ir_add_bof = 3;
+		break;
+	case USB_IRDA_AB_2:
+		ir_add_bof = 2;
+		break;
+	case USB_IRDA_AB_1:
+		ir_add_bof = 1;
+		break;
+	case USB_IRDA_AB_0:
+		ir_add_bof = 0;
+		break;
+	default:
+		break;
+	}
+
+	kfree(irda_desc);
+
+	return 0;
+}
+
+static int ir_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+		port->write_urbs[i]->transfer_flags = URB_ZERO_PACKET;
+
+	/* Start reading from the device */
+	return usb_serial_generic_open(tty, port);
+}
+
+static int ir_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	unsigned char *buf = dest;
+	int count;
+
+	/*
+	 * The first byte of the packet we send to the device contains an
+	 * inbound header which indicates an additional number of BOFs and
+	 * a baud rate change.
+	 *
+	 * See section 5.4.2.2 of the USB IrDA spec.
+	 */
+	*buf = ir_xbof | ir_baud;
+
+	count = kfifo_out_locked(&port->write_fifo, buf + 1, size - 1,
+								&port->lock);
+	return count + 1;
+}
+
+static void ir_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+
+	if (!urb->actual_length)
+		return;
+	/*
+	 * The first byte of the packet we get from the device
+	 * contains a busy indicator and baud rate change.
+	 * See section 5.4.1.2 of the USB IrDA spec.
+	 */
+	if (*data & 0x0f)
+		ir_baud = *data & 0x0f;
+
+	if (urb->actual_length == 1)
+		return;
+
+	tty_insert_flip_string(&port->port, data + 1, urb->actual_length - 1);
+	tty_flip_buffer_push(&port->port);
+}
+
+static void ir_set_termios_callback(struct urb *urb)
+{
+	kfree(urb->transfer_buffer);
+
+	if (urb->status)
+		dev_dbg(&urb->dev->dev, "%s - non-zero urb status: %d\n",
+			__func__, urb->status);
+}
+
+static void ir_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct urb *urb;
+	unsigned char *transfer_buffer;
+	int result;
+	speed_t baud;
+	int ir_baud;
+
+	baud = tty_get_baud_rate(tty);
+
+	/*
+	 * FIXME, we should compare the baud request against the
+	 * capability stated in the IR header that we got in the
+	 * startup function.
+	 */
+
+	switch (baud) {
+	case 2400:
+		ir_baud = USB_IRDA_BR_2400;
+		break;
+	case 9600:
+		ir_baud = USB_IRDA_BR_9600;
+		break;
+	case 19200:
+		ir_baud = USB_IRDA_BR_19200;
+		break;
+	case 38400:
+		ir_baud = USB_IRDA_BR_38400;
+		break;
+	case 57600:
+		ir_baud = USB_IRDA_BR_57600;
+		break;
+	case 115200:
+		ir_baud = USB_IRDA_BR_115200;
+		break;
+	case 576000:
+		ir_baud = USB_IRDA_BR_576000;
+		break;
+	case 1152000:
+		ir_baud = USB_IRDA_BR_1152000;
+		break;
+	case 4000000:
+		ir_baud = USB_IRDA_BR_4000000;
+		break;
+	default:
+		ir_baud = USB_IRDA_BR_9600;
+		baud = 9600;
+	}
+
+	if (xbof == -1)
+		ir_xbof = ir_xbof_change(ir_add_bof);
+	else
+		ir_xbof = ir_xbof_change(xbof) ;
+
+	/* Only speed changes are supported */
+	tty_termios_copy_hw(&tty->termios, old_termios);
+	tty_encode_baud_rate(tty, baud, baud);
+
+	/*
+	 * send the baud change out on an "empty" data packet
+	 */
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return;
+
+	transfer_buffer = kmalloc(1, GFP_KERNEL);
+	if (!transfer_buffer)
+		goto err_buf;
+
+	*transfer_buffer = ir_xbof | ir_baud;
+
+	usb_fill_bulk_urb(
+		urb,
+		port->serial->dev,
+		usb_sndbulkpipe(port->serial->dev,
+			port->bulk_out_endpointAddress),
+		transfer_buffer,
+		1,
+		ir_set_termios_callback,
+		port);
+
+	urb->transfer_flags = URB_ZERO_PACKET;
+
+	result = usb_submit_urb(urb, GFP_KERNEL);
+	if (result) {
+		dev_err(&port->dev, "%s - failed to submit urb: %d\n",
+							__func__, result);
+		goto err_subm;
+	}
+
+	usb_free_urb(urb);
+
+	return;
+err_subm:
+	kfree(transfer_buffer);
+err_buf:
+	usb_free_urb(urb);
+}
+
+static int __init ir_init(void)
+{
+	if (buffer_size) {
+		ir_device.bulk_in_size = buffer_size;
+		ir_device.bulk_out_size = buffer_size;
+	}
+
+	return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ir_id_table);
+}
+
+static void __exit ir_exit(void)
+{
+	usb_serial_deregister_drivers(serial_drivers);
+}
+
+
+module_init(ir_init);
+module_exit(ir_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(xbof, int, 0);
+MODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers");
+
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
new file mode 100644
index 0000000..449e89d
--- /dev/null
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Infinity Unlimited USB Phoenix driver
+ *
+ * Copyright (C) 2010 James Courtier-Dutton (James@superbug.co.uk)
+
+ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
+ *
+ * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
+ *
+ *  And tested with help of WB Electronics
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "iuu_phoenix.h"
+#include <linux/random.h>
+
+#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(IUU_USB_VENDOR_ID, IUU_USB_PRODUCT_ID)},
+	{}			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* turbo parameter */
+static int boost = 100;
+static int clockmode = 1;
+static int cdmode = 1;
+static int iuu_cardin;
+static int iuu_cardout;
+static bool xmas;
+static int vcc_default = 5;
+
+static int iuu_create_sysfs_attrs(struct usb_serial_port *port);
+static int iuu_remove_sysfs_attrs(struct usb_serial_port *port);
+static void read_rxcmd_callback(struct urb *urb);
+
+struct iuu_private {
+	spinlock_t lock;	/* store irq state */
+	u8 line_status;
+	int tiostatus;		/* store IUART SIGNAL for tiocmget call */
+	u8 reset;		/* if 1 reset is needed */
+	int poll;		/* number of poll */
+	u8 *writebuf;		/* buffer for writing to device */
+	int writelen;		/* num of byte to write to device */
+	u8 *buf;		/* used for initialize speed */
+	u8 len;
+	int vcc;		/* vcc (either 3 or 5 V) */
+	u32 boost;
+	u32 clk;
+};
+
+static int iuu_port_probe(struct usb_serial_port *port)
+{
+	struct iuu_private *priv;
+	int ret;
+
+	priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->buf = kzalloc(256, GFP_KERNEL);
+	if (!priv->buf) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	priv->writebuf = kzalloc(256, GFP_KERNEL);
+	if (!priv->writebuf) {
+		kfree(priv->buf);
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	priv->vcc = vcc_default;
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	ret = iuu_create_sysfs_attrs(port);
+	if (ret) {
+		kfree(priv->writebuf);
+		kfree(priv->buf);
+		kfree(priv);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int iuu_port_remove(struct usb_serial_port *port)
+{
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	iuu_remove_sysfs_attrs(port);
+	kfree(priv->writebuf);
+	kfree(priv->buf);
+	kfree(priv);
+
+	return 0;
+}
+
+static int iuu_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	/* FIXME: locking on tiomstatus */
+	dev_dbg(&port->dev, "%s msg : SET = 0x%04x, CLEAR = 0x%04x\n",
+		__func__, set, clear);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) {
+		dev_dbg(&port->dev, "%s TIOCMSET RESET called !!!\n", __func__);
+		priv->reset = 1;
+	}
+	if (set & TIOCM_RTS)
+		priv->tiostatus = TIOCM_RTS;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return 0;
+}
+
+/* This is used to provide a carrier detect mechanism
+ * When a card is present, the response is 0x00
+ * When no card , the reader respond with TIOCM_CD
+ * This is known as CD autodetect mechanism
+ */
+static int iuu_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = priv->tiostatus;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return rc;
+}
+
+static void iuu_rxcmd(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int result;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
+		/* error stop all */
+		return;
+	}
+
+
+	memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1);
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 1,
+			  read_rxcmd_callback, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+}
+
+static int iuu_reset(struct usb_serial_port *port, u8 wt)
+{
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	int result;
+	char *buf_ptr = port->write_urb->transfer_buffer;
+
+	/* Prepare the reset sequence */
+
+	*buf_ptr++ = IUU_RST_SET;
+	*buf_ptr++ = IUU_DELAY_MS;
+	*buf_ptr++ = wt;
+	*buf_ptr = IUU_RST_CLEAR;
+
+	/* send the sequence */
+
+	usb_fill_bulk_urb(port->write_urb,
+			  port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 4, iuu_rxcmd, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	priv->reset = 0;
+	return result;
+}
+
+/* Status Function
+ * Return value is
+ * 0x00 = no card
+ * 0x01 = smartcard
+ * 0x02 = sim card
+ */
+static void iuu_update_status_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	u8 *st;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
+		/* error stop all */
+		return;
+	}
+
+	st = urb->transfer_buffer;
+	dev_dbg(&port->dev, "%s - enter\n", __func__);
+	if (urb->actual_length == 1) {
+		switch (st[0]) {
+		case 0x1:
+			priv->tiostatus = iuu_cardout;
+			break;
+		case 0x0:
+			priv->tiostatus = iuu_cardin;
+			break;
+		default:
+			priv->tiostatus = iuu_cardin;
+		}
+	}
+	iuu_rxcmd(urb);
+}
+
+static void iuu_status_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int result;
+	int status = urb->status;
+
+	dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
+	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+			  usb_rcvbulkpipe(port->serial->dev,
+					  port->bulk_in_endpointAddress),
+			  port->read_urb->transfer_buffer, 256,
+			  iuu_update_status_callback, port);
+	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+}
+
+static int iuu_status(struct usb_serial_port *port)
+{
+	int result;
+
+	memset(port->write_urb->transfer_buffer, IUU_GET_STATE_REGISTER, 1);
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 1,
+			  iuu_status_callback, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	return result;
+
+}
+
+static int bulk_immediate(struct usb_serial_port *port, u8 *buf, u8 count)
+{
+	int status;
+	struct usb_serial *serial = port->serial;
+	int actual = 0;
+
+	/* send the data out the bulk port */
+
+	status =
+	    usb_bulk_msg(serial->dev,
+			 usb_sndbulkpipe(serial->dev,
+					 port->bulk_out_endpointAddress), buf,
+			 count, &actual, 1000);
+
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - error = %2x\n", __func__, status);
+	else
+		dev_dbg(&port->dev, "%s - write OK !\n", __func__);
+	return status;
+}
+
+static int read_immediate(struct usb_serial_port *port, u8 *buf, u8 count)
+{
+	int status;
+	struct usb_serial *serial = port->serial;
+	int actual = 0;
+
+	/* send the data out the bulk port */
+	status =
+	    usb_bulk_msg(serial->dev,
+			 usb_rcvbulkpipe(serial->dev,
+					 port->bulk_in_endpointAddress), buf,
+			 count, &actual, 1000);
+
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - error = %2x\n", __func__, status);
+	else
+		dev_dbg(&port->dev, "%s - read OK !\n", __func__);
+	return status;
+}
+
+static int iuu_led(struct usb_serial_port *port, unsigned int R,
+		   unsigned int G, unsigned int B, u8 f)
+{
+	int status;
+	u8 *buf;
+	buf = kmalloc(8, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_SET_LED;
+	buf[1] = R & 0xFF;
+	buf[2] = (R >> 8) & 0xFF;
+	buf[3] = G & 0xFF;
+	buf[4] = (G >> 8) & 0xFF;
+	buf[5] = B & 0xFF;
+	buf[6] = (B >> 8) & 0xFF;
+	buf[7] = f;
+	status = bulk_immediate(port, buf, 8);
+	kfree(buf);
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - led error status = %2x\n", __func__, status);
+	else
+		dev_dbg(&port->dev, "%s - led OK !\n", __func__);
+	return IUU_OPERATION_OK;
+}
+
+static void iuu_rgbf_fill_buffer(u8 *buf, u8 r1, u8 r2, u8 g1, u8 g2, u8 b1,
+				 u8 b2, u8 freq)
+{
+	*buf++ = IUU_SET_LED;
+	*buf++ = r1;
+	*buf++ = r2;
+	*buf++ = g1;
+	*buf++ = g2;
+	*buf++ = b1;
+	*buf++ = b2;
+	*buf = freq;
+}
+
+static void iuu_led_activity_on(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int result;
+	char *buf_ptr = port->write_urb->transfer_buffer;
+	*buf_ptr++ = IUU_SET_LED;
+	if (xmas) {
+		get_random_bytes(buf_ptr, 6);
+		*(buf_ptr+7) = 1;
+	} else {
+		iuu_rgbf_fill_buffer(buf_ptr, 255, 255, 0, 0, 0, 0, 255);
+	}
+
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 8 ,
+			  iuu_rxcmd, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+}
+
+static void iuu_led_activity_off(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int result;
+	char *buf_ptr = port->write_urb->transfer_buffer;
+	if (xmas) {
+		iuu_rxcmd(urb);
+		return;
+	} else {
+		*buf_ptr++ = IUU_SET_LED;
+		iuu_rgbf_fill_buffer(buf_ptr, 0, 0, 255, 255, 0, 0, 255);
+	}
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 8 ,
+			  iuu_rxcmd, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+}
+
+
+
+static int iuu_clk(struct usb_serial_port *port, int dwFrq)
+{
+	int status;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	int Count = 0;
+	u8 FrqGenAdr = 0x69;
+	u8 DIV = 0;		/* 8bit */
+	u8 XDRV = 0;		/* 8bit */
+	u8 PUMP = 0;		/* 3bit */
+	u8 PBmsb = 0;		/* 2bit */
+	u8 PBlsb = 0;		/* 8bit */
+	u8 PO = 0;		/* 1bit */
+	u8 Q = 0;		/* 7bit */
+	/* 24bit = 3bytes */
+	unsigned int P = 0;
+	unsigned int P2 = 0;
+	int frq = (int)dwFrq;
+
+	if (frq == 0) {
+		priv->buf[Count++] = IUU_UART_WRITE_I2C;
+		priv->buf[Count++] = FrqGenAdr << 1;
+		priv->buf[Count++] = 0x09;
+		priv->buf[Count++] = 0x00;
+
+		status = bulk_immediate(port, (u8 *) priv->buf, Count);
+		if (status != 0) {
+			dev_dbg(&port->dev, "%s - write error\n", __func__);
+			return status;
+		}
+	} else if (frq == 3579000) {
+		DIV = 100;
+		P = 1193;
+		Q = 40;
+		XDRV = 0;
+	} else if (frq == 3680000) {
+		DIV = 105;
+		P = 161;
+		Q = 5;
+		XDRV = 0;
+	} else if (frq == 6000000) {
+		DIV = 66;
+		P = 66;
+		Q = 2;
+		XDRV = 0x28;
+	} else {
+		unsigned int result = 0;
+		unsigned int tmp = 0;
+		unsigned int check;
+		unsigned int check2;
+		char found = 0x00;
+		unsigned int lQ = 2;
+		unsigned int lP = 2055;
+		unsigned int lDiv = 4;
+
+		for (lQ = 2; lQ <= 47 && !found; lQ++)
+			for (lP = 2055; lP >= 8 && !found; lP--)
+				for (lDiv = 4; lDiv <= 127 && !found; lDiv++) {
+					tmp = (12000000 / lDiv) * (lP / lQ);
+					if (abs((int)(tmp - frq)) <
+					    abs((int)(frq - result))) {
+						check2 = (12000000 / lQ);
+						if (check2 < 250000)
+							continue;
+						check = (12000000 / lQ) * lP;
+						if (check > 400000000)
+							continue;
+						if (check < 100000000)
+							continue;
+						if (lDiv < 4 || lDiv > 127)
+							continue;
+						result = tmp;
+						P = lP;
+						DIV = lDiv;
+						Q = lQ;
+						if (result == frq)
+							found = 0x01;
+					}
+				}
+	}
+	P2 = ((P - PO) / 2) - 4;
+	PUMP = 0x04;
+	PBmsb = (P2 >> 8 & 0x03);
+	PBlsb = P2 & 0xFF;
+	PO = (P >> 10) & 0x01;
+	Q = Q - 2;
+
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x09;
+	priv->buf[Count++] = 0x20;	/* Adr = 0x09 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x0C;
+	priv->buf[Count++] = DIV;	/* Adr = 0x0C */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x12;
+	priv->buf[Count++] = XDRV;	/* Adr = 0x12 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x13;
+	priv->buf[Count++] = 0x6B;	/* Adr = 0x13 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x40;
+	priv->buf[Count++] = (0xC0 | ((PUMP & 0x07) << 2)) |
+			     (PBmsb & 0x03);	/* Adr = 0x40 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x41;
+	priv->buf[Count++] = PBlsb;	/* Adr = 0x41 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x42;
+	priv->buf[Count++] = Q | (((PO & 0x01) << 7));	/* Adr = 0x42 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x44;
+	priv->buf[Count++] = (char)0xFF;	/* Adr = 0x44 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x45;
+	priv->buf[Count++] = (char)0xFE;	/* Adr = 0x45 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x46;
+	priv->buf[Count++] = 0x7F;	/* Adr = 0x46 */
+	priv->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	priv->buf[Count++] = FrqGenAdr << 1;
+	priv->buf[Count++] = 0x47;
+	priv->buf[Count++] = (char)0x84;	/* Adr = 0x47 */
+
+	status = bulk_immediate(port, (u8 *) priv->buf, Count);
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - write error\n", __func__);
+	return status;
+}
+
+static int iuu_uart_flush(struct usb_serial_port *port)
+{
+	struct device *dev = &port->dev;
+	int i;
+	int status;
+	u8 rxcmd = IUU_UART_RX;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	if (iuu_led(port, 0xF000, 0, 0, 0xFF) < 0)
+		return -EIO;
+
+	for (i = 0; i < 2; i++) {
+		status = bulk_immediate(port, &rxcmd, 1);
+		if (status != IUU_OPERATION_OK) {
+			dev_dbg(dev, "%s - uart_flush_write error\n", __func__);
+			return status;
+		}
+
+		status = read_immediate(port, &priv->len, 1);
+		if (status != IUU_OPERATION_OK) {
+			dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
+			return status;
+		}
+
+		if (priv->len > 0) {
+			dev_dbg(dev, "%s - uart_flush datalen is : %i\n", __func__, priv->len);
+			status = read_immediate(port, priv->buf, priv->len);
+			if (status != IUU_OPERATION_OK) {
+				dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
+				return status;
+			}
+		}
+	}
+	dev_dbg(dev, "%s - uart_flush_read OK!\n", __func__);
+	iuu_led(port, 0, 0xF000, 0, 0xFF);
+	return status;
+}
+
+static void read_buf_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	if (status) {
+		if (status == -EPROTO) {
+			/* reschedule needed */
+		}
+		return;
+	}
+
+	dev_dbg(&port->dev, "%s - %i chars to write\n", __func__, urb->actual_length);
+
+	if (urb->actual_length) {
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+	iuu_led_activity_on(urb);
+}
+
+static int iuu_bulk_write(struct usb_serial_port *port)
+{
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int result;
+	int buf_len;
+	char *buf_ptr = port->write_urb->transfer_buffer;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	*buf_ptr++ = IUU_UART_ESC;
+	*buf_ptr++ = IUU_UART_TX;
+	*buf_ptr++ = priv->writelen;
+
+	memcpy(buf_ptr, priv->writebuf, priv->writelen);
+	buf_len = priv->writelen;
+	priv->writelen = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	dev_dbg(&port->dev, "%s - writing %i chars : %*ph\n", __func__,
+		buf_len, buf_len, buf_ptr);
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, buf_len + 3,
+			  iuu_rxcmd, port);
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	usb_serial_port_softint(port);
+	return result;
+}
+
+static int iuu_read_buf(struct usb_serial_port *port, int len)
+{
+	int result;
+
+	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+			  usb_rcvbulkpipe(port->serial->dev,
+					  port->bulk_in_endpointAddress),
+			  port->read_urb->transfer_buffer, len,
+			  read_buf_callback, port);
+	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+	return result;
+}
+
+static void iuu_uart_read_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int status = urb->status;
+	int error = 0;
+	int len = 0;
+	unsigned char *data = urb->transfer_buffer;
+	priv->poll++;
+
+	if (status) {
+		dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
+		/* error stop all */
+		return;
+	}
+
+	if (urb->actual_length == 1)
+		len = (int) data[0];
+
+	if (urb->actual_length > 1) {
+		dev_dbg(&port->dev, "%s - urb->actual_length = %i\n", __func__,
+		    urb->actual_length);
+		error = 1;
+		return;
+	}
+	/* if len > 0 call readbuf */
+
+	if (len > 0 && error == 0) {
+		dev_dbg(&port->dev, "%s - call read buf - len to read is %i\n",
+			__func__, len);
+		status = iuu_read_buf(port, len);
+		return;
+	}
+	/* need to update status  ? */
+	if (priv->poll > 99) {
+		status = iuu_status(port);
+		priv->poll = 0;
+		return;
+	}
+
+	/* reset waiting ? */
+
+	if (priv->reset == 1) {
+		status = iuu_reset(port, 0xC);
+		return;
+	}
+	/* Writebuf is waiting */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->writelen > 0) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		status = iuu_bulk_write(port);
+		return;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+	/* if nothing to write call again rxcmd */
+	dev_dbg(&port->dev, "%s - rxcmd recall\n", __func__);
+	iuu_led_activity_off(urb);
+}
+
+static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port,
+			  const u8 *buf, int count)
+{
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	if (count > 256)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* fill the buffer */
+	memcpy(priv->writebuf + priv->writelen, buf, count);
+	priv->writelen += count;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return count;
+}
+
+static void read_rxcmd_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int result;
+	int status = urb->status;
+
+	if (status) {
+		/* error stop all */
+		return;
+	}
+
+	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+			  usb_rcvbulkpipe(port->serial->dev,
+					  port->bulk_in_endpointAddress),
+			  port->read_urb->transfer_buffer, 256,
+			  iuu_uart_read_callback, port);
+	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+	dev_dbg(&port->dev, "%s - submit result = %d\n", __func__, result);
+}
+
+static int iuu_uart_on(struct usb_serial_port *port)
+{
+	int status;
+	u8 *buf;
+
+	buf = kmalloc(4, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_UART_ENABLE;
+	buf[1] = (u8) ((IUU_BAUD_9600 >> 8) & 0x00FF);
+	buf[2] = (u8) (0x00FF & IUU_BAUD_9600);
+	buf[3] = (u8) (0x0F0 & IUU_ONE_STOP_BIT) | (0x07 & IUU_PARITY_EVEN);
+
+	status = bulk_immediate(port, buf, 4);
+	if (status != IUU_OPERATION_OK) {
+		dev_dbg(&port->dev, "%s - uart_on error\n", __func__);
+		goto uart_enable_failed;
+	}
+	/*  iuu_reset() the card after iuu_uart_on() */
+	status = iuu_uart_flush(port);
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - uart_flush error\n", __func__);
+uart_enable_failed:
+	kfree(buf);
+	return status;
+}
+
+/*  Disables the IUU UART (a.k.a. the Phoenix voiderface) */
+static int iuu_uart_off(struct usb_serial_port *port)
+{
+	int status;
+	u8 *buf;
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	buf[0] = IUU_UART_DISABLE;
+
+	status = bulk_immediate(port, buf, 1);
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - uart_off error\n", __func__);
+
+	kfree(buf);
+	return status;
+}
+
+static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
+			 u32 *actual, u8 parity)
+{
+	int status;
+	u32 baud;
+	u8 *dataout;
+	u8 DataCount = 0;
+	u8 T1Frekvens = 0;
+	u8 T1reload = 0;
+	unsigned int T1FrekvensHZ = 0;
+
+	dev_dbg(&port->dev, "%s - enter baud_base=%d\n", __func__, baud_base);
+	dataout = kmalloc(5, GFP_KERNEL);
+
+	if (!dataout)
+		return -ENOMEM;
+	/*baud = (((priv->clk / 35) * baud_base) / 100000); */
+	baud = baud_base;
+
+	if (baud < 1200 || baud > 230400) {
+		kfree(dataout);
+		return IUU_INVALID_PARAMETER;
+	}
+	if (baud > 977) {
+		T1Frekvens = 3;
+		T1FrekvensHZ = 500000;
+	}
+
+	if (baud > 3906) {
+		T1Frekvens = 2;
+		T1FrekvensHZ = 2000000;
+	}
+
+	if (baud > 11718) {
+		T1Frekvens = 1;
+		T1FrekvensHZ = 6000000;
+	}
+
+	if (baud > 46875) {
+		T1Frekvens = 0;
+		T1FrekvensHZ = 24000000;
+	}
+
+	T1reload = 256 - (u8) (T1FrekvensHZ / (baud * 2));
+
+	/*  magic number here:  ENTER_FIRMWARE_UPDATE; */
+	dataout[DataCount++] = IUU_UART_ESC;
+	/*  magic number here:  CHANGE_BAUD; */
+	dataout[DataCount++] = IUU_UART_CHANGE;
+	dataout[DataCount++] = T1Frekvens;
+	dataout[DataCount++] = T1reload;
+
+	*actual = (T1FrekvensHZ / (256 - T1reload)) / 2;
+
+	switch (parity & 0x0F) {
+	case IUU_PARITY_NONE:
+		dataout[DataCount++] = 0x00;
+		break;
+	case IUU_PARITY_EVEN:
+		dataout[DataCount++] = 0x01;
+		break;
+	case IUU_PARITY_ODD:
+		dataout[DataCount++] = 0x02;
+		break;
+	case IUU_PARITY_MARK:
+		dataout[DataCount++] = 0x03;
+		break;
+	case IUU_PARITY_SPACE:
+		dataout[DataCount++] = 0x04;
+		break;
+	default:
+		kfree(dataout);
+		return IUU_INVALID_PARAMETER;
+		break;
+	}
+
+	switch (parity & 0xF0) {
+	case IUU_ONE_STOP_BIT:
+		dataout[DataCount - 1] |= IUU_ONE_STOP_BIT;
+		break;
+
+	case IUU_TWO_STOP_BITS:
+		dataout[DataCount - 1] |= IUU_TWO_STOP_BITS;
+		break;
+	default:
+		kfree(dataout);
+		return IUU_INVALID_PARAMETER;
+		break;
+	}
+
+	status = bulk_immediate(port, dataout, DataCount);
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - uart_off error\n", __func__);
+	kfree(dataout);
+	return status;
+}
+
+static void iuu_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	const u32 supported_mask = CMSPAR|PARENB|PARODD;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned int cflag = tty->termios.c_cflag;
+	int status;
+	u32 actual;
+	u32 parity;
+	int csize = CS7;
+	int baud;
+	u32 newval = cflag & supported_mask;
+
+	/* Just use the ospeed. ispeed should be the same. */
+	baud = tty->termios.c_ospeed;
+
+	dev_dbg(&port->dev, "%s - enter c_ospeed or baud=%d\n", __func__, baud);
+
+	/* compute the parity parameter */
+	parity = 0;
+	if (cflag & CMSPAR) {	/* Using mark space */
+		if (cflag & PARODD)
+			parity |= IUU_PARITY_SPACE;
+		else
+			parity |= IUU_PARITY_MARK;
+	} else if (!(cflag & PARENB)) {
+		parity |= IUU_PARITY_NONE;
+		csize = CS8;
+	} else if (cflag & PARODD)
+		parity |= IUU_PARITY_ODD;
+	else
+		parity |= IUU_PARITY_EVEN;
+
+	parity |= (cflag & CSTOPB ? IUU_TWO_STOP_BITS : IUU_ONE_STOP_BIT);
+
+	/* set it */
+	status = iuu_uart_baud(port,
+			baud * priv->boost / 100,
+			&actual, parity);
+
+	/* set the termios value to the real one, so the user now what has
+	 * changed. We support few fields so its easies to copy the old hw
+	 * settings back over and then adjust them
+	 */
+	if (old_termios)
+		tty_termios_copy_hw(&tty->termios, old_termios);
+	if (status != 0)	/* Set failed - return old bits */
+		return;
+	/* Re-encode speed, parity and csize */
+	tty_encode_baud_rate(tty, baud, baud);
+	tty->termios.c_cflag &= ~(supported_mask|CSIZE);
+	tty->termios.c_cflag |= newval | csize;
+}
+
+static void iuu_close(struct usb_serial_port *port)
+{
+	/* iuu_led (port,255,0,0,0); */
+
+	iuu_uart_off(port);
+
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+
+	iuu_led(port, 0, 0, 0xF000, 0xFF);
+}
+
+static void iuu_init_termios(struct tty_struct *tty)
+{
+	tty->termios = tty_std_termios;
+	tty->termios.c_cflag = CLOCAL | CREAD | CS8 | B9600
+				| TIOCM_CTS | CSTOPB | PARENB;
+	tty->termios.c_ispeed = 9600;
+	tty->termios.c_ospeed = 9600;
+	tty->termios.c_lflag = 0;
+	tty->termios.c_oflag = 0;
+	tty->termios.c_iflag = 0;
+}
+
+static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct device *dev = &port->dev;
+	int result;
+	int baud;
+	u32 actual;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	baud = tty->termios.c_ospeed;
+
+	dev_dbg(dev, "%s - baud %d\n", __func__, baud);
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	priv->poll = 0;
+
+#define SOUP(a, b, c, d)  do { \
+	result = usb_control_msg(port->serial->dev,	\
+				usb_sndctrlpipe(port->serial->dev, 0),	\
+				b, a, c, d, NULL, 0, 1000); \
+	dev_dbg(dev, "0x%x:0x%x:0x%x:0x%x  %d\n", a, b, c, d, result); } while (0)
+
+	/*  This is not UART related but IUU USB driver related or something */
+	/*  like that. Basically no IUU will accept any commands from the USB */
+	/*  host unless it has received the following message */
+	/* sprintf(buf ,"%c%c%c%c",0x03,0x02,0x02,0x0); */
+
+	SOUP(0x03, 0x02, 0x02, 0x0);
+
+	iuu_led(port, 0xF000, 0xF000, 0, 0xFF);
+	iuu_uart_on(port);
+	if (boost < 100)
+		boost = 100;
+	priv->boost = boost;
+	switch (clockmode) {
+	case 2:		/*  3.680 Mhz */
+		priv->clk = IUU_CLK_3680000;
+		iuu_clk(port, IUU_CLK_3680000 * boost / 100);
+		result =
+		    iuu_uart_baud(port, baud * boost / 100, &actual,
+				  IUU_PARITY_EVEN);
+		break;
+	case 3:		/*  6.00 Mhz */
+		iuu_clk(port, IUU_CLK_6000000 * boost / 100);
+		priv->clk = IUU_CLK_6000000;
+		/* Ratio of 6000000 to 3500000 for baud 9600 */
+		result =
+		    iuu_uart_baud(port, 16457 * boost / 100, &actual,
+				  IUU_PARITY_EVEN);
+		break;
+	default:		/*  3.579 Mhz */
+		iuu_clk(port, IUU_CLK_3579000 * boost / 100);
+		priv->clk = IUU_CLK_3579000;
+		result =
+		    iuu_uart_baud(port, baud * boost / 100, &actual,
+				  IUU_PARITY_EVEN);
+	}
+
+	/* set the cardin cardout signals */
+	switch (cdmode) {
+	case 0:
+		iuu_cardin = 0;
+		iuu_cardout = 0;
+		break;
+	case 1:
+		iuu_cardin = TIOCM_CD;
+		iuu_cardout =  0;
+		break;
+	case 2:
+		iuu_cardin = 0;
+		iuu_cardout = TIOCM_CD;
+		break;
+	case 3:
+		iuu_cardin = TIOCM_DSR;
+		iuu_cardout = 0;
+		break;
+	case 4:
+		iuu_cardin = 0;
+		iuu_cardout = TIOCM_DSR;
+		break;
+	case 5:
+		iuu_cardin = TIOCM_CTS;
+		iuu_cardout = 0;
+		break;
+	case 6:
+		iuu_cardin = 0;
+		iuu_cardout = TIOCM_CTS;
+		break;
+	case 7:
+		iuu_cardin = TIOCM_RNG;
+		iuu_cardout = 0;
+		break;
+	case 8:
+		iuu_cardin = 0;
+		iuu_cardout = TIOCM_RNG;
+	}
+
+	iuu_uart_flush(port);
+
+	dev_dbg(dev, "%s - initialization done\n", __func__);
+
+	memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1);
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			  usb_sndbulkpipe(port->serial->dev,
+					  port->bulk_out_endpointAddress),
+			  port->write_urb->transfer_buffer, 1,
+			  read_rxcmd_callback, port);
+	result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+	if (result) {
+		dev_err(dev, "%s - failed submitting read urb, error %d\n", __func__, result);
+		iuu_close(port);
+	} else {
+		dev_dbg(dev, "%s - rxcmd OK\n", __func__);
+	}
+
+	return result;
+}
+
+/* how to change VCC */
+static int iuu_vcc_set(struct usb_serial_port *port, unsigned int vcc)
+{
+	int status;
+	u8 *buf;
+
+	buf = kmalloc(5, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_SET_VCC;
+	buf[1] = vcc & 0xFF;
+	buf[2] = (vcc >> 8) & 0xFF;
+	buf[3] = (vcc >> 16) & 0xFF;
+	buf[4] = (vcc >> 24) & 0xFF;
+
+	status = bulk_immediate(port, buf, 5);
+	kfree(buf);
+
+	if (status != IUU_OPERATION_OK)
+		dev_dbg(&port->dev, "%s - vcc error status = %2x\n", __func__, status);
+	else
+		dev_dbg(&port->dev, "%s - vcc OK !\n", __func__);
+
+	return status;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t vcc_mode_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	return sprintf(buf, "%d\n", priv->vcc);
+}
+
+static ssize_t vcc_mode_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	unsigned long v;
+
+	if (kstrtoul(buf, 10, &v)) {
+		dev_err(dev, "%s - vcc_mode: %s is not a unsigned long\n",
+				__func__, buf);
+		goto fail_store_vcc_mode;
+	}
+
+	dev_dbg(dev, "%s: setting vcc_mode = %ld\n", __func__, v);
+
+	if ((v != 3) && (v != 5)) {
+		dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v);
+	} else {
+		iuu_vcc_set(port, v);
+		priv->vcc = v;
+	}
+fail_store_vcc_mode:
+	return count;
+}
+static DEVICE_ATTR_RW(vcc_mode);
+
+static int iuu_create_sysfs_attrs(struct usb_serial_port *port)
+{
+	return device_create_file(&port->dev, &dev_attr_vcc_mode);
+}
+
+static int iuu_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+	device_remove_file(&port->dev, &dev_attr_vcc_mode);
+	return 0;
+}
+
+/*
+ * End Sysfs Attributes
+ */
+
+static struct usb_serial_driver iuu_device = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "iuu_phoenix",
+		   },
+	.id_table = id_table,
+	.num_ports = 1,
+	.num_bulk_in = 1,
+	.num_bulk_out = 1,
+	.bulk_in_size = 512,
+	.bulk_out_size = 512,
+	.open = iuu_open,
+	.close = iuu_close,
+	.write = iuu_uart_write,
+	.read_bulk_callback = iuu_uart_read_callback,
+	.tiocmget = iuu_tiocmget,
+	.tiocmset = iuu_tiocmset,
+	.set_termios = iuu_set_termios,
+	.init_termios = iuu_init_termios,
+	.port_probe = iuu_port_probe,
+	.port_remove = iuu_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&iuu_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR("Alain Degreffe eczema@ecze.com");
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(xmas, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(xmas, "Xmas colors enabled or not");
+
+module_param(boost, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(boost, "Card overclock boost (in percent 100-500)");
+
+module_param(clockmode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(clockmode, "Card clock mode (1=3.579 MHz, 2=3.680 MHz, "
+		"3=6 Mhz)");
+
+module_param(cdmode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(cdmode, "Card detect mode (0=none, 1=CD, 2=!CD, 3=DSR, "
+		 "4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING)");
+
+module_param(vcc_default, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(vcc_default, "Set default VCC (either 3 for 3.3V or 5 "
+		"for 5V). Default to 5.");
diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h
new file mode 100644
index 0000000..b400b26
--- /dev/null
+++ b/drivers/usb/serial/iuu_phoenix.h
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Infinity Unlimited USB Phoenix driver
+ *
+ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
+ *
+ *
+ * Original code taken from iuutool ( Copyright (C) 2006 Juan Carlos Borrás )
+ *
+ *  And tested with help of WB Electronics
+ */
+
+#define   IUU_USB_VENDOR_ID  0x104f
+#define   IUU_USB_PRODUCT_ID  0x0004
+#define   IUU_USB_OP_TIMEOUT  0x0200
+
+/* Programmer commands */
+
+#define IUU_NO_OPERATION   0x00
+#define IUU_GET_FIRMWARE_VERSION   0x01
+#define IUU_GET_PRODUCT_NAME   0x02
+#define IUU_GET_STATE_REGISTER   0x03
+#define IUU_SET_LED   0x04
+#define IUU_WAIT_MUS   0x05
+#define IUU_WAIT_MS   0x06
+#define IUU_GET_LOADER_VERSION   0x50
+#define IUU_RST_SET   0x52
+#define IUU_RST_CLEAR   0x53
+#define IUU_SET_VCC   0x59
+#define IUU_UART_ENABLE   0x49
+#define IUU_UART_DISABLE   0x4A
+#define IUU_UART_WRITE_I2C   0x4C
+#define IUU_UART_ESC   0x5E
+#define IUU_UART_TRAP   0x54
+#define IUU_UART_TRAP_BREAK   0x5B
+#define IUU_UART_RX   0x56
+#define IUU_AVR_ON   0x21
+#define IUU_AVR_OFF   0x22
+#define IUU_AVR_1CLK   0x23
+#define IUU_AVR_RESET   0x24
+#define IUU_AVR_RESET_PC   0x25
+#define IUU_AVR_INC_PC   0x26
+#define IUU_AVR_INCN_PC   0x27
+#define IUU_AVR_PREAD   0x29
+#define IUU_AVR_PREADN   0x2A
+#define IUU_AVR_PWRITE   0x28
+#define IUU_AVR_DREAD   0x2C
+#define IUU_AVR_DREADN   0x2D
+#define IUU_AVR_DWRITE   0x2B
+#define IUU_AVR_PWRITEN   0x2E
+#define IUU_EEPROM_ON   0x37
+#define IUU_EEPROM_OFF   0x38
+#define IUU_EEPROM_WRITE   0x39
+#define IUU_EEPROM_WRITEX   0x3A
+#define IUU_EEPROM_WRITE8   0x3B
+#define IUU_EEPROM_WRITE16   0x3C
+#define IUU_EEPROM_WRITEX32   0x3D
+#define IUU_EEPROM_WRITEX64   0x3E
+#define IUU_EEPROM_READ   0x3F
+#define IUU_EEPROM_READX   0x40
+#define IUU_EEPROM_BREAD   0x41
+#define IUU_EEPROM_BREADX   0x42
+#define IUU_PIC_CMD   0x0A
+#define IUU_PIC_CMD_LOAD   0x0B
+#define IUU_PIC_CMD_READ   0x0C
+#define IUU_PIC_ON   0x0D
+#define IUU_PIC_OFF   0x0E
+#define IUU_PIC_RESET   0x16
+#define IUU_PIC_INC_PC   0x0F
+#define IUU_PIC_INCN_PC   0x10
+#define IUU_PIC_PWRITE   0x11
+#define IUU_PIC_PREAD   0x12
+#define IUU_PIC_PREADN   0x13
+#define IUU_PIC_DWRITE   0x14
+#define IUU_PIC_DREAD   0x15
+#define IUU_UART_NOP   0x00
+#define IUU_UART_CHANGE   0x02
+#define IUU_UART_TX   0x04
+#define IUU_DELAY_MS   0x06
+
+#define IUU_OPERATION_OK   0x00
+#define IUU_DEVICE_NOT_FOUND   0x01
+#define IUU_INVALID_HANDLE   0x02
+#define IUU_INVALID_PARAMETER   0x03
+#define IUU_INVALID_voidERFACE   0x04
+#define IUU_INVALID_REQUEST_LENGTH   0x05
+#define IUU_UART_NOT_ENABLED   0x06
+#define IUU_WRITE_ERROR   0x07
+#define IUU_READ_ERROR   0x08
+#define IUU_TX_ERROR   0x09
+#define IUU_RX_ERROR   0x0A
+
+#define IUU_PARITY_NONE   0x00
+#define IUU_PARITY_EVEN   0x01
+#define IUU_PARITY_ODD   0x02
+#define IUU_PARITY_MARK   0x03
+#define IUU_PARITY_SPACE   0x04
+#define IUU_SC_INSERTED   0x01
+#define IUU_VERIFY_ERROR   0x02
+#define IUU_SIM_INSERTED   0x04
+#define IUU_TWO_STOP_BITS   0x00
+#define IUU_ONE_STOP_BIT   0x20
+#define IUU_BAUD_2400   0x0398
+#define IUU_BAUD_9600   0x0298
+#define IUU_BAUD_19200   0x0164
+#define IUU_BAUD_28800   0x0198
+#define IUU_BAUD_38400   0x01B2
+#define IUU_BAUD_57600   0x0030
+#define IUU_BAUD_115200   0x0098
+#define IUU_CLK_3579000   3579000
+#define IUU_CLK_3680000   3680000
+#define IUU_CLK_6000000   6000000
+#define IUU_FULLCARD_IN   0x01
+#define IUU_DEV_ERROR   0x02
+#define IUU_MINICARD_IN   0x04
+#define IUU_VCC_5V   0x00
+#define IUU_VCC_3V   0x01
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
new file mode 100644
index 0000000..d34779f
--- /dev/null
+++ b/drivers/usb/serial/keyspan.c
@@ -0,0 +1,3123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+  Keyspan USB to Serial Converter driver
+
+  (C) Copyright (C) 2000-2001	Hugh Blemings <hugh@blemings.org>
+  (C) Copyright (C) 2002	Greg Kroah-Hartman <greg@kroah.com>
+
+  See http://blemings.org/hugh/keyspan.html for more information.
+
+  Code in this driver inspired by and in a number of places taken
+  from Brian Warner's original Keyspan-PDA driver.
+
+  This driver has been put together with the support of Innosys, Inc.
+  and Keyspan, Inc the manufacturers of the Keyspan USB-serial products.
+  Thanks Guys :)
+
+  Thanks to Paulus for miscellaneous tidy ups, some largish chunks
+  of much nicer and/or completely new code and (perhaps most uniquely)
+  having the patience to sit down and explain why and where he'd changed
+  stuff.
+
+  Tip 'o the hat to IBM (and previously Linuxcare :) for supporting
+  staff in their work on open source projects.
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/usb/ezusb.h>
+
+#define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
+#define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
+
+/* Function prototypes for Keyspan serial converter */
+static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void keyspan_close(struct usb_serial_port *port);
+static void keyspan_dtr_rts(struct usb_serial_port *port, int on);
+static int keyspan_startup(struct usb_serial *serial);
+static void keyspan_disconnect(struct usb_serial *serial);
+static void keyspan_release(struct usb_serial *serial);
+static int keyspan_port_probe(struct usb_serial_port *port);
+static int keyspan_port_remove(struct usb_serial_port *port);
+static int keyspan_write_room(struct tty_struct *tty);
+static int keyspan_write(struct tty_struct *tty, struct usb_serial_port *port,
+			 const unsigned char *buf, int count);
+static void keyspan_send_setup(struct usb_serial_port *port, int reset_port);
+static void keyspan_set_termios(struct tty_struct *tty,
+				struct usb_serial_port *port,
+				struct ktermios *old);
+static void keyspan_break_ctl(struct tty_struct *tty, int break_state);
+static int keyspan_tiocmget(struct tty_struct *tty);
+static int keyspan_tiocmset(struct tty_struct *tty, unsigned int set,
+			    unsigned int clear);
+static int keyspan_fake_startup(struct usb_serial *serial);
+
+static int keyspan_usa19_calc_baud(struct usb_serial_port *port,
+				   u32 baud_rate, u32 baudclk,
+				   u8 *rate_hi, u8 *rate_low,
+				   u8 *prescaler, int portnum);
+static int keyspan_usa19w_calc_baud(struct usb_serial_port *port,
+				    u32 baud_rate, u32 baudclk,
+				    u8 *rate_hi, u8 *rate_low,
+				    u8 *prescaler, int portnum);
+static int keyspan_usa28_calc_baud(struct usb_serial_port *port,
+				   u32 baud_rate, u32 baudclk,
+				   u8 *rate_hi, u8 *rate_low,
+				   u8 *prescaler, int portnum);
+static int keyspan_usa19hs_calc_baud(struct usb_serial_port *port,
+				     u32 baud_rate, u32 baudclk,
+				     u8 *rate_hi, u8 *rate_low,
+				     u8 *prescaler, int portnum);
+
+static int keyspan_usa28_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port);
+static int keyspan_usa26_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port);
+static int keyspan_usa49_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port);
+static int keyspan_usa90_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port);
+static int keyspan_usa67_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port);
+
+/* Values used for baud rate calculation - device specific */
+#define KEYSPAN_INVALID_BAUD_RATE		(-1)
+#define KEYSPAN_BAUD_RATE_OK			(0)
+#define KEYSPAN_USA18X_BAUDCLK			(12000000L)	/* a guess */
+#define KEYSPAN_USA19_BAUDCLK			(12000000L)
+#define KEYSPAN_USA19W_BAUDCLK			(24000000L)
+#define KEYSPAN_USA19HS_BAUDCLK			(14769231L)
+#define KEYSPAN_USA28_BAUDCLK			(1843200L)
+#define KEYSPAN_USA28X_BAUDCLK			(12000000L)
+#define KEYSPAN_USA49W_BAUDCLK			(48000000L)
+
+/* Some constants used to characterise each device.  */
+#define KEYSPAN_MAX_NUM_PORTS			(4)
+#define KEYSPAN_MAX_FLIPS			(2)
+
+/*
+ * Device info for the Keyspan serial converter, used by the overall
+ * usb-serial probe function.
+ */
+#define KEYSPAN_VENDOR_ID			(0x06cd)
+
+/* Product IDs for the products supported, pre-renumeration */
+#define keyspan_usa18x_pre_product_id		0x0105
+#define keyspan_usa19_pre_product_id		0x0103
+#define keyspan_usa19qi_pre_product_id		0x010b
+#define keyspan_mpr_pre_product_id		0x011b
+#define keyspan_usa19qw_pre_product_id		0x0118
+#define keyspan_usa19w_pre_product_id		0x0106
+#define keyspan_usa28_pre_product_id		0x0101
+#define keyspan_usa28x_pre_product_id		0x0102
+#define keyspan_usa28xa_pre_product_id		0x0114
+#define keyspan_usa28xb_pre_product_id		0x0113
+#define keyspan_usa49w_pre_product_id		0x0109
+#define keyspan_usa49wlc_pre_product_id		0x011a
+
+/*
+ * Product IDs post-renumeration.  Note that the 28x and 28xb have the same
+ * id's post-renumeration but behave identically so it's not an issue. As
+ * such, the 28xb is not listed in any of the device tables.
+ */
+#define keyspan_usa18x_product_id		0x0112
+#define keyspan_usa19_product_id		0x0107
+#define keyspan_usa19qi_product_id		0x010c
+#define keyspan_usa19hs_product_id		0x0121
+#define keyspan_mpr_product_id			0x011c
+#define keyspan_usa19qw_product_id		0x0119
+#define keyspan_usa19w_product_id		0x0108
+#define keyspan_usa28_product_id		0x010f
+#define keyspan_usa28x_product_id		0x0110
+#define keyspan_usa28xa_product_id		0x0115
+#define keyspan_usa28xb_product_id		0x0110
+#define keyspan_usa28xg_product_id		0x0135
+#define keyspan_usa49w_product_id		0x010a
+#define keyspan_usa49wlc_product_id		0x012a
+#define keyspan_usa49wg_product_id		0x0131
+
+struct keyspan_device_details {
+	/* product ID value */
+	int	product_id;
+
+	enum	{msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format;
+
+		/* Number of physical ports */
+	int	num_ports;
+
+		/* 1 if endpoint flipping used on input, 0 if not */
+	int	indat_endp_flip;
+
+		/* 1 if endpoint flipping used on output, 0 if not */
+	int	outdat_endp_flip;
+
+		/*
+		 * Table mapping input data endpoint IDs to physical port
+		 * number and flip if used
+		 */
+	int	indat_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+		/* Same for output endpoints */
+	int	outdat_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+		/* Input acknowledge endpoints */
+	int	inack_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+		/* Output control endpoints */
+	int	outcont_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+		/* Endpoint used for input status */
+	int	instat_endpoint;
+
+		/* Endpoint used for input data 49WG only */
+	int	indat_endpoint;
+
+		/* Endpoint used for global control functions */
+	int	glocont_endpoint;
+
+	int	(*calculate_baud_rate)(struct usb_serial_port *port,
+				       u32 baud_rate, u32 baudclk,
+				       u8 *rate_hi, u8 *rate_low, u8 *prescaler,
+				       int portnum);
+	u32	baudclk;
+};
+
+/*
+ * Now for each device type we setup the device detail structure with the
+ * appropriate information (provided in Keyspan's documentation)
+ */
+
+static const struct keyspan_device_details usa18x_device_details = {
+	.product_id		= keyspan_usa18x_product_id,
+	.msg_format		= msg_usa26,
+	.num_ports		= 1,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x85},
+	.outcont_endpoints	= {0x05},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA18X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19_device_details = {
+	.product_id		= keyspan_usa19_product_id,
+	.msg_format		= msg_usa28,
+	.num_ports		= 1,
+	.indat_endp_flip	= 1,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x83},
+	.outcont_endpoints	= {0x03},
+	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= -1,
+	.calculate_baud_rate	= keyspan_usa19_calc_baud,
+	.baudclk		= KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19qi_device_details = {
+	.product_id		= keyspan_usa19qi_product_id,
+	.msg_format		= msg_usa28,
+	.num_ports		= 1,
+	.indat_endp_flip	= 1,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x83},
+	.outcont_endpoints	= {0x03},
+	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= -1,
+	.calculate_baud_rate	= keyspan_usa28_calc_baud,
+	.baudclk		= KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details mpr_device_details = {
+	.product_id		= keyspan_mpr_product_id,
+	.msg_format		= msg_usa28,
+	.num_ports		= 1,
+	.indat_endp_flip	= 1,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x83},
+	.outcont_endpoints	= {0x03},
+	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= -1,
+	.calculate_baud_rate	= keyspan_usa28_calc_baud,
+	.baudclk		= KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19qw_device_details = {
+	.product_id		= keyspan_usa19qw_product_id,
+	.msg_format		= msg_usa26,
+	.num_ports		= 1,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x85},
+	.outcont_endpoints	= {0x05},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19w_device_details = {
+	.product_id		= keyspan_usa19w_product_id,
+	.msg_format		= msg_usa26,
+	.num_ports		= 1,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {0x85},
+	.outcont_endpoints	= {0x05},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19hs_device_details = {
+	.product_id		= keyspan_usa19hs_product_id,
+	.msg_format		= msg_usa90,
+	.num_ports		= 1,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x81},
+	.outdat_endpoints	= {0x01},
+	.inack_endpoints	= {-1},
+	.outcont_endpoints	= {0x02},
+	.instat_endpoint	= 0x82,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= -1,
+	.calculate_baud_rate	= keyspan_usa19hs_calc_baud,
+	.baudclk		= KEYSPAN_USA19HS_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28_device_details = {
+	.product_id		= keyspan_usa28_product_id,
+	.msg_format		= msg_usa28,
+	.num_ports		= 2,
+	.indat_endp_flip	= 1,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81, 0x83},
+	.outdat_endpoints	= {0x01, 0x03},
+	.inack_endpoints	= {0x85, 0x86},
+	.outcont_endpoints	= {0x05, 0x06},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa28_calc_baud,
+	.baudclk		= KEYSPAN_USA28_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28x_device_details = {
+	.product_id		= keyspan_usa28x_product_id,
+	.msg_format		= msg_usa26,
+	.num_ports		= 2,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81, 0x83},
+	.outdat_endpoints	= {0x01, 0x03},
+	.inack_endpoints	= {0x85, 0x86},
+	.outcont_endpoints	= {0x05, 0x06},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28xa_device_details = {
+	.product_id		= keyspan_usa28xa_product_id,
+	.msg_format		= msg_usa26,
+	.num_ports		= 2,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 1,
+	.indat_endpoints	= {0x81, 0x83},
+	.outdat_endpoints	= {0x01, 0x03},
+	.inack_endpoints	= {0x85, 0x86},
+	.outcont_endpoints	= {0x05, 0x06},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28xg_device_details = {
+	.product_id		= keyspan_usa28xg_product_id,
+	.msg_format		= msg_usa67,
+	.num_ports		= 2,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x84, 0x88},
+	.outdat_endpoints	= {0x02, 0x06},
+	.inack_endpoints	= {-1, -1},
+	.outcont_endpoints	= {-1, -1},
+	.instat_endpoint	= 0x81,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x01,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
+};
+/*
+ * We don't need a separate entry for the usa28xb as it appears as a 28x
+ * anyway.
+ */
+
+static const struct keyspan_device_details usa49w_device_details = {
+	.product_id		= keyspan_usa49w_product_id,
+	.msg_format		= msg_usa49,
+	.num_ports		= 4,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x81, 0x82, 0x83, 0x84},
+	.outdat_endpoints	= {0x01, 0x02, 0x03, 0x04},
+	.inack_endpoints	= {-1, -1, -1, -1},
+	.outcont_endpoints	= {-1, -1, -1, -1},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA49W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa49wlc_device_details = {
+	.product_id		= keyspan_usa49wlc_product_id,
+	.msg_format		= msg_usa49,
+	.num_ports		= 4,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x81, 0x82, 0x83, 0x84},
+	.outdat_endpoints	= {0x01, 0x02, 0x03, 0x04},
+	.inack_endpoints	= {-1, -1, -1, -1},
+	.outcont_endpoints	= {-1, -1, -1, -1},
+	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa49wg_device_details = {
+	.product_id		= keyspan_usa49wg_product_id,
+	.msg_format		= msg_usa49,
+	.num_ports		= 4,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {-1, -1, -1, -1},	/* single 'global' data in EP */
+	.outdat_endpoints	= {0x01, 0x02, 0x04, 0x06},
+	.inack_endpoints	= {-1, -1, -1, -1},
+	.outcont_endpoints	= {-1, -1, -1, -1},
+	.instat_endpoint	= 0x81,
+	.indat_endpoint		= 0x88,
+	.glocont_endpoint	= 0x00,			/* uses control EP */
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details *keyspan_devices[] = {
+	&usa18x_device_details,
+	&usa19_device_details,
+	&usa19qi_device_details,
+	&mpr_device_details,
+	&usa19qw_device_details,
+	&usa19w_device_details,
+	&usa19hs_device_details,
+	&usa28_device_details,
+	&usa28x_device_details,
+	&usa28xa_device_details,
+	&usa28xg_device_details,
+	/* 28xb not required as it renumerates as a 28x */
+	&usa49w_device_details,
+	&usa49wlc_device_details,
+	&usa49wg_device_details,
+	NULL,
+};
+
+static const struct usb_device_id keyspan_ids_combined[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, keyspan_ids_combined);
+
+/* usb_device_id table for the pre-firmware download keyspan devices */
+static const struct usb_device_id keyspan_pre_ids[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
+	{ } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_1port_ids[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
+	{ } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_2port_ids[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
+	{ } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_4port_ids[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
+	{ } /* Terminating entry */
+};
+
+#define INSTAT_BUFLEN	32
+#define GLOCONT_BUFLEN	64
+#define INDAT49W_BUFLEN	512
+#define IN_BUFLEN	64
+#define OUT_BUFLEN	64
+#define INACK_BUFLEN	1
+#define OUTCONT_BUFLEN	64
+
+	/* Per device and per port private data */
+struct keyspan_serial_private {
+	const struct keyspan_device_details	*device_details;
+
+	struct urb	*instat_urb;
+	char		*instat_buf;
+
+	/* added to support 49wg, where data from all 4 ports comes in
+	   on 1 EP and high-speed supported */
+	struct urb	*indat_urb;
+	char		*indat_buf;
+
+	/* XXX this one probably will need a lock */
+	struct urb	*glocont_urb;
+	char		*glocont_buf;
+	char		*ctrl_buf;	/* for EP0 control message */
+};
+
+struct keyspan_port_private {
+	/* Keep track of which input & output endpoints to use */
+	int		in_flip;
+	int		out_flip;
+
+	/* Keep duplicate of device details in each port
+	   structure as well - simplifies some of the
+	   callback functions etc. */
+	const struct keyspan_device_details	*device_details;
+
+	/* Input endpoints and buffer for this port */
+	struct urb	*in_urbs[2];
+	char		*in_buffer[2];
+	/* Output endpoints and buffer for this port */
+	struct urb	*out_urbs[2];
+	char		*out_buffer[2];
+
+	/* Input ack endpoint */
+	struct urb	*inack_urb;
+	char		*inack_buffer;
+
+	/* Output control endpoint */
+	struct urb	*outcont_urb;
+	char		*outcont_buffer;
+
+	/* Settings for the port */
+	int		baud;
+	int		old_baud;
+	unsigned int	cflag;
+	unsigned int	old_cflag;
+	enum		{flow_none, flow_cts, flow_xon} flow_control;
+	int		rts_state;	/* Handshaking pins (outputs) */
+	int		dtr_state;
+	int		cts_state;	/* Handshaking pins (inputs) */
+	int		dsr_state;
+	int		dcd_state;
+	int		ri_state;
+	int		break_on;
+
+	unsigned long	tx_start_time[2];
+	int		resend_cont;	/* need to resend control packet */
+};
+
+/* Include Keyspan message headers.  All current Keyspan Adapters
+   make use of one of five message formats which are referred
+   to as USA-26, USA-28, USA-49, USA-90, USA-67 by Keyspan and
+   within this driver. */
+#include "keyspan_usa26msg.h"
+#include "keyspan_usa28msg.h"
+#include "keyspan_usa49msg.h"
+#include "keyspan_usa90msg.h"
+#include "keyspan_usa67msg.h"
+
+
+static void keyspan_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct keyspan_port_private 	*p_priv;
+
+	p_priv = usb_get_serial_port_data(port);
+
+	if (break_state == -1)
+		p_priv->break_on = 1;
+	else
+		p_priv->break_on = 0;
+
+	keyspan_send_setup(port, 0);
+}
+
+
+static void keyspan_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	int				baud_rate, device_port;
+	struct keyspan_port_private 	*p_priv;
+	const struct keyspan_device_details	*d_details;
+	unsigned int 			cflag;
+
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+	cflag = tty->termios.c_cflag;
+	device_port = port->port_number;
+
+	/* Baud rate calculation takes baud rate as an integer
+	   so other rates can be generated if desired. */
+	baud_rate = tty_get_baud_rate(tty);
+	/* If no match or invalid, don't change */
+	if (d_details->calculate_baud_rate(port, baud_rate, d_details->baudclk,
+				NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
+		/* FIXME - more to do here to ensure rate changes cleanly */
+		/* FIXME - calculate exact rate from divisor ? */
+		p_priv->baud = baud_rate;
+	} else
+		baud_rate = tty_termios_baud_rate(old_termios);
+
+	tty_encode_baud_rate(tty, baud_rate, baud_rate);
+	/* set CTS/RTS handshake etc. */
+	p_priv->cflag = cflag;
+	p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none;
+
+	/* Mark/Space not supported */
+	tty->termios.c_cflag &= ~CMSPAR;
+
+	keyspan_send_setup(port, 0);
+}
+
+static int keyspan_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
+	unsigned int			value;
+
+	value = ((p_priv->rts_state) ? TIOCM_RTS : 0) |
+		((p_priv->dtr_state) ? TIOCM_DTR : 0) |
+		((p_priv->cts_state) ? TIOCM_CTS : 0) |
+		((p_priv->dsr_state) ? TIOCM_DSR : 0) |
+		((p_priv->dcd_state) ? TIOCM_CAR : 0) |
+		((p_priv->ri_state) ? TIOCM_RNG : 0);
+
+	return value;
+}
+
+static int keyspan_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
+
+	if (set & TIOCM_RTS)
+		p_priv->rts_state = 1;
+	if (set & TIOCM_DTR)
+		p_priv->dtr_state = 1;
+	if (clear & TIOCM_RTS)
+		p_priv->rts_state = 0;
+	if (clear & TIOCM_DTR)
+		p_priv->dtr_state = 0;
+	keyspan_send_setup(port, 0);
+	return 0;
+}
+
+/* Write function is similar for the four protocols used
+   with only a minor change for usa90 (usa19hs) required */
+static int keyspan_write(struct tty_struct *tty,
+	struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+	struct keyspan_port_private 	*p_priv;
+	const struct keyspan_device_details	*d_details;
+	int				flip;
+	int 				left, todo;
+	struct urb			*this_urb;
+	int 				err, maxDataLen, dataOffset;
+
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+
+	if (d_details->msg_format == msg_usa90) {
+		maxDataLen = 64;
+		dataOffset = 0;
+	} else {
+		maxDataLen = 63;
+		dataOffset = 1;
+	}
+
+	dev_dbg(&port->dev, "%s - %d chars, flip=%d\n", __func__, count,
+		p_priv->out_flip);
+
+	for (left = count; left > 0; left -= todo) {
+		todo = left;
+		if (todo > maxDataLen)
+			todo = maxDataLen;
+
+		flip = p_priv->out_flip;
+
+		/* Check we have a valid urb/endpoint before we use it... */
+		this_urb = p_priv->out_urbs[flip];
+		if (this_urb == NULL) {
+			/* no bulk out, so return 0 bytes written */
+			dev_dbg(&port->dev, "%s - no output urb :(\n", __func__);
+			return count;
+		}
+
+		dev_dbg(&port->dev, "%s - endpoint %x flip %d\n",
+			__func__, usb_pipeendpoint(this_urb->pipe), flip);
+
+		if (this_urb->status == -EINPROGRESS) {
+			if (time_before(jiffies,
+					p_priv->tx_start_time[flip] + 10 * HZ))
+				break;
+			usb_unlink_urb(this_urb);
+			break;
+		}
+
+		/* First byte in buffer is "last flag" (except for usa19hx)
+		   - unused so for now so set to zero */
+		((char *)this_urb->transfer_buffer)[0] = 0;
+
+		memcpy(this_urb->transfer_buffer + dataOffset, buf, todo);
+		buf += todo;
+
+		/* send the data out the bulk port */
+		this_urb->transfer_buffer_length = todo + dataOffset;
+
+		err = usb_submit_urb(this_urb, GFP_ATOMIC);
+		if (err != 0)
+			dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed (%d)\n", err);
+		p_priv->tx_start_time[flip] = jiffies;
+
+		/* Flip for next time if usa26 or usa28 interface
+		   (not used on usa49) */
+		p_priv->out_flip = (flip + 1) & d_details->outdat_endp_flip;
+	}
+
+	return count - left;
+}
+
+static void	usa26_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
+			__func__, status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	if (urb->actual_length) {
+		/* 0x80 bit is error flag */
+		if ((data[0] & 0x80) == 0) {
+			/* no errors on individual bytes, only
+			   possible overrun err */
+			if (data[0] & RXERROR_OVERRUN) {
+				tty_insert_flip_char(&port->port, 0,
+								TTY_OVERRUN);
+			}
+			for (i = 1; i < urb->actual_length ; ++i)
+				tty_insert_flip_char(&port->port, data[i],
+								TTY_NORMAL);
+		} else {
+			/* some bytes had errors, every byte has status */
+			dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
+			for (i = 0; i + 1 < urb->actual_length; i += 2) {
+				int stat = data[i];
+				int flag = TTY_NORMAL;
+
+				if (stat & RXERROR_OVERRUN) {
+					tty_insert_flip_char(&port->port, 0,
+								TTY_OVERRUN);
+				}
+				/* XXX should handle break (0x10) */
+				if (stat & RXERROR_PARITY)
+					flag = TTY_PARITY;
+				else if (stat & RXERROR_FRAMING)
+					flag = TTY_FRAME;
+
+				tty_insert_flip_char(&port->port, data[i+1],
+						flag);
+			}
+		}
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+}
+
+/* Outdat handling is common for all devices */
+static void	usa2x_outdat_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+	dev_dbg(&port->dev, "%s - urb %d\n", __func__, urb == p_priv->out_urbs[1]);
+
+	usb_serial_port_softint(port);
+}
+
+static void	usa26_inack_callback(struct urb *urb)
+{
+}
+
+static void	usa26_outcont_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+
+	if (p_priv->resend_cont) {
+		dev_dbg(&port->dev, "%s - sending setup\n", __func__);
+		keyspan_usa26_send_setup(port->serial, port,
+						p_priv->resend_cont - 1);
+	}
+}
+
+static void	usa26_instat_callback(struct urb *urb)
+{
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa26_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state, err;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+	if (urb->actual_length != 9) {
+		dev_dbg(&urb->dev->dev, "%s - %d byte report??\n", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa26_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
+		goto exit;
+	}
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+	if (!p_priv)
+		goto resubmit;
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);
+	p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+	p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
+	p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+	if (old_dcd_state != p_priv->dcd_state)
+		tty_port_tty_hangup(&port->port, true);
+resubmit:
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+exit: ;
+}
+
+static void	usa26_glocont_callback(struct urb *urb)
+{
+}
+
+
+static void usa28_indat_callback(struct urb *urb)
+{
+	int                     err;
+	struct usb_serial_port  *port;
+	unsigned char           *data;
+	struct keyspan_port_private             *p_priv;
+	int status = urb->status;
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+	data = urb->transfer_buffer;
+
+	if (urb != p_priv->in_urbs[p_priv->in_flip])
+		return;
+
+	do {
+		if (status) {
+			dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
+				__func__, status, usb_pipeendpoint(urb->pipe));
+			return;
+		}
+
+		port =  urb->context;
+		p_priv = usb_get_serial_port_data(port);
+		data = urb->transfer_buffer;
+
+		if (urb->actual_length) {
+			tty_insert_flip_string(&port->port, data,
+					urb->actual_length);
+			tty_flip_buffer_push(&port->port);
+		}
+
+		/* Resubmit urb so we continue receiving */
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err != 0)
+			dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n",
+							__func__, err);
+		p_priv->in_flip ^= 1;
+
+		urb = p_priv->in_urbs[p_priv->in_flip];
+	} while (urb->status != -EINPROGRESS);
+}
+
+static void	usa28_inack_callback(struct urb *urb)
+{
+}
+
+static void	usa28_outcont_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+
+	if (p_priv->resend_cont) {
+		dev_dbg(&port->dev, "%s - sending setup\n", __func__);
+		keyspan_usa28_send_setup(port->serial, port,
+						p_priv->resend_cont - 1);
+	}
+}
+
+static void	usa28_instat_callback(struct urb *urb)
+{
+	int					err;
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa28_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+
+	if (urb->actual_length != sizeof(struct keyspan_usa28_portStatusMessage)) {
+		dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa28_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
+		goto exit;
+	}
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+	if (!p_priv)
+		goto resubmit;
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->cts) ? 1 : 0);
+	p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+	p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+	if (old_dcd_state != p_priv->dcd_state && old_dcd_state)
+		tty_port_tty_hangup(&port->port, true);
+resubmit:
+		/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+exit: ;
+}
+
+static void	usa28_glocont_callback(struct urb *urb)
+{
+}
+
+
+static void	usa49_glocont_callback(struct urb *urb)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+	int i;
+
+	serial =  urb->context;
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+
+		if (p_priv->resend_cont) {
+			dev_dbg(&port->dev, "%s - sending setup\n", __func__);
+			keyspan_usa49_send_setup(serial, port,
+						p_priv->resend_cont - 1);
+			break;
+		}
+	}
+}
+
+	/* This is actually called glostat in the Keyspan
+	   doco */
+static void	usa49_instat_callback(struct urb *urb)
+{
+	int					err;
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa49_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+
+	if (urb->actual_length !=
+			sizeof(struct keyspan_usa49_portStatusMessage)) {
+		dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa49_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->portNumber >= serial->num_ports) {
+		dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n",
+			__func__, msg->portNumber);
+		goto exit;
+	}
+	port = serial->port[msg->portNumber];
+	p_priv = usb_get_serial_port_data(port);
+	if (!p_priv)
+		goto resubmit;
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->cts) ? 1 : 0);
+	p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+	p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+	if (old_dcd_state != p_priv->dcd_state && old_dcd_state)
+		tty_port_tty_hangup(&port->port, true);
+resubmit:
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+exit:	;
+}
+
+static void	usa49_inack_callback(struct urb *urb)
+{
+}
+
+static void	usa49_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
+			__func__, status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	if (urb->actual_length) {
+		/* 0x80 bit is error flag */
+		if ((data[0] & 0x80) == 0) {
+			/* no error on any byte */
+			tty_insert_flip_string(&port->port, data + 1,
+						urb->actual_length - 1);
+		} else {
+			/* some bytes had errors, every byte has status */
+			for (i = 0; i + 1 < urb->actual_length; i += 2) {
+				int stat = data[i];
+				int flag = TTY_NORMAL;
+
+				if (stat & RXERROR_OVERRUN) {
+					tty_insert_flip_char(&port->port, 0,
+								TTY_OVERRUN);
+				}
+				/* XXX should handle break (0x10) */
+				if (stat & RXERROR_PARITY)
+					flag = TTY_PARITY;
+				else if (stat & RXERROR_FRAMING)
+					flag = TTY_FRAME;
+
+				tty_insert_flip_char(&port->port, data[i+1],
+						flag);
+			}
+		}
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+}
+
+static void usa49wg_indat_callback(struct urb *urb)
+{
+	int			i, len, x, err;
+	struct usb_serial	*serial;
+	struct usb_serial_port	*port;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	serial = urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+
+	/* inbound data is in the form P#, len, status, data */
+	i = 0;
+	len = 0;
+
+	while (i < urb->actual_length) {
+
+		/* Check port number from message */
+		if (data[i] >= serial->num_ports) {
+			dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n",
+				__func__, data[i]);
+			return;
+		}
+		port = serial->port[data[i++]];
+		len = data[i++];
+
+		/* 0x80 bit is error flag */
+		if ((data[i] & 0x80) == 0) {
+			/* no error on any byte */
+			i++;
+			for (x = 1; x < len && i < urb->actual_length; ++x)
+				tty_insert_flip_char(&port->port,
+						data[i++], 0);
+		} else {
+			/*
+			 * some bytes had errors, every byte has status
+			 */
+			for (x = 0; x + 1 < len &&
+				    i + 1 < urb->actual_length; x += 2) {
+				int stat = data[i];
+				int flag = TTY_NORMAL;
+
+				if (stat & RXERROR_OVERRUN) {
+					tty_insert_flip_char(&port->port, 0,
+								TTY_OVERRUN);
+				}
+				/* XXX should handle break (0x10) */
+				if (stat & RXERROR_PARITY)
+					flag = TTY_PARITY;
+				else if (stat & RXERROR_FRAMING)
+					flag = TTY_FRAME;
+
+				tty_insert_flip_char(&port->port, data[i+1],
+						     flag);
+				i += 2;
+			}
+		}
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&urb->dev->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+}
+
+/* not used, usa-49 doesn't have per-port control endpoints */
+static void usa49_outcont_callback(struct urb *urb)
+{
+}
+
+static void usa90_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	struct keyspan_port_private	 	*p_priv;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
+			__func__, status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+
+	if (urb->actual_length) {
+		/* if current mode is DMA, looks like usa28 format
+		   otherwise looks like usa26 data format */
+
+		if (p_priv->baud > 57600)
+			tty_insert_flip_string(&port->port, data,
+					urb->actual_length);
+		else {
+			/* 0x80 bit is error flag */
+			if ((data[0] & 0x80) == 0) {
+				/* no errors on individual bytes, only
+				   possible overrun err*/
+				if (data[0] & RXERROR_OVERRUN) {
+					tty_insert_flip_char(&port->port, 0,
+								TTY_OVERRUN);
+				}
+				for (i = 1; i < urb->actual_length ; ++i)
+					tty_insert_flip_char(&port->port,
+							data[i], TTY_NORMAL);
+			}  else {
+			/* some bytes had errors, every byte has status */
+				dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
+				for (i = 0; i + 1 < urb->actual_length; i += 2) {
+					int stat = data[i];
+					int flag = TTY_NORMAL;
+
+					if (stat & RXERROR_OVERRUN) {
+						tty_insert_flip_char(
+								&port->port, 0,
+								TTY_OVERRUN);
+					}
+					/* XXX should handle break (0x10) */
+					if (stat & RXERROR_PARITY)
+						flag = TTY_PARITY;
+					else if (stat & RXERROR_FRAMING)
+						flag = TTY_FRAME;
+
+					tty_insert_flip_char(&port->port,
+							data[i+1], flag);
+				}
+			}
+		}
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+}
+
+
+static void	usa90_instat_callback(struct urb *urb)
+{
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa90_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state, err;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+	if (urb->actual_length < 14) {
+		dev_dbg(&urb->dev->dev, "%s - %d byte report??\n", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa90_portStatusMessage *)data;
+
+	/* Now do something useful with the data */
+
+	port = serial->port[0];
+	p_priv = usb_get_serial_port_data(port);
+	if (!p_priv)
+		goto resubmit;
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->cts) ? 1 : 0);
+	p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+	p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+	if (old_dcd_state != p_priv->dcd_state && old_dcd_state)
+		tty_port_tty_hangup(&port->port, true);
+resubmit:
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+exit:
+	;
+}
+
+static void	usa90_outcont_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+
+	if (p_priv->resend_cont) {
+		dev_dbg(&urb->dev->dev, "%s - sending setup\n", __func__);
+		keyspan_usa90_send_setup(port->serial, port,
+						p_priv->resend_cont - 1);
+	}
+}
+
+/* Status messages from the 28xg */
+static void	usa67_instat_callback(struct urb *urb)
+{
+	int					err;
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa67_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state;
+	int status = urb->status;
+
+	serial = urb->context;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
+				__func__, status);
+		return;
+	}
+
+	if (urb->actual_length !=
+			sizeof(struct keyspan_usa67_portStatusMessage)) {
+		dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
+		return;
+	}
+
+
+	/* Now do something useful with the data */
+	msg = (struct keyspan_usa67_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
+		return;
+	}
+
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+	if (!p_priv)
+		goto resubmit;
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);
+	p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
+
+	if (old_dcd_state != p_priv->dcd_state && old_dcd_state)
+		tty_port_tty_hangup(&port->port, true);
+resubmit:
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
+}
+
+static void usa67_glocont_callback(struct urb *urb)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+	int i;
+
+	serial = urb->context;
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+
+		if (p_priv->resend_cont) {
+			dev_dbg(&port->dev, "%s - sending setup\n", __func__);
+			keyspan_usa67_send_setup(serial, port,
+						p_priv->resend_cont - 1);
+			break;
+		}
+	}
+}
+
+static int keyspan_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct keyspan_port_private	*p_priv;
+	const struct keyspan_device_details	*d_details;
+	int				flip;
+	int				data_len;
+	struct urb			*this_urb;
+
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+
+	/* FIXME: locking */
+	if (d_details->msg_format == msg_usa90)
+		data_len = 64;
+	else
+		data_len = 63;
+
+	flip = p_priv->out_flip;
+
+	/* Check both endpoints to see if any are available. */
+	this_urb = p_priv->out_urbs[flip];
+	if (this_urb != NULL) {
+		if (this_urb->status != -EINPROGRESS)
+			return data_len;
+		flip = (flip + 1) & d_details->outdat_endp_flip;
+		this_urb = p_priv->out_urbs[flip];
+		if (this_urb != NULL) {
+			if (this_urb->status != -EINPROGRESS)
+				return data_len;
+		}
+	}
+	return 0;
+}
+
+
+static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct keyspan_port_private 	*p_priv;
+	const struct keyspan_device_details	*d_details;
+	int				i, err;
+	int				baud_rate, device_port;
+	struct urb			*urb;
+	unsigned int			cflag = 0;
+
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+
+	/* Set some sane defaults */
+	p_priv->rts_state = 1;
+	p_priv->dtr_state = 1;
+	p_priv->baud = 9600;
+
+	/* force baud and lcr to be set on open */
+	p_priv->old_baud = 0;
+	p_priv->old_cflag = 0;
+
+	p_priv->out_flip = 0;
+	p_priv->in_flip = 0;
+
+	/* Reset low level data toggle and start reading from endpoints */
+	for (i = 0; i < 2; i++) {
+		urb = p_priv->in_urbs[i];
+		if (urb == NULL)
+			continue;
+
+		/* make sure endpoint data toggle is synchronized
+		   with the device */
+		usb_clear_halt(urb->dev, urb->pipe);
+		err = usb_submit_urb(urb, GFP_KERNEL);
+		if (err != 0)
+			dev_dbg(&port->dev, "%s - submit urb %d failed (%d)\n", __func__, i, err);
+	}
+
+	/* Reset low level data toggle on out endpoints */
+	for (i = 0; i < 2; i++) {
+		urb = p_priv->out_urbs[i];
+		if (urb == NULL)
+			continue;
+		/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+						usb_pipeout(urb->pipe), 0); */
+	}
+
+	/* get the terminal config for the setup message now so we don't
+	 * need to send 2 of them */
+
+	device_port = port->port_number;
+	if (tty) {
+		cflag = tty->termios.c_cflag;
+		/* Baud rate calculation takes baud rate as an integer
+		   so other rates can be generated if desired. */
+		baud_rate = tty_get_baud_rate(tty);
+		/* If no match or invalid, leave as default */
+		if (baud_rate >= 0
+		    && d_details->calculate_baud_rate(port, baud_rate, d_details->baudclk,
+					NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
+			p_priv->baud = baud_rate;
+		}
+	}
+	/* set CTS/RTS handshake etc. */
+	p_priv->cflag = cflag;
+	p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none;
+
+	keyspan_send_setup(port, 1);
+	/* mdelay(100); */
+	/* keyspan_set_termios(port, NULL); */
+
+	return 0;
+}
+
+static void keyspan_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
+
+	p_priv->rts_state = on;
+	p_priv->dtr_state = on;
+	keyspan_send_setup(port, 0);
+}
+
+static void keyspan_close(struct usb_serial_port *port)
+{
+	int			i;
+	struct keyspan_port_private 	*p_priv;
+
+	p_priv = usb_get_serial_port_data(port);
+
+	p_priv->rts_state = 0;
+	p_priv->dtr_state = 0;
+
+	keyspan_send_setup(port, 2);
+	/* pilot-xfer seems to work best with this delay */
+	mdelay(100);
+
+	p_priv->out_flip = 0;
+	p_priv->in_flip = 0;
+
+	usb_kill_urb(p_priv->inack_urb);
+	for (i = 0; i < 2; i++) {
+		usb_kill_urb(p_priv->in_urbs[i]);
+		usb_kill_urb(p_priv->out_urbs[i]);
+	}
+}
+
+/* download the firmware to a pre-renumeration device */
+static int keyspan_fake_startup(struct usb_serial *serial)
+{
+	char	*fw_name;
+
+	dev_dbg(&serial->dev->dev, "Keyspan startup version %04x product %04x\n",
+		le16_to_cpu(serial->dev->descriptor.bcdDevice),
+		le16_to_cpu(serial->dev->descriptor.idProduct));
+
+	if ((le16_to_cpu(serial->dev->descriptor.bcdDevice) & 0x8000)
+								!= 0x8000) {
+		dev_dbg(&serial->dev->dev, "Firmware already loaded.  Quitting.\n");
+		return 1;
+	}
+
+		/* Select firmware image on the basis of idProduct */
+	switch (le16_to_cpu(serial->dev->descriptor.idProduct)) {
+	case keyspan_usa28_pre_product_id:
+		fw_name = "keyspan/usa28.fw";
+		break;
+
+	case keyspan_usa28x_pre_product_id:
+		fw_name = "keyspan/usa28x.fw";
+		break;
+
+	case keyspan_usa28xa_pre_product_id:
+		fw_name = "keyspan/usa28xa.fw";
+		break;
+
+	case keyspan_usa28xb_pre_product_id:
+		fw_name = "keyspan/usa28xb.fw";
+		break;
+
+	case keyspan_usa19_pre_product_id:
+		fw_name = "keyspan/usa19.fw";
+		break;
+
+	case keyspan_usa19qi_pre_product_id:
+		fw_name = "keyspan/usa19qi.fw";
+		break;
+
+	case keyspan_mpr_pre_product_id:
+		fw_name = "keyspan/mpr.fw";
+		break;
+
+	case keyspan_usa19qw_pre_product_id:
+		fw_name = "keyspan/usa19qw.fw";
+		break;
+
+	case keyspan_usa18x_pre_product_id:
+		fw_name = "keyspan/usa18x.fw";
+		break;
+
+	case keyspan_usa19w_pre_product_id:
+		fw_name = "keyspan/usa19w.fw";
+		break;
+
+	case keyspan_usa49w_pre_product_id:
+		fw_name = "keyspan/usa49w.fw";
+		break;
+
+	case keyspan_usa49wlc_pre_product_id:
+		fw_name = "keyspan/usa49wlc.fw";
+		break;
+
+	default:
+		dev_err(&serial->dev->dev, "Unknown product ID (%04x)\n",
+			le16_to_cpu(serial->dev->descriptor.idProduct));
+		return 1;
+	}
+
+	dev_dbg(&serial->dev->dev, "Uploading Keyspan %s firmware.\n", fw_name);
+
+	if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
+		dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
+			fw_name);
+		return -ENOENT;
+	}
+
+	/* after downloading firmware Renumeration will occur in a
+	  moment and the new device will bind to the real driver */
+
+	/* we don't want this device to have a driver assigned to it. */
+	return 1;
+}
+
+/* Helper functions used by keyspan_setup_urbs */
+static struct usb_endpoint_descriptor const *find_ep(struct usb_serial const *serial,
+						     int endpoint)
+{
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *ep;
+	int i;
+
+	iface_desc = serial->interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		ep = &iface_desc->endpoint[i].desc;
+		if (ep->bEndpointAddress == endpoint)
+			return ep;
+	}
+	dev_warn(&serial->interface->dev, "found no endpoint descriptor for endpoint %x\n",
+			endpoint);
+	return NULL;
+}
+
+static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
+				      int dir, void *ctx, char *buf, int len,
+				      void (*callback)(struct urb *))
+{
+	struct urb *urb;
+	struct usb_endpoint_descriptor const *ep_desc;
+	char const *ep_type_name;
+
+	if (endpoint == -1)
+		return NULL;		/* endpoint not needed */
+
+	dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %x\n",
+			__func__, endpoint);
+	urb = usb_alloc_urb(0, GFP_KERNEL);		/* No ISO */
+	if (!urb)
+		return NULL;
+
+	if (endpoint == 0) {
+		/* control EP filled in when used */
+		return urb;
+	}
+
+	ep_desc = find_ep(serial, endpoint);
+	if (!ep_desc) {
+		/* leak the urb, something's wrong and the callers don't care */
+		return urb;
+	}
+	if (usb_endpoint_xfer_int(ep_desc)) {
+		ep_type_name = "INT";
+		usb_fill_int_urb(urb, serial->dev,
+				 usb_sndintpipe(serial->dev, endpoint) | dir,
+				 buf, len, callback, ctx,
+				 ep_desc->bInterval);
+	} else if (usb_endpoint_xfer_bulk(ep_desc)) {
+		ep_type_name = "BULK";
+		usb_fill_bulk_urb(urb, serial->dev,
+				  usb_sndbulkpipe(serial->dev, endpoint) | dir,
+				  buf, len, callback, ctx);
+	} else {
+		dev_warn(&serial->interface->dev,
+			 "unsupported endpoint type %x\n",
+			 usb_endpoint_type(ep_desc));
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	dev_dbg(&serial->interface->dev, "%s - using urb %p for %s endpoint %x\n",
+	    __func__, urb, ep_type_name, endpoint);
+	return urb;
+}
+
+static struct callbacks {
+	void	(*instat_callback)(struct urb *);
+	void	(*glocont_callback)(struct urb *);
+	void	(*indat_callback)(struct urb *);
+	void	(*outdat_callback)(struct urb *);
+	void	(*inack_callback)(struct urb *);
+	void	(*outcont_callback)(struct urb *);
+} keyspan_callbacks[] = {
+	{
+		/* msg_usa26 callbacks */
+		.instat_callback =	usa26_instat_callback,
+		.glocont_callback =	usa26_glocont_callback,
+		.indat_callback =	usa26_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa26_inack_callback,
+		.outcont_callback =	usa26_outcont_callback,
+	}, {
+		/* msg_usa28 callbacks */
+		.instat_callback =	usa28_instat_callback,
+		.glocont_callback =	usa28_glocont_callback,
+		.indat_callback =	usa28_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa28_inack_callback,
+		.outcont_callback =	usa28_outcont_callback,
+	}, {
+		/* msg_usa49 callbacks */
+		.instat_callback =	usa49_instat_callback,
+		.glocont_callback =	usa49_glocont_callback,
+		.indat_callback =	usa49_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa49_inack_callback,
+		.outcont_callback =	usa49_outcont_callback,
+	}, {
+		/* msg_usa90 callbacks */
+		.instat_callback =	usa90_instat_callback,
+		.glocont_callback =	usa28_glocont_callback,
+		.indat_callback =	usa90_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa28_inack_callback,
+		.outcont_callback =	usa90_outcont_callback,
+	}, {
+		/* msg_usa67 callbacks */
+		.instat_callback =	usa67_instat_callback,
+		.glocont_callback =	usa67_glocont_callback,
+		.indat_callback =	usa26_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa26_inack_callback,
+		.outcont_callback =	usa26_outcont_callback,
+	}
+};
+
+	/* Generic setup urbs function that uses
+	   data in device_details */
+static void keyspan_setup_urbs(struct usb_serial *serial)
+{
+	struct keyspan_serial_private 	*s_priv;
+	const struct keyspan_device_details	*d_details;
+	struct callbacks		*cback;
+
+	s_priv = usb_get_serial_data(serial);
+	d_details = s_priv->device_details;
+
+	/* Setup values for the various callback routines */
+	cback = &keyspan_callbacks[d_details->msg_format];
+
+	/* Allocate and set up urbs for each one that is in use,
+	   starting with instat endpoints */
+	s_priv->instat_urb = keyspan_setup_urb
+		(serial, d_details->instat_endpoint, USB_DIR_IN,
+		 serial, s_priv->instat_buf, INSTAT_BUFLEN,
+		 cback->instat_callback);
+
+	s_priv->indat_urb = keyspan_setup_urb
+		(serial, d_details->indat_endpoint, USB_DIR_IN,
+		 serial, s_priv->indat_buf, INDAT49W_BUFLEN,
+		 usa49wg_indat_callback);
+
+	s_priv->glocont_urb = keyspan_setup_urb
+		(serial, d_details->glocont_endpoint, USB_DIR_OUT,
+		 serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
+		 cback->glocont_callback);
+}
+
+/* usa19 function doesn't require prescaler */
+static int keyspan_usa19_calc_baud(struct usb_serial_port *port,
+				   u32 baud_rate, u32 baudclk, u8 *rate_hi,
+				   u8 *rate_low, u8 *prescaler, int portnum)
+{
+	u32 	b16,	/* baud rate times 16 (actual rate used internally) */
+		div,	/* divisor */
+		cnt;	/* inverse of divisor (programmed into 8051) */
+
+	dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
+
+	/* prevent divide by zero...  */
+	b16 = baud_rate * 16L;
+	if (b16 == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+	/* Any "standard" rate over 57k6 is marginal on the USA-19
+	   as we run out of divisor resolution. */
+	if (baud_rate > 57600)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* calculate the divisor and the counter (its inverse) */
+	div = baudclk / b16;
+	if (div == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+	else
+		cnt = 0 - div;
+
+	if (div > 0xffff)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* return the counter values if non-null */
+	if (rate_low)
+		*rate_low = (u8) (cnt & 0xff);
+	if (rate_hi)
+		*rate_hi = (u8) ((cnt >> 8) & 0xff);
+	if (rate_low && rate_hi)
+		dev_dbg(&port->dev, "%s - %d %02x %02x.\n",
+				__func__, baud_rate, *rate_hi, *rate_low);
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+/* usa19hs function doesn't require prescaler */
+static int keyspan_usa19hs_calc_baud(struct usb_serial_port *port,
+				     u32 baud_rate, u32 baudclk, u8 *rate_hi,
+				     u8 *rate_low, u8 *prescaler, int portnum)
+{
+	u32 	b16,	/* baud rate times 16 (actual rate used internally) */
+			div;	/* divisor */
+
+	dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
+
+	/* prevent divide by zero...  */
+	b16 = baud_rate * 16L;
+	if (b16 == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* calculate the divisor */
+	div = baudclk / b16;
+	if (div == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	if (div > 0xffff)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* return the counter values if non-null */
+	if (rate_low)
+		*rate_low = (u8) (div & 0xff);
+
+	if (rate_hi)
+		*rate_hi = (u8) ((div >> 8) & 0xff);
+
+	if (rate_low && rate_hi)
+		dev_dbg(&port->dev, "%s - %d %02x %02x.\n",
+			__func__, baud_rate, *rate_hi, *rate_low);
+
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+static int keyspan_usa19w_calc_baud(struct usb_serial_port *port,
+				    u32 baud_rate, u32 baudclk, u8 *rate_hi,
+				    u8 *rate_low, u8 *prescaler, int portnum)
+{
+	u32 	b16,	/* baud rate times 16 (actual rate used internally) */
+		clk,	/* clock with 13/8 prescaler */
+		div,	/* divisor using 13/8 prescaler */
+		res,	/* resulting baud rate using 13/8 prescaler */
+		diff,	/* error using 13/8 prescaler */
+		smallest_diff;
+	u8	best_prescaler;
+	int	i;
+
+	dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
+
+	/* prevent divide by zero */
+	b16 = baud_rate * 16L;
+	if (b16 == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* Calculate prescaler by trying them all and looking
+	   for best fit */
+
+	/* start with largest possible difference */
+	smallest_diff = 0xffffffff;
+
+		/* 0 is an invalid prescaler, used as a flag */
+	best_prescaler = 0;
+
+	for (i = 8; i <= 0xff; ++i) {
+		clk = (baudclk * 8) / (u32) i;
+
+		div = clk / b16;
+		if (div == 0)
+			continue;
+
+		res = clk / div;
+		diff = (res > b16) ? (res-b16) : (b16-res);
+
+		if (diff < smallest_diff) {
+			best_prescaler = i;
+			smallest_diff = diff;
+		}
+	}
+
+	if (best_prescaler == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	clk = (baudclk * 8) / (u32) best_prescaler;
+	div = clk / b16;
+
+	/* return the divisor and prescaler if non-null */
+	if (rate_low)
+		*rate_low = (u8) (div & 0xff);
+	if (rate_hi)
+		*rate_hi = (u8) ((div >> 8) & 0xff);
+	if (prescaler) {
+		*prescaler = best_prescaler;
+		/*  dev_dbg(&port->dev, "%s - %d %d\n", __func__, *prescaler, div); */
+	}
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+	/* USA-28 supports different maximum baud rates on each port */
+static int keyspan_usa28_calc_baud(struct usb_serial_port *port,
+				   u32 baud_rate, u32 baudclk, u8 *rate_hi,
+				   u8 *rate_low, u8 *prescaler, int portnum)
+{
+	u32 	b16,	/* baud rate times 16 (actual rate used internally) */
+		div,	/* divisor */
+		cnt;	/* inverse of divisor (programmed into 8051) */
+
+	dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
+
+		/* prevent divide by zero */
+	b16 = baud_rate * 16L;
+	if (b16 == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+
+	/* calculate the divisor and the counter (its inverse) */
+	div = KEYSPAN_USA28_BAUDCLK / b16;
+	if (div == 0)
+		return KEYSPAN_INVALID_BAUD_RATE;
+	else
+		cnt = 0 - div;
+
+	/* check for out of range, based on portnum,
+	   and return result */
+	if (portnum == 0) {
+		if (div > 0xffff)
+			return KEYSPAN_INVALID_BAUD_RATE;
+	} else {
+		if (portnum == 1) {
+			if (div > 0xff)
+				return KEYSPAN_INVALID_BAUD_RATE;
+		} else
+			return KEYSPAN_INVALID_BAUD_RATE;
+	}
+
+		/* return the counter values if not NULL
+		   (port 1 will ignore retHi) */
+	if (rate_low)
+		*rate_low = (u8) (cnt & 0xff);
+	if (rate_hi)
+		*rate_hi = (u8) ((cnt >> 8) & 0xff);
+	dev_dbg(&port->dev, "%s - %d OK.\n", __func__, baud_rate);
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+static int keyspan_usa26_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa26_portControlMessage	msg;
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					device_port, err;
+
+	dev_dbg(&port->dev, "%s reset=%d\n", __func__, reset_port);
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+	device_port = port->port_number;
+
+	this_urb = p_priv->outcont_urb;
+
+		/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
+		return -1;
+	}
+
+	dev_dbg(&port->dev, "%s - endpoint %x\n",
+			__func__, usb_pipeendpoint(this_urb->pipe));
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		/*  dev_dbg(&port->dev, "%s - already writing\n", __func__); */
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa26_portControlMessage));
+
+	/* Only set baud rate if it's changed */
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0xff;
+		if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+						   &msg.baudHi, &msg.baudLo, &msg.prescaler,
+						   device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+				__func__, p_priv->baud);
+			msg.baudLo = 0;
+			msg.baudHi = 125;	/* Values for 9600 baud */
+			msg.prescaler = 10;
+		}
+		msg.setPrescaler = 0xff;
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD) ?
+			USA_PARITY_ODD : USA_PARITY_EVEN;
+	}
+	msg.setLcr = 0xff;
+
+	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+	msg.xonFlowControl = 0;
+	msg.setFlowControl = 0xff;
+	msg.forwardingLength = 16;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	/* Opening port */
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	}
+
+	/* Closing port */
+	else if (reset_port == 2) {
+		msg._txOn = 0;
+		msg._txOff = 1;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 0;
+		msg.rxOff = 1;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0;
+	}
+
+	/* Sending intermediate configs */
+	else {
+		msg._txOn = (!p_priv->break_on);
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = (p_priv->break_on);
+		msg.rxOn = 0;
+		msg.rxOff = 0;
+		msg.rxFlush = 0;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0x0;
+	}
+
+	/* Do handshaking outputs */
+	msg.setTxTriState_setRts = 0xff;
+	msg.txTriState_rts = p_priv->rts_state;
+
+	msg.setHskoa_setDtr = 0xff;
+	msg.hskoa_dtr = p_priv->dtr_state;
+
+	p_priv->resend_cont = 0;
+	memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
+	return 0;
+}
+
+static int keyspan_usa28_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa28_portControlMessage	msg;
+	struct keyspan_serial_private	 	*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					device_port, err;
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+	device_port = port->port_number;
+
+	/* only do something if we have a bulk out endpoint */
+	this_urb = p_priv->outcont_urb;
+	if (this_urb == NULL) {
+		dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
+		return -1;
+	}
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		dev_dbg(&port->dev, "%s already writing\n", __func__);
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa28_portControlMessage));
+
+	msg.setBaudRate = 1;
+	if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+					   &msg.baudHi, &msg.baudLo, NULL,
+					   device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+		dev_dbg(&port->dev, "%s - Invalid baud rate requested %d.\n",
+						__func__, p_priv->baud);
+		msg.baudLo = 0xff;
+		msg.baudHi = 0xb2;	/* Values for 9600 baud */
+	}
+
+	/* If parity is enabled, we must calculate it ourselves. */
+	msg.parity = 0;		/* XXX for now */
+
+	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+	msg.xonFlowControl = 0;
+
+	/* Do handshaking outputs, DTR is inverted relative to RTS */
+	msg.rts = p_priv->rts_state;
+	msg.dtr = p_priv->dtr_state;
+
+	msg.forwardingLength = 16;
+	msg.forwardMs = 10;
+	msg.breakThreshold = 45;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	/*msg.returnStatus = 1;
+	msg.resetDataToggle = 0xff;*/
+	/* Opening port */
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txForceXoff = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	}
+	/* Closing port */
+	else if (reset_port == 2) {
+		msg._txOn = 0;
+		msg._txOff = 1;
+		msg.txFlush = 0;
+		msg.txForceXoff = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 0;
+		msg.rxOff = 1;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0;
+	}
+	/* Sending intermediate configs */
+	else {
+		msg._txOn = (!p_priv->break_on);
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txForceXoff = 0;
+		msg.txBreak = (p_priv->break_on);
+		msg.rxOn = 0;
+		msg.rxOff = 0;
+		msg.rxFlush = 0;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0x0;
+	}
+
+	p_priv->resend_cont = 0;
+	memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed\n", __func__);
+
+	return 0;
+}
+
+static int keyspan_usa49_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa49_portControlMessage	msg;
+	struct usb_ctrlrequest 			*dr = NULL;
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					err, device_port;
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+
+	this_urb = s_priv->glocont_urb;
+
+	/* Work out which port within the device is being setup */
+	device_port = port->port_number;
+
+	/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dev_dbg(&port->dev, "%s - oops no urb for port.\n", __func__);
+		return -1;
+	}
+
+	dev_dbg(&port->dev, "%s - endpoint %x (%d)\n",
+		__func__, usb_pipeendpoint(this_urb->pipe), device_port);
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+
+	if (this_urb->status == -EINPROGRESS) {
+		/*  dev_dbg(&port->dev, "%s - already writing\n", __func__); */
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa49_portControlMessage));
+
+	msg.portNumber = device_port;
+
+	/* Only set baud rate if it's changed */
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0xff;
+		if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+						   &msg.baudHi, &msg.baudLo, &msg.prescaler,
+						   device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+				__func__, p_priv->baud);
+			msg.baudLo = 0;
+			msg.baudHi = 125;	/* Values for 9600 baud */
+			msg.prescaler = 10;
+		}
+		/* msg.setPrescaler = 0xff; */
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD) ?
+			USA_PARITY_ODD : USA_PARITY_EVEN;
+	}
+	msg.setLcr = 0xff;
+
+	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+	msg.xonFlowControl = 0;
+	msg.setFlowControl = 0xff;
+
+	msg.forwardingLength = 16;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	/* Opening port */
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+		msg.enablePort = 1;
+		msg.disablePort = 0;
+	}
+	/* Closing port */
+	else if (reset_port == 2) {
+		msg._txOn = 0;
+		msg._txOff = 1;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 0;
+		msg.rxOff = 1;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0;
+		msg.enablePort = 0;
+		msg.disablePort = 1;
+	}
+	/* Sending intermediate configs */
+	else {
+		msg._txOn = (!p_priv->break_on);
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = (p_priv->break_on);
+		msg.rxOn = 0;
+		msg.rxOff = 0;
+		msg.rxFlush = 0;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0x0;
+		msg.enablePort = 0;
+		msg.disablePort = 0;
+	}
+
+	/* Do handshaking outputs */
+	msg.setRts = 0xff;
+	msg.rts = p_priv->rts_state;
+
+	msg.setDtr = 0xff;
+	msg.dtr = p_priv->dtr_state;
+
+	p_priv->resend_cont = 0;
+
+	/* if the device is a 49wg, we send control message on usb
+	   control EP 0 */
+
+	if (d_details->product_id == keyspan_usa49wg_product_id) {
+		dr = (void *)(s_priv->ctrl_buf);
+		dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_OUT;
+		dr->bRequest = 0xB0;	/* 49wg control message */
+		dr->wValue = 0;
+		dr->wIndex = 0;
+		dr->wLength = cpu_to_le16(sizeof(msg));
+
+		memcpy(s_priv->glocont_buf, &msg, sizeof(msg));
+
+		usb_fill_control_urb(this_urb, serial->dev,
+				usb_sndctrlpipe(serial->dev, 0),
+				(unsigned char *)dr, s_priv->glocont_buf,
+				sizeof(msg), usa49_glocont_callback, serial);
+
+	} else {
+		memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+		/* send the data out the device on control endpoint */
+		this_urb->transfer_buffer_length = sizeof(msg);
+	}
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
+
+	return 0;
+}
+
+static int keyspan_usa90_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa90_portControlMessage	msg;
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					err;
+	u8						prescaler;
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+
+	/* only do something if we have a bulk out endpoint */
+	this_urb = p_priv->outcont_urb;
+	if (this_urb == NULL) {
+		dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
+		return -1;
+	}
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		dev_dbg(&port->dev, "%s already writing\n", __func__);
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa90_portControlMessage));
+
+	/* Only set baud rate if it's changed */
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0x01;
+		if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+						   &msg.baudHi, &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE) {
+			dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+				__func__, p_priv->baud);
+			p_priv->baud = 9600;
+			d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+				&msg.baudHi, &msg.baudLo, &prescaler, 0);
+		}
+		msg.setRxMode = 1;
+		msg.setTxMode = 1;
+	}
+
+	/* modes must always be correctly specified */
+	if (p_priv->baud > 57600) {
+		msg.rxMode = RXMODE_DMA;
+		msg.txMode = TXMODE_DMA;
+	} else {
+		msg.rxMode = RXMODE_BYHAND;
+		msg.txMode = TXMODE_BYHAND;
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD) ?
+			USA_PARITY_ODD : USA_PARITY_EVEN;
+	}
+	if (p_priv->old_cflag != p_priv->cflag) {
+		p_priv->old_cflag = p_priv->cflag;
+		msg.setLcr = 0x01;
+	}
+
+	if (p_priv->flow_control == flow_cts)
+		msg.txFlowControl = TXFLOW_CTS;
+	msg.setTxFlowControl = 0x01;
+	msg.setRxFlowControl = 0x01;
+
+	msg.rxForwardingLength = 16;
+	msg.rxForwardingTimeout = 16;
+	msg.txAckSetting = 0;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	/* Opening port */
+	if (reset_port == 1) {
+		msg.portEnabled = 1;
+		msg.rxFlush = 1;
+		msg.txBreak = (p_priv->break_on);
+	}
+	/* Closing port */
+	else if (reset_port == 2)
+		msg.portEnabled = 0;
+	/* Sending intermediate configs */
+	else {
+		msg.portEnabled = 1;
+		msg.txBreak = (p_priv->break_on);
+	}
+
+	/* Do handshaking outputs */
+	msg.setRts = 0x01;
+	msg.rts = p_priv->rts_state;
+
+	msg.setDtr = 0x01;
+	msg.dtr = p_priv->dtr_state;
+
+	p_priv->resend_cont = 0;
+	memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
+	return 0;
+}
+
+static int keyspan_usa67_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa67_portControlMessage	msg;
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					err, device_port;
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+
+	this_urb = s_priv->glocont_urb;
+
+	/* Work out which port within the device is being setup */
+	device_port = port->port_number;
+
+	/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dev_dbg(&port->dev, "%s - oops no urb for port.\n", __func__);
+		return -1;
+	}
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		/*  dev_dbg(&port->dev, "%s - already writing\n", __func__); */
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa67_portControlMessage));
+
+	msg.port = device_port;
+
+	/* Only set baud rate if it's changed */
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0xff;
+		if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+						   &msg.baudHi, &msg.baudLo, &msg.prescaler,
+						   device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+				__func__, p_priv->baud);
+			msg.baudLo = 0;
+			msg.baudHi = 125;	/* Values for 9600 baud */
+			msg.prescaler = 10;
+		}
+		msg.setPrescaler = 0xff;
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD) ?
+					USA_PARITY_ODD : USA_PARITY_EVEN;
+	}
+	msg.setLcr = 0xff;
+
+	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+	msg.xonFlowControl = 0;
+	msg.setFlowControl = 0xff;
+	msg.forwardingLength = 16;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	if (reset_port == 1) {
+		/* Opening port */
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	} else if (reset_port == 2) {
+		/* Closing port */
+		msg._txOn = 0;
+		msg._txOff = 1;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 0;
+		msg.rxOff = 1;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0;
+	} else {
+		/* Sending intermediate configs */
+		msg._txOn = (!p_priv->break_on);
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = (p_priv->break_on);
+		msg.rxOn = 0;
+		msg.rxOff = 0;
+		msg.rxFlush = 0;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0x0;
+	}
+
+	/* Do handshaking outputs */
+	msg.setTxTriState_setRts = 0xff;
+	msg.txTriState_rts = p_priv->rts_state;
+
+	msg.setHskoa_setDtr = 0xff;
+	msg.hskoa_dtr = p_priv->dtr_state;
+
+	p_priv->resend_cont = 0;
+
+	memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
+	return 0;
+}
+
+static void keyspan_send_setup(struct usb_serial_port *port, int reset_port)
+{
+	struct usb_serial *serial = port->serial;
+	struct keyspan_serial_private *s_priv;
+	const struct keyspan_device_details *d_details;
+
+	s_priv = usb_get_serial_data(serial);
+	d_details = s_priv->device_details;
+
+	switch (d_details->msg_format) {
+	case msg_usa26:
+		keyspan_usa26_send_setup(serial, port, reset_port);
+		break;
+	case msg_usa28:
+		keyspan_usa28_send_setup(serial, port, reset_port);
+		break;
+	case msg_usa49:
+		keyspan_usa49_send_setup(serial, port, reset_port);
+		break;
+	case msg_usa90:
+		keyspan_usa90_send_setup(serial, port, reset_port);
+		break;
+	case msg_usa67:
+		keyspan_usa67_send_setup(serial, port, reset_port);
+		break;
+	}
+}
+
+
+/* Gets called by the "real" driver (ie once firmware is loaded
+   and renumeration has taken place. */
+static int keyspan_startup(struct usb_serial *serial)
+{
+	int				i, err;
+	struct keyspan_serial_private 	*s_priv;
+	const struct keyspan_device_details	*d_details;
+
+	for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i)
+		if (d_details->product_id ==
+				le16_to_cpu(serial->dev->descriptor.idProduct))
+			break;
+	if (d_details == NULL) {
+		dev_err(&serial->dev->dev, "%s - unknown product id %x\n",
+		    __func__, le16_to_cpu(serial->dev->descriptor.idProduct));
+		return -ENODEV;
+	}
+
+	/* Setup private data for serial driver */
+	s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
+	if (!s_priv)
+		return -ENOMEM;
+
+	s_priv->instat_buf = kzalloc(INSTAT_BUFLEN, GFP_KERNEL);
+	if (!s_priv->instat_buf)
+		goto err_instat_buf;
+
+	s_priv->indat_buf = kzalloc(INDAT49W_BUFLEN, GFP_KERNEL);
+	if (!s_priv->indat_buf)
+		goto err_indat_buf;
+
+	s_priv->glocont_buf = kzalloc(GLOCONT_BUFLEN, GFP_KERNEL);
+	if (!s_priv->glocont_buf)
+		goto err_glocont_buf;
+
+	s_priv->ctrl_buf = kzalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+	if (!s_priv->ctrl_buf)
+		goto err_ctrl_buf;
+
+	s_priv->device_details = d_details;
+	usb_set_serial_data(serial, s_priv);
+
+	keyspan_setup_urbs(serial);
+
+	if (s_priv->instat_urb != NULL) {
+		err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL);
+		if (err != 0)
+			dev_dbg(&serial->dev->dev, "%s - submit instat urb failed %d\n", __func__, err);
+	}
+	if (s_priv->indat_urb != NULL) {
+		err = usb_submit_urb(s_priv->indat_urb, GFP_KERNEL);
+		if (err != 0)
+			dev_dbg(&serial->dev->dev, "%s - submit indat urb failed %d\n", __func__, err);
+	}
+
+	return 0;
+
+err_ctrl_buf:
+	kfree(s_priv->glocont_buf);
+err_glocont_buf:
+	kfree(s_priv->indat_buf);
+err_indat_buf:
+	kfree(s_priv->instat_buf);
+err_instat_buf:
+	kfree(s_priv);
+
+	return -ENOMEM;
+}
+
+static void keyspan_disconnect(struct usb_serial *serial)
+{
+	struct keyspan_serial_private *s_priv;
+
+	s_priv = usb_get_serial_data(serial);
+
+	usb_kill_urb(s_priv->instat_urb);
+	usb_kill_urb(s_priv->glocont_urb);
+	usb_kill_urb(s_priv->indat_urb);
+}
+
+static void keyspan_release(struct usb_serial *serial)
+{
+	struct keyspan_serial_private *s_priv;
+
+	s_priv = usb_get_serial_data(serial);
+
+	/* Make sure to unlink the URBs submitted in attach. */
+	usb_kill_urb(s_priv->instat_urb);
+	usb_kill_urb(s_priv->indat_urb);
+
+	usb_free_urb(s_priv->instat_urb);
+	usb_free_urb(s_priv->indat_urb);
+	usb_free_urb(s_priv->glocont_urb);
+
+	kfree(s_priv->ctrl_buf);
+	kfree(s_priv->glocont_buf);
+	kfree(s_priv->indat_buf);
+	kfree(s_priv->instat_buf);
+
+	kfree(s_priv);
+}
+
+static int keyspan_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct keyspan_serial_private *s_priv;
+	struct keyspan_port_private *p_priv;
+	const struct keyspan_device_details *d_details;
+	struct callbacks *cback;
+	int endp;
+	int port_num;
+	int i;
+
+	s_priv = usb_get_serial_data(serial);
+	d_details = s_priv->device_details;
+
+	p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL);
+	if (!p_priv)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) {
+		p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL);
+		if (!p_priv->in_buffer[i])
+			goto err_in_buffer;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) {
+		p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL);
+		if (!p_priv->out_buffer[i])
+			goto err_out_buffer;
+	}
+
+	p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL);
+	if (!p_priv->inack_buffer)
+		goto err_inack_buffer;
+
+	p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL);
+	if (!p_priv->outcont_buffer)
+		goto err_outcont_buffer;
+
+	p_priv->device_details = d_details;
+
+	/* Setup values for the various callback routines */
+	cback = &keyspan_callbacks[d_details->msg_format];
+
+	port_num = port->port_number;
+
+	/* Do indat endpoints first, once for each flip */
+	endp = d_details->indat_endpoints[port_num];
+	for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) {
+		p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp,
+						USB_DIR_IN, port,
+						p_priv->in_buffer[i],
+						IN_BUFLEN,
+						cback->indat_callback);
+	}
+	/* outdat endpoints also have flip */
+	endp = d_details->outdat_endpoints[port_num];
+	for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) {
+		p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp,
+						USB_DIR_OUT, port,
+						p_priv->out_buffer[i],
+						OUT_BUFLEN,
+						cback->outdat_callback);
+	}
+	/* inack endpoint */
+	p_priv->inack_urb = keyspan_setup_urb(serial,
+					d_details->inack_endpoints[port_num],
+					USB_DIR_IN, port,
+					p_priv->inack_buffer,
+					INACK_BUFLEN,
+					cback->inack_callback);
+	/* outcont endpoint */
+	p_priv->outcont_urb = keyspan_setup_urb(serial,
+					d_details->outcont_endpoints[port_num],
+					USB_DIR_OUT, port,
+					p_priv->outcont_buffer,
+					OUTCONT_BUFLEN,
+					 cback->outcont_callback);
+
+	usb_set_serial_port_data(port, p_priv);
+
+	return 0;
+
+err_outcont_buffer:
+	kfree(p_priv->inack_buffer);
+err_inack_buffer:
+	for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
+		kfree(p_priv->out_buffer[i]);
+err_out_buffer:
+	for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
+		kfree(p_priv->in_buffer[i]);
+err_in_buffer:
+	kfree(p_priv);
+
+	return -ENOMEM;
+}
+
+static int keyspan_port_remove(struct usb_serial_port *port)
+{
+	struct keyspan_port_private *p_priv;
+	int i;
+
+	p_priv = usb_get_serial_port_data(port);
+
+	usb_kill_urb(p_priv->inack_urb);
+	usb_kill_urb(p_priv->outcont_urb);
+	for (i = 0; i < 2; i++) {
+		usb_kill_urb(p_priv->in_urbs[i]);
+		usb_kill_urb(p_priv->out_urbs[i]);
+	}
+
+	usb_free_urb(p_priv->inack_urb);
+	usb_free_urb(p_priv->outcont_urb);
+	for (i = 0; i < 2; i++) {
+		usb_free_urb(p_priv->in_urbs[i]);
+		usb_free_urb(p_priv->out_urbs[i]);
+	}
+
+	kfree(p_priv->outcont_buffer);
+	kfree(p_priv->inack_buffer);
+	for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
+		kfree(p_priv->out_buffer[i]);
+	for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
+		kfree(p_priv->in_buffer[i]);
+
+	kfree(p_priv);
+
+	return 0;
+}
+
+/* Structs for the devices, pre and post renumeration. */
+static struct usb_serial_driver keyspan_pre_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "keyspan_no_firm",
+	},
+	.description		= "Keyspan - (without firmware)",
+	.id_table		= keyspan_pre_ids,
+	.num_ports		= 1,
+	.attach			= keyspan_fake_startup,
+};
+
+static struct usb_serial_driver keyspan_1port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "keyspan_1",
+	},
+	.description		= "Keyspan 1 port adapter",
+	.id_table		= keyspan_1port_ids,
+	.num_ports		= 1,
+	.open			= keyspan_open,
+	.close			= keyspan_close,
+	.dtr_rts		= keyspan_dtr_rts,
+	.write			= keyspan_write,
+	.write_room		= keyspan_write_room,
+	.set_termios		= keyspan_set_termios,
+	.break_ctl		= keyspan_break_ctl,
+	.tiocmget		= keyspan_tiocmget,
+	.tiocmset		= keyspan_tiocmset,
+	.attach			= keyspan_startup,
+	.disconnect		= keyspan_disconnect,
+	.release		= keyspan_release,
+	.port_probe		= keyspan_port_probe,
+	.port_remove		= keyspan_port_remove,
+};
+
+static struct usb_serial_driver keyspan_2port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "keyspan_2",
+	},
+	.description		= "Keyspan 2 port adapter",
+	.id_table		= keyspan_2port_ids,
+	.num_ports		= 2,
+	.open			= keyspan_open,
+	.close			= keyspan_close,
+	.dtr_rts		= keyspan_dtr_rts,
+	.write			= keyspan_write,
+	.write_room		= keyspan_write_room,
+	.set_termios		= keyspan_set_termios,
+	.break_ctl		= keyspan_break_ctl,
+	.tiocmget		= keyspan_tiocmget,
+	.tiocmset		= keyspan_tiocmset,
+	.attach			= keyspan_startup,
+	.disconnect		= keyspan_disconnect,
+	.release		= keyspan_release,
+	.port_probe		= keyspan_port_probe,
+	.port_remove		= keyspan_port_remove,
+};
+
+static struct usb_serial_driver keyspan_4port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "keyspan_4",
+	},
+	.description		= "Keyspan 4 port adapter",
+	.id_table		= keyspan_4port_ids,
+	.num_ports		= 4,
+	.open			= keyspan_open,
+	.close			= keyspan_close,
+	.dtr_rts		= keyspan_dtr_rts,
+	.write			= keyspan_write,
+	.write_room		= keyspan_write_room,
+	.set_termios		= keyspan_set_termios,
+	.break_ctl		= keyspan_break_ctl,
+	.tiocmget		= keyspan_tiocmget,
+	.tiocmset		= keyspan_tiocmset,
+	.attach			= keyspan_startup,
+	.disconnect		= keyspan_disconnect,
+	.release		= keyspan_release,
+	.port_probe		= keyspan_port_probe,
+	.port_remove		= keyspan_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&keyspan_pre_device, &keyspan_1port_device,
+	&keyspan_2port_device, &keyspan_4port_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, keyspan_ids_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("keyspan/usa28.fw");
+MODULE_FIRMWARE("keyspan/usa28x.fw");
+MODULE_FIRMWARE("keyspan/usa28xa.fw");
+MODULE_FIRMWARE("keyspan/usa28xb.fw");
+MODULE_FIRMWARE("keyspan/usa19.fw");
+MODULE_FIRMWARE("keyspan/usa19qi.fw");
+MODULE_FIRMWARE("keyspan/mpr.fw");
+MODULE_FIRMWARE("keyspan/usa19qw.fw");
+MODULE_FIRMWARE("keyspan/usa18x.fw");
+MODULE_FIRMWARE("keyspan/usa19w.fw");
+MODULE_FIRMWARE("keyspan/usa49w.fw");
+MODULE_FIRMWARE("keyspan/usa49wlc.fw");
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
new file mode 100644
index 0000000..38d43c4
--- /dev/null
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -0,0 +1,807 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Keyspan PDA / Xircom / Entrega Converter driver
+ *
+ * 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>
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/usb.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_DESC "USB Keyspan PDA Converter driver"
+
+struct keyspan_pda_private {
+	int			tx_room;
+	int			tx_throttled;
+	struct work_struct			wakeup_work;
+	struct work_struct			unthrottle_work;
+	struct usb_serial	*serial;
+	struct usb_serial_port	*port;
+};
+
+
+#define KEYSPAN_VENDOR_ID		0x06cd
+#define KEYSPAN_PDA_FAKE_ID		0x0103
+#define KEYSPAN_PDA_ID			0x0104 /* no clue */
+
+/* For Xircom PGSDB9 and older Entrega version of the same device */
+#define XIRCOM_VENDOR_ID		0x085a
+#define XIRCOM_FAKE_ID			0x8027
+#define XIRCOM_FAKE_ID_2		0x8025 /* "PGMFHUB" serial */
+#define ENTREGA_VENDOR_ID		0x1645
+#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[] = {
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
+	{ }						/* 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) },
+	{ }
+};
+#endif
+
+static void keyspan_pda_wakeup_write(struct work_struct *work)
+{
+	struct keyspan_pda_private *priv =
+		container_of(work, struct keyspan_pda_private, wakeup_work);
+	struct usb_serial_port *port = priv->port;
+
+	tty_port_tty_wakeup(&port->port);
+}
+
+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;
+	int result;
+
+	/* 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 */
+				 0, /* index */
+				 NULL,
+				 0,
+				 2000);
+	if (result < 0)
+		dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n",
+			__func__, result);
+}
+
+
+static void keyspan_pda_rx_interrupt(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int len = urb->actual_length;
+	int retval;
+	int status = urb->status;
+	struct keyspan_pda_private *priv;
+	priv = usb_get_serial_port_data(port);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
+		goto exit;
+	}
+
+	if (len < 1) {
+		dev_warn(&port->dev, "short message received\n");
+		goto exit;
+	}
+
+	/* see if the message is data or a status interrupt */
+	switch (data[0]) {
+	case 0:
+		 /* rest of message is rx data */
+		if (len < 2)
+			break;
+		tty_insert_flip_string(&port->port, data + 1, len - 1);
+		tty_flip_buffer_push(&port->port);
+		break;
+	case 1:
+		/* status interrupt */
+		if (len < 3) {
+			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]);
+		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);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&port->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__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;
+
+	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 */
+
+	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;
+	int bindex;
+
+	switch (baud) {
+	case 110:
+		bindex = 0;
+		break;
+	case 300:
+		bindex = 1;
+		break;
+	case 1200:
+		bindex = 2;
+		break;
+	case 2400:
+		bindex = 3;
+		break;
+	case 4800:
+		bindex = 4;
+		break;
+	case 9600:
+		bindex = 5;
+		break;
+	case 19200:
+		bindex = 6;
+		break;
+	case 38400:
+		bindex = 7;
+		break;
+	case 57600:
+		bindex = 8;
+		break;
+	case 115200:
+		bindex = 9;
+		break;
+	default:
+		bindex = 5;	/* Default to 9600 */
+		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
+			     | USB_RECIP_INTERFACE
+			     | USB_DIR_OUT, /* type */
+			     bindex, /* value */
+			     0, /* index */
+			     NULL, /* &data */
+			     0, /* size */
+			     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;
+	struct usb_serial *serial = port->serial;
+	int value;
+	int result;
+
+	if (break_state == -1)
+		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,
+			value, 0, NULL, 0, 2000);
+	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. */
+
+	speed = tty_get_baud_rate(tty);
+	speed = keyspan_pda_setbaud(serial, speed);
+
+	if (speed == 0) {
+		dev_dbg(&port->dev, "can't handle requested baud rate\n");
+		/* 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 */
+	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 */
+
+static int keyspan_pda_get_modem_info(struct usb_serial *serial,
+				      unsigned char *value)
+{
+	int rc;
+	u8 *data;
+
+	data = kmalloc(1, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			     3, /* get pins */
+			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
+			     0, 0, data, 1, 2000);
+	if (rc == 1)
+		*value = *data;
+	else if (rc >= 0)
+		rc = -EIO;
+
+	kfree(data);
+	return rc;
+}
+
+
+static int keyspan_pda_set_modem_info(struct usb_serial *serial,
+				      unsigned char value)
+{
+	int rc;
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     3, /* set pins */
+			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
+			     value, 0, NULL, 0, 2000);
+	return rc;
+}
+
+static int keyspan_pda_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	int rc;
+	unsigned char status;
+	int value;
+
+	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);
+	return value;
+}
+
+static int keyspan_pda_tiocmset(struct tty_struct *tty,
+				unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	int rc;
+	unsigned char status;
+
+	rc = keyspan_pda_get_modem_info(serial, &status);
+	if (rc < 0)
+		return rc;
+
+	if (set & TIOCM_RTS)
+		status |= (1<<2);
+	if (set & TIOCM_DTR)
+		status |= (1<<7);
+
+	if (clear & TIOCM_RTS)
+		status &= ~(1<<2);
+	if (clear & TIOCM_DTR)
+		status &= ~(1<<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)
+{
+	struct usb_serial *serial = port->serial;
+	int request_unthrottle = 0;
+	int rc = 0;
+	struct keyspan_pda_private *priv;
+
+	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;
+	}
+
+	/* 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);
+		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;
+		}
+
+		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) {
+		/* 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;
+
+		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;
+	}
+
+	if (request_unthrottle) {
+		priv->tx_throttled = 1; /* block writers */
+		schedule_work(&priv->unthrottle_work);
+	}
+
+	rc = count;
+exit:
+	if (rc < 0)
+		set_bit(0, &port->write_urbs_free);
+	return rc;
+}
+
+
+static void keyspan_pda_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct keyspan_pda_private *priv;
+
+	set_bit(0, &port->write_urbs_free);
+	priv = usb_get_serial_port_data(port);
+
+	/* queue up a wakeup at scheduler time */
+	schedule_work(&priv->wakeup_work);
+}
+
+
+static int keyspan_pda_write_room(struct tty_struct *tty)
+{
+	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;
+}
+
+
+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;
+
+	priv = usb_get_serial_port_data(port);
+
+	/* when throttled, return at least WAKEUP_CHARS to tell select() (via
+	   n_tty.c:normal_poll() ) that we're not writeable. */
+
+	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;
+}
+
+
+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));
+	else
+		keyspan_pda_set_modem_info(serial, 0);
+}
+
+
+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;
+
+	/* find out how much room is in the Tx ring */
+	room = kmalloc(1, GFP_KERNEL);
+	if (!room)
+		return -ENOMEM;
+
+	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;
+
+	/*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;
+	}
+error:
+	kfree(room);
+	return rc;
+}
+static void keyspan_pda_close(struct usb_serial_port *port)
+{
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+
+/* download the firmware to a "fake" device (pre-renumeration) */
+static int keyspan_pda_fake_startup(struct usb_serial *serial)
+{
+	int response;
+	const char *fw_name;
+
+	/* download the firmware here ... */
+	response = ezusb_fx1_set_reset(serial->dev, 1);
+
+	if (0) { ; }
+#ifdef KEYSPAN
+	else if (le16_to_cpu(serial->dev->descriptor.idVendor) == 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))
+		fw_name = "keyspan_pda/xircom_pgs.fw";
+#endif
+	else {
+		dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
+		dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
+			fw_name);
+		return -ENOENT;
+	}
+
+	/* 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. */
+	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)
+{
+
+	struct keyspan_pda_private *priv;
+
+	priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
+	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);
+
+	return 0;
+}
+
+static int keyspan_pda_port_remove(struct usb_serial_port *port)
+{
+	struct keyspan_pda_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+#ifdef KEYSPAN
+static struct usb_serial_driver keyspan_pda_fake_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"keyspan_pda_pre",
+	},
+	.description =		"Keyspan PDA - (prerenumeration)",
+	.id_table =		id_table_fake,
+	.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 = {
+		.owner =	THIS_MODULE,
+		.name =		"keyspan_pda",
+	},
+	.description =		"Keyspan PDA",
+	.id_table =		id_table_std,
+	.num_ports =		1,
+	.num_bulk_out =		1,
+	.num_interrupt_in =	1,
+	.dtr_rts =		keyspan_pda_dtr_rts,
+	.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,
+	.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,
+	.break_ctl =		keyspan_pda_break_ctl,
+	.tiocmget =		keyspan_pda_tiocmget,
+	.tiocmset =		keyspan_pda_tiocmset,
+	.port_probe =		keyspan_pda_port_probe,
+	.port_remove =		keyspan_pda_port_remove,
+};
+
+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
+};
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h
new file mode 100644
index 0000000..09e21e8
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa26msg.h
@@ -0,0 +1,260 @@
+/*
+	usa26msg.h
+
+	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Message Formats for the USA28X
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.    
+
+	Third revision: USA28X version (aka USA26)
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USAxx, transmit) messages contain a 
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the 
+	completion of transmit; 0x00 otherwise), followed by data:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of 63.
+
+	USB IN (USAxx -> host, receive) messages begin with a status
+	byte in which the 0x80 bit is either:
+
+		(a)	0x80 bit clear
+			indicates that the bytes following it are all data
+			bytes:
+
+				STAT DATA DATA DATA DATA DATA ...
+
+			for a total of up to 63 DATA bytes,
+
+	or:
+
+		(b)	0x80 bit set
+			indicates that the bytes following alternate data and
+			status bytes:
+
+				STAT DATA STAT DATA STAT DATA STAT DATA ...
+
+			for a total of up to 32 DATA bytes.
+
+	The valid bits in the STAT bytes are:
+
+		OVERRUN	0x02
+		PARITY	0x04
+		FRAMING	0x08
+		BREAK	0x10
+
+	Notes:
+
+	(1) The OVERRUN bit can appear in either (a) or (b) format
+		messages, but the but the PARITY/FRAMING/BREAK bits
+		only appear in (b) format messages.
+	(2) For the host to determine the exact point at which the
+		overrun occurred (to identify the point in the data
+		stream at which the data was lost), it needs to count
+		128 characters, starting at the first character of the
+		message in which OVERRUN was reported; the lost character(s)
+		would have been received between the 128th and 129th
+		characters.
+	(3)	An RX data message in which the first byte has 0x80 clear
+		serves as a "break off" indicator.
+
+	revision history:
+
+	1999feb10	add reportHskiaChanges to allow us to ignore them
+	1999feb10	add txAckThreshold for fast+loose throughput enhancement
+	1999mar30	beef up support for RX error reporting
+	1999apr14	add resetDataToggle to control message
+	2000jan04	merge with usa17msg.h
+	2000jun01	add extended BSD-style copyright text
+	2001jul05	change message format to improve OVERRUN case
+
+	Note on shared names:
+
+	In the case of fields which have been merged between the USA17
+	and USA26 definitions, the USA26 definition is the first part
+	of the name and the USA17 definition is the second part of the
+	name; both meanings are described below.
+*/
+
+#ifndef	__USA26MSG__
+#define	__USA26MSG__
+
+
+struct keyspan_usa26_portControlMessage
+{
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the USA26):
+	*/
+	u8	setClocking,	// BOTH: host requests baud rate be set
+		baudLo,		// BOTH: host does baud divisor calculation
+		baudHi,		// BOTH: baudHi is only used for first port (gives lower rates)
+		externalClock_txClocking,
+					// USA26: 0=internal, other=external
+					// USA17: 0=internal, other=external/RI
+		rxClocking,		// USA17: 0=internal, 1=external/RI, other=external/DSR
+
+
+		setLcr,			// BOTH: host requests lcr be set
+		lcr,			// BOTH: use PARITY, STOPBITS, DATABITS below
+
+		setFlowControl,		// BOTH: host requests flow control be set
+		ctsFlowControl,		// BOTH: 1=use CTS flow control, 0=don't
+		xonFlowControl,		// BOTH: 1=use XON/XOFF flow control, 0=don't
+		xonChar,		// BOTH: specified in current character format
+		xoffChar,		// BOTH: specified in current character format
+
+		setTxTriState_setRts,
+					// USA26: host requests TX tri-state be set
+					// USA17: host requests RTS output be set
+		txTriState_rts,		// BOTH: 1=active (normal), 0=tristate (off)
+
+		setHskoa_setDtr,
+					// USA26: host requests HSKOA output be set
+					// USA17: host requests DTR output be set
+		hskoa_dtr,		// BOTH: 1=on, 0=off
+
+		setPrescaler,		// USA26: host requests prescalar be set (default: 13)
+		prescaler;		// BOTH: specified as N/8; values 8-ff are valid
+					// must be set any time internal baud rate is set;
+					// must not be set when external clocking is used
+					// note: in USA17, prescaler is applied whenever
+					// setClocking is requested
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be specified correctly in every host message).
+	*/
+	u8	forwardingLength,  // BOTH: forward when this number of chars available
+		reportHskiaChanges_dsrFlowControl,
+						// USA26: 1=normal; 0=ignore external clock
+						// USA17: 1=use DSR flow control, 0=don't
+		txAckThreshold,	// BOTH: 0=not allowed, 1=normal, 2-255 deliver ACK faster
+		loopbackMode;	// BOTH: 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// BOTH: enable transmitting (and continue if there's data)
+		_txOff,			// BOTH: stop transmitting
+		txFlush,		// BOTH: toss outbound data
+		txBreak,		// BOTH: turn on break (cleared by _txOn)
+		rxOn,			// BOTH: turn on receiver
+		rxOff,			// BOTH: turn off receiver
+		rxFlush,		// BOTH: toss inbound data
+		rxForward,		// BOTH: forward all inbound data, NOW (as if fwdLen==1)
+		returnStatus,	// BOTH: return current status (even if it hasn't changed)
+		resetDataToggle;// BOTH: reset data toggle state to DATA0
+	
+};
+
+// defines for bits in lcr
+#define	USA_DATABITS_5		0x00
+#define	USA_DATABITS_6		0x01
+#define	USA_DATABITS_7		0x02
+#define	USA_DATABITS_8		0x03
+#define	STOPBITS_5678_1	0x00	// 1 stop bit for all byte sizes
+#define	STOPBITS_5_1p5	0x04	// 1.5 stop bits for 5-bit byte
+#define	STOPBITS_678_2	0x04	// 2 stop bits for 6/7/8-bit byte
+#define	USA_PARITY_NONE		0x00
+#define	USA_PARITY_ODD		0x08
+#define	USA_PARITY_EVEN		0x18
+#define	PARITY_1		0x28
+#define	PARITY_0		0x38
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+struct keyspan_usa26_portStatusMessage	// one for each port
+{
+	u8	port,			// BOTH: 0=first, 1=second, other=see below
+		hskia_cts,		// USA26: reports HSKIA pin
+						// USA17: reports CTS pin
+		gpia_dcd,		// USA26: reports GPIA pin
+						// USA17: reports DCD pin
+		dsr,			// USA17: reports DSR pin
+		ri,				// USA17: reports RI pin
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		controlResponse;// 1=a control message has been processed
+};
+
+// bits in RX data message when STAT byte is included
+#define	RXERROR_OVERRUN	0x02
+#define	RXERROR_PARITY	0x04
+#define	RXERROR_FRAMING	0x08
+#define	RXERROR_BREAK	0x10
+
+struct keyspan_usa26_globalControlMessage
+{
+	u8	sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+};
+
+struct keyspan_usa26_globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+};
+
+struct keyspan_usa26_globalDebugMessage
+{
+	u8	port,				// 2
+		a,
+		b,
+		c,
+		d;
+};
+
+// ie: the maximum length of an EZUSB endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+// status rationing tuning value (each port gets checked each n ms)
+#define	STATUS_RATION	10
+
+#endif
+
+
diff --git a/drivers/usb/serial/keyspan_usa28msg.h b/drivers/usb/serial/keyspan_usa28msg.h
new file mode 100644
index 0000000..dee454c
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa28msg.h
@@ -0,0 +1,201 @@
+/*
+	usa28msg.h
+
+	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Message Formats for the USA26X
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.    
+
+	Note: these message formats are common to USA18, USA19, and USA28;
+	(for USA28X, see usa26msg.h)
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USA28, transmit) messages contain a 
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the 
+	completion of transmit; 0x00 otherwise), followed by data.
+	If the port is configured for parity, the data will be an 
+	alternating string of parity and data bytes, so the message
+	format will be:
+
+		RQSTACK PAR DAT PAR DAT ...
+
+	so the maximum length is 63 bytes (1 + 62, or 31 data bytes);
+	always an odd number for the total message length.
+
+	If there is no parity, the format is simply:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of 63.
+
+	USB IN (USA28 -> host, receive) messages contain data and parity
+	if parity is configred, thusly:
+	
+		DAT PAR DAT PAR DAT PAR ...
+
+	for a total of 32 data bytes;
+	
+	If parity is not configured, the format is:
+
+		DAT DAT DAT ...
+
+	for a total of 64 data bytes.
+
+	In the TX messages (USB OUT), the 0x01 bit of the PARity byte is 
+	the parity bit.  In the RX messages (USB IN), the PARity byte is 
+	the content of the 8051's status register; the parity bit 
+	(RX_PARITY_BIT) is the 0x04 bit.
+
+	revision history:
+
+	1999may06	add resetDataToggle to control message
+	2000mar21	add rs232invalid to status response message
+	2000apr04	add 230.4Kb definition to setBaudRate
+	2000apr13	add/remove loopbackMode switch
+	2000apr13	change definition of setBaudRate to cover 115.2Kb, too
+	2000jun01	add extended BSD-style copyright text
+*/
+
+#ifndef	__USA28MSG__
+#define	__USA28MSG__
+
+
+struct keyspan_usa28_portControlMessage
+{
+	/*
+		there are four types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the USA28):
+	*/
+	u8	setBaudRate,	// 0=don't set, 1=baudLo/Hi, 2=115.2K, 3=230.4K
+		baudLo,			// host does baud divisor calculation
+		baudHi;			// baudHi is only used for first port (gives lower rates)
+
+	/*
+		2.	configuration changes which are done every time (because it's
+			hardly more trouble to do them than to check whether to do them):
+	*/
+	u8	parity,			// 1=use parity, 0=don't
+		ctsFlowControl,	        // all except 19Q: 1=use CTS flow control, 0=don't
+					// 19Q: 0x08:CTSflowControl 0x10:DSRflowControl
+		xonFlowControl,	// 1=use XON/XOFF flow control, 0=don't
+		rts,			// 1=on, 0=off
+		dtr;			// 1=on, 0=off
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be correct in every host message).
+	*/
+	u8	forwardingLength,  // forward when this number of chars available
+		forwardMs,		// forward this many ms after last rx data
+		breakThreshold,	// specified in ms, 1-255 (see note below)
+		xonChar,		// specified in current character format
+		xoffChar;		// specified in current character format
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// enable transmitting (and continue if there's data)
+		_txOff,			// stop transmitting
+		txFlush,		// toss outbound data
+		txForceXoff,	// pretend we've received XOFF
+		txBreak,		// turn on break (leave on until txOn clears it)
+		rxOn,			// turn on receiver
+		rxOff,			// turn off receiver
+		rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW
+		returnStatus,	// return current status n times (1 or 2)
+		resetDataToggle;// reset data toggle state to DATA0
+	
+};
+
+struct keyspan_usa28_portStatusMessage
+{
+	u8	port,			// 0=first, 1=second, 2=global (see below)
+		cts,
+		dsr,			// (not used in all products)
+		dcd,
+
+		ri,				// (not used in all products)
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		dataLost,		// count of lost chars; wraps; not guaranteed exact
+
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		rxBreak,		// 1=we're in break state
+		rs232invalid,	// 1=no valid signals on rs-232 inputs
+		controlResponse;// 1=a control messages has been processed
+};
+
+// bit defines in txState
+#define	TX_OFF			0x01	// requested by host txOff command
+#define	TX_XOFF			0x02	// either real, or simulated by host
+
+struct keyspan_usa28_globalControlMessage
+{
+	u8	sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+};
+
+struct keyspan_usa28_globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+};
+
+struct keyspan_usa28_globalDebugMessage
+{
+	u8	port,				// 2
+		n,					// typically a count/status byte
+		b;					// typically a data byte
+};
+
+// ie: the maximum length of an EZUSB endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// the parity bytes have only one significant bit
+#define	RX_PARITY_BIT			0x04
+#define	TX_PARITY_BIT			0x01
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+#endif
+
diff --git a/drivers/usb/serial/keyspan_usa49msg.h b/drivers/usb/serial/keyspan_usa49msg.h
new file mode 100644
index 0000000..163b2de
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa49msg.h
@@ -0,0 +1,282 @@
+/*
+	usa49msg.h
+
+	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Message Formats for the USA49W
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.    
+
+	4th revision: USA49W version
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USAxx, transmit) messages contain a 
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the 
+	completion of transmit; 0x00 otherwise), followed by data:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of 63.
+
+	USB IN (USAxx -> host, receive) messages begin with a status
+	byte in which the 0x80 bit is either:
+				   	
+		(a)	0x80 bit clear
+			indicates that the bytes following it are all data
+			bytes:
+
+				STAT DATA DATA DATA DATA DATA ...
+
+			for a total of up to 63 DATA bytes,
+
+	or:
+
+		(b)	0x80 bit set
+			indiates that the bytes following alternate data and
+			status bytes:
+
+				STAT DATA STAT DATA STAT DATA STAT DATA ...
+
+			for a total of up to 32 DATA bytes.
+
+	The valid bits in the STAT bytes are:
+
+		OVERRUN	0x02
+		PARITY	0x04
+		FRAMING	0x08
+		BREAK	0x10
+
+	Notes:
+	
+	(1) The OVERRUN bit can appear in either (a) or (b) format
+		messages, but the but the PARITY/FRAMING/BREAK bits
+		only appear in (b) format messages.
+	(2) For the host to determine the exact point at which the
+		overrun occurred (to identify the point in the data
+		stream at which the data was lost), it needs to count
+		128 characters, starting at the first character of the
+		message in which OVERRUN was reported; the lost character(s)
+		would have been received between the 128th and 129th
+		characters.
+	(3)	An RX data message in which the first byte has 0x80 clear
+		serves as a "break off" indicator.
+	(4)	a control message specifying disablePort will be answered
+		with a status message, but no further status will be sent
+		until a control messages with enablePort is sent
+
+	revision history:
+
+	1999feb10	add reportHskiaChanges to allow us to ignore them
+	1999feb10	add txAckThreshold for fast+loose throughput enhancement
+	1999mar30	beef up support for RX error reporting
+	1999apr14	add resetDataToggle to control message
+	2000jan04	merge with usa17msg.h
+	2000mar08	clone from usa26msg.h -> usa49msg.h
+	2000mar09	change to support 4 ports
+	2000may03	change external clocking to match USA-49W hardware
+	2000jun01	add extended BSD-style copyright text
+	2001jul05	change message format to improve OVERRUN case
+*/
+
+#ifndef	__USA49MSG__
+#define	__USA49MSG__
+
+
+/*
+	Host->device messages sent on the global control endpoint:
+
+	portNumber	message
+	----------	--------------------
+	0,1,2,3		portControlMessage
+	0x80		globalControlMessage
+*/
+
+struct keyspan_usa49_portControlMessage
+{
+	/*
+		0.	0/1/2/3 	port control message follows
+			0x80 set	non-port control message follows
+	*/
+	u8	portNumber,
+
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the USA26):
+	*/
+		setClocking,	// host requests baud rate be set
+		baudLo,			// host does baud divisor calculation
+		baudHi,			// baudHi is only used for first port (gives lower rates)
+		prescaler,		// specified as N/8; values 8-ff are valid
+						// must be set any time internal baud rate is set;
+		txClocking,		// 0=internal, 1=external/DSR
+		rxClocking,		// 0=internal, 1=external/DSR
+
+		setLcr,			// host requests lcr be set
+		lcr,			// use PARITY, STOPBITS, DATABITS below
+
+		setFlowControl,	// host requests flow control be set
+		ctsFlowControl,	// 1=use CTS flow control, 0=don't
+		xonFlowControl,	// 1=use XON/XOFF flow control, 0=don't
+		xonChar,		// specified in current character format
+		xoffChar,		// specified in current character format
+
+		setRts,			// host requests RTS output be set
+		rts,			// 1=active, 0=inactive
+
+		setDtr,			// host requests DTR output be set
+		dtr;			// 1=on, 0=off
+
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be specified correctly in every host message).
+	*/
+	u8	forwardingLength,  // forward when this number of chars available
+		dsrFlowControl,	// 1=use DSR flow control, 0=don't
+		txAckThreshold,	// 0=not allowed, 1=normal, 2-255 deliver ACK faster
+		loopbackMode;	// 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// enable transmitting (and continue if there's data)
+		_txOff,			// stop transmitting
+		txFlush,		// toss outbound data
+		txBreak,		// turn on break (cleared by _txOn)
+		rxOn,			// turn on receiver
+		rxOff,			// turn off receiver
+		rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW (as if fwdLen==1)
+		returnStatus,	// return current status (even if it hasn't changed)
+		resetDataToggle,// reset data toggle state to DATA0
+		enablePort,		// start servicing port (move data, check status)
+		disablePort;	// stop servicing port (does implicit tx/rx flush/off)
+	
+};
+
+// defines for bits in lcr
+#define	USA_DATABITS_5		0x00
+#define	USA_DATABITS_6		0x01
+#define	USA_DATABITS_7		0x02
+#define	USA_DATABITS_8		0x03
+#define	STOPBITS_5678_1		0x00	// 1 stop bit for all byte sizes
+#define	STOPBITS_5_1p5		0x04	// 1.5 stop bits for 5-bit byte
+#define	STOPBITS_678_2		0x04	// 2 stop bits for 6/7/8-bit byte
+#define	USA_PARITY_NONE		0x00
+#define	USA_PARITY_ODD		0x08
+#define	USA_PARITY_EVEN		0x18
+#define	PARITY_1			0x28
+#define	PARITY_0			0x38
+
+/*
+	during normal operation, status messages are returned 
+	to the host whenever the board detects changes.  In some
+	circumstances (e.g. Windows), status messages from the
+	device cause problems; to shut them off, the host issues
+	a control message with the disableStatusMessages flags
+	set (to any non-zero value).  The device will respond to
+	this message, and then suppress further status messages;
+	it will resume sending status messages any time the host
+	sends any control message (either global or port-specific).
+*/
+
+struct keyspan_usa49_globalControlMessage
+{
+	u8	portNumber,			// 0x80
+		sendGlobalStatus,	// 1/2=number of status responses requested
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount,	// a cycling value
+		remoteWakeupEnable,		// 0x10=P1, 0x20=P2, 0x40=P3, 0x80=P4
+		disableStatusMessages;	// 1=send no status until host talks
+};
+
+/*
+	Device->host messages send on the global status endpoint
+
+	portNumber			message
+	----------			--------------------
+	0x00,0x01,0x02,0x03	portStatusMessage
+	0x80				globalStatusMessage
+	0x81				globalDebugMessage
+*/
+
+struct keyspan_usa49_portStatusMessage	// one for each port
+{
+	u8	portNumber,		// 0,1,2,3
+		cts,			// reports CTS pin
+		dcd,			// reports DCD pin
+		dsr,			// reports DSR pin
+		ri,				// reports RI pin
+		_txOff,			// transmit has been disabled (by host)
+		_txXoff,		// transmit is in XOFF state (either host or RX XOFF)
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		controlResponse,// 1=a control message has been processed
+		txAck,			// ACK (data TX complete)
+		rs232valid;		// RS-232 signal valid
+};
+
+// bits in RX data message when STAT byte is included
+#define	RXERROR_OVERRUN	0x02
+#define	RXERROR_PARITY	0x04
+#define	RXERROR_FRAMING	0x08
+#define	RXERROR_BREAK	0x10
+
+struct keyspan_usa49_globalStatusMessage
+{
+	u8	portNumber,			// 0x80=globalStatusMessage
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+};
+
+struct keyspan_usa49_globalDebugMessage
+{
+	u8	portNumber,			// 0x81=globalDebugMessage
+		n,					// typically a count/status byte
+		b;					// typically a data byte
+};
+
+// ie: the maximum length of an EZUSB endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+// status rationing tuning value (each port gets checked each n ms)
+#define	STATUS_RATION	10
+
+#endif
diff --git a/drivers/usb/serial/keyspan_usa67msg.h b/drivers/usb/serial/keyspan_usa67msg.h
new file mode 100644
index 0000000..20fa3e2
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa67msg.h
@@ -0,0 +1,254 @@
+/*
+	usa67msg.h
+
+	Copyright (c) 1998-2007 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Firmware to run on Anchor FX1
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2007 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. Redistributions in binary form must reproduce the above copyright
+   	notice, this list of conditions and the following disclaimer in the
+   	documentation and/or other materials provided with the distribution.
+
+	3. The name of InnoSys Incorprated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.
+
+	Fourth revision: This message format supports the USA28XG
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USAxx, transmit) messages contain a
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the
+	completion of transmit; 0x00 otherwise), followed by data:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of up to 63.
+
+	USB IN (USAxx -> host, receive) messages begin with a status
+	byte in which the 0x80 bit is either:
+
+		(a)	0x80 bit clear
+			indicates that the bytes following it are all data
+			bytes:
+
+				STAT DATA DATA DATA DATA DATA ...
+
+			for a total of up to 63 DATA bytes,
+
+	or:
+
+		(b)	0x80 bit set
+			indiates that the bytes following alternate data and
+			status bytes:
+
+				STAT DATA STAT DATA STAT DATA STAT DATA ...
+
+			for a total of up to 32 DATA bytes.
+
+	The valid bits in the STAT bytes are:
+
+		OVERRUN	0x02
+		PARITY	0x04
+		FRAMING	0x08
+		BREAK	0x10
+
+	Notes:
+
+	(1) The OVERRUN bit can appear in either (a) or (b) format
+		messages, but the but the PARITY/FRAMING/BREAK bits
+		only appear in (b) format messages.
+	(2) For the host to determine the exact point at which the
+		overrun occurred (to identify the point in the data
+		stream at which the data was lost), it needs to count
+		128 characters, starting at the first character of the
+		message in which OVERRUN was reported; the lost character(s)
+		would have been received between the 128th and 129th
+		characters.
+	(3)	An RX data message in which the first byte has 0x80 clear
+		serves as a "break off" indicator.
+
+	revision history:
+
+	1999feb10	add reportHskiaChanges to allow us to ignore them
+	1999feb10	add txAckThreshold for fast+loose throughput enhancement
+	1999mar30	beef up support for RX error reporting
+	1999apr14	add resetDataToggle to control message
+	2000jan04	merge with usa17msg.h
+	2000jun01	add extended BSD-style copyright text
+	2001jul05	change message format to improve OVERRUN case
+	2002jun05	update copyright date, improve comments
+	2006feb06	modify for FX1 chip
+
+*/
+
+#ifndef	__USA67MSG__
+#define	__USA67MSG__
+
+
+// all things called "ControlMessage" are sent on the 'control' endpoint
+
+typedef struct keyspan_usa67_portControlMessage
+{
+	u8	port;		// 0 or 1 (selects port)
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the device):
+	*/
+	u8	setClocking,	// host requests baud rate be set
+		baudLo,			// host does baud divisor calculation
+		baudHi,			// baudHi is only used for first port (gives lower rates)
+		externalClock_txClocking,
+						// 0=internal, other=external
+
+		setLcr,			// host requests lcr be set
+		lcr,			// use PARITY, STOPBITS, DATABITS below
+
+		setFlowControl,	// host requests flow control be set
+		ctsFlowControl,	// 1=use CTS flow control, 0=don't
+		xonFlowControl,	// 1=use XON/XOFF flow control, 0=don't
+		xonChar,		// specified in current character format
+		xoffChar,		// specified in current character format
+
+		setTxTriState_setRts,
+						// host requests TX tri-state be set
+		txTriState_rts,	// 1=active (normal), 0=tristate (off)
+
+		setHskoa_setDtr,
+						// host requests HSKOA output be set
+		hskoa_dtr,		// 1=on, 0=off
+
+		setPrescaler,	// host requests prescalar be set (default: 13)
+		prescaler;		// specified as N/8; values 8-ff are valid
+						// must be set any time internal baud rate is set;
+						// must not be set when external clocking is used
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be specified correctly in every host message).
+	*/
+	u8	forwardingLength,  // forward when this number of chars available
+		reportHskiaChanges_dsrFlowControl,
+						// 1=normal; 0=ignore external clock
+						// 1=use DSR flow control, 0=don't
+		txAckThreshold,	// 0=not allowed, 1=normal, 2-255 deliver ACK faster
+		loopbackMode;	// 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// enable transmitting (and continue if there's data)
+		_txOff,			// stop transmitting
+		txFlush,		// toss outbound data
+		txBreak,		// turn on break (cleared by _txOn)
+		rxOn,			// turn on receiver
+		rxOff,			// turn off receiver
+		rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW (as if fwdLen==1)
+		returnStatus,	// return current status (even if it hasn't changed)
+		resetDataToggle;// reset data toggle state to DATA0
+
+} keyspan_usa67_portControlMessage;
+
+// defines for bits in lcr
+#define	USA_DATABITS_5		0x00
+#define	USA_DATABITS_6		0x01
+#define	USA_DATABITS_7		0x02
+#define	USA_DATABITS_8		0x03
+#define	STOPBITS_5678_1		0x00	// 1 stop bit for all byte sizes
+#define	STOPBITS_5_1p5		0x04	// 1.5 stop bits for 5-bit byte
+#define	STOPBITS_678_2		0x04	// 2 stop bits for 6/7/8-bit byte
+#define	USA_PARITY_NONE		0x00
+#define	USA_PARITY_ODD		0x08
+#define	USA_PARITY_EVEN		0x18
+#define	PARITY_1			0x28
+#define	PARITY_0			0x38
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+typedef struct keyspan_usa67_portStatusMessage	// one for each port
+{
+	u8	port,			// 0=first, 1=second, other=see below
+		hskia_cts,		// reports HSKIA pin
+		gpia_dcd,		// reports GPIA pin
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		txAck,			// indicates a TX message acknowledgement
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		controlResponse;// 1=a control message has been processed
+} keyspan_usa67_portStatusMessage;
+
+// bits in RX data message when STAT byte is included
+#define	RXERROR_OVERRUN	0x02
+#define	RXERROR_PARITY	0x04
+#define	RXERROR_FRAMING	0x08
+#define	RXERROR_BREAK	0x10
+
+typedef struct keyspan_usa67_globalControlMessage
+{
+	u8	port,	 			// 3
+		sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+} keyspan_usa67_globalControlMessage;
+
+typedef struct keyspan_usa67_globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+} keyspan_usa67_globalStatusMessage;
+
+typedef struct keyspan_usa67_globalDebugMessage
+{
+	u8	port,				// 2
+		a,
+		b,
+		c,
+		d;
+} keyspan_usa67_globalDebugMessage;
+
+// ie: the maximum length of an FX1 endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+// status rationing tuning value (each port gets checked each n ms)
+#define	STATUS_RATION	10
+
+#endif
+
+
diff --git a/drivers/usb/serial/keyspan_usa90msg.h b/drivers/usb/serial/keyspan_usa90msg.h
new file mode 100644
index 0000000..86708ec
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa90msg.h
@@ -0,0 +1,198 @@
+/*
+	usa90msg.h
+
+	Copyright (c) 1998-2003 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Message Formats for the USA19HS
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2003 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.    
+
+	Revisions:
+
+	2003feb14		add setTxMode/txMode  and cancelRxXoff to portControl
+	2003mar21		change name of PARITY_0/1 to add MARK/SPACE
+*/
+
+#ifndef	__USA90MSG__
+#define	__USA90MSG__
+
+struct keyspan_usa90_portControlMessage
+{
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the device):
+	*/
+
+	u8	setClocking,	// host requests baud rate be set
+		baudLo,			// host does baud divisor calculation
+		baudHi,			// host does baud divisor calculation 
+		
+		setLcr,			// host requests lcr be set
+		lcr,			// use PARITY, STOPBITS, DATABITS below
+		
+		setRxMode,		// set receive mode
+		rxMode,			// RXMODE_DMA or RXMODE_BYHAND
+
+		setTxMode,		// set transmit mode
+		txMode,			// TXMODE_DMA or TXMODE_BYHAND
+
+		setTxFlowControl,	// host requests tx flow control be set
+		txFlowControl	,	// use TX_FLOW... bits below
+		setRxFlowControl,	// host requests rx flow control be set
+		rxFlowControl,	// use RX_FLOW... bits below
+		sendXoff,		// host requests XOFF transmitted immediately
+		sendXon,		// host requests XON char transmitted
+		xonChar,		// specified in current character format
+		xoffChar,		// specified in current character format
+
+		sendChar,		// host requests char transmitted immediately
+		txChar,			// character to send
+
+		setRts,			// host requests RTS output be set
+		rts,			// 1=on, 0=off
+		setDtr, 		// host requests DTR output be set
+		dtr;			// 1=on, 0=off
+
+	
+	/*
+		2.	configuration data which is simply used as is 
+			and must be specified correctly in every host message.
+	*/
+
+	u8	rxForwardingLength,  // forward when this number of chars available
+		rxForwardingTimeout, // (1-31 in ms)
+		txAckSetting;	   // 0=don't ack, 1=normal, 2-255 TBD...
+	/*
+		3.	Firmware states which cause actions if they change					
+		and must be specified correctly in every host message.
+	*/
+
+	u8	portEnabled,	// 0=disabled, 1=enabled
+		txFlush,		// 0=normal, 1=toss outbound data
+		txBreak,		// 0=break off, 1=break on
+		loopbackMode;	// 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if rxFlush and rxForward flags are set, the
+			port will have no data to forward); any non-zero value 
+			is respected
+	*/
+
+	u8	rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW (as if fwdLen==1)
+		cancelRxXoff,	// cancel any receive XOFF state (_txXoff)
+		returnStatus;	// return current status NOW
+};
+
+// defines for bits in lcr
+#define		USA_DATABITS_5		0x00
+#define		USA_DATABITS_6		0x01
+#define		USA_DATABITS_7		0x02
+#define		USA_DATABITS_8		0x03
+#define		STOPBITS_5678_1		0x00	// 1 stop bit for all byte sizes
+#define		STOPBITS_5_1p5		0x04	// 1.5 stop bits for 5-bit byte
+#define		STOPBITS_678_2		0x04	// 2 stop bits for 6-8 bit byte
+#define		USA_PARITY_NONE		0x00
+#define		USA_PARITY_ODD		0x08
+#define		USA_PARITY_EVEN		0x18
+#define		PARITY_MARK_1  		0x28   	// force parity MARK
+#define		PARITY_SPACE_0 		0x38	// force parity SPACE
+
+#define		TXFLOW_CTS			0x04	
+#define		TXFLOW_DSR			0x08
+#define		TXFLOW_XOFF			0x01	
+#define		TXFLOW_XOFF_ANY		0x02	
+#define		TXFLOW_XOFF_BITS	(TXFLOW_XOFF | TXFLOW_XOFF_ANY)
+
+#define		RXFLOW_XOFF			0x10	
+#define		RXFLOW_RTS			0x20	
+#define		RXFLOW_DTR			0x40
+#define		RXFLOW_DSR_SENSITIVITY	0x80
+
+#define		RXMODE_BYHAND		0x00	
+#define		RXMODE_DMA			0x02	
+
+#define		TXMODE_BYHAND		0x00	
+#define		TXMODE_DMA			0x02	
+
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+struct keyspan_usa90_portStatusMessage	
+{
+	u8	msr,			// reports the actual MSR register
+		cts,			// reports CTS pin
+		dcd,			// reports DCD pin
+		dsr,			// reports DSR pin
+		ri,				// reports RI pin
+		_txXoff,		// port is in XOFF state (we received XOFF)
+		rxBreak,		// reports break state
+		rxOverrun,		// count of overrun errors (since last reported)
+		rxParity,		// count of parity errors (since last reported)
+		rxFrame,		// count of frame errors (since last reported)
+		portState,		// PORTSTATE_xxx bits (useful for debugging)
+		messageAck,		// message acknowledgement
+		charAck,		// character acknowledgement
+		controlResponse;	// (value = returnStatus) a control message has been processed 
+};
+
+// bits in RX data message when STAT byte is included
+
+#define	RXERROR_OVERRUN		0x02
+#define	RXERROR_PARITY		0x04
+#define	RXERROR_FRAMING		0x08
+#define	RXERROR_BREAK		0x10
+
+#define	PORTSTATE_ENABLED	0x80
+#define	PORTSTATE_TXFLUSH	0x01
+#define	PORTSTATE_TXBREAK	0x02
+#define	PORTSTATE_LOOPBACK 	0x04
+
+// MSR bits
+
+#define USA_MSR_dCTS	  		0x01		// CTS has changed since last report	
+#define USA_MSR_dDSR	  		0x02
+#define USA_MSR_dRI			0x04
+#define USA_MSR_dDCD	  		0x08
+
+#define USA_MSR_CTS			0x10	  	// current state of CTS
+#define USA_MSR_DSR			0x20
+#define USA_USA_MSR_RI			0x40
+#define MSR_DCD				0x80
+
+// ie: the maximum length of an endpoint buffer
+#define		MAX_DATA_LEN			64
+
+#endif
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
new file mode 100644
index 0000000..5ee48b0
--- /dev/null
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * KLSI KL5KUSB105 chip RS232 converter driver
+ *
+ *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
+ *
+ * All information about the device was acquired using SniffUSB ans snoopUSB
+ * on Windows98.
+ * It was written out of frustration with the PalmConnect USB Serial adapter
+ * sold by Palm Inc.
+ * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
+ * information that was not already available.
+ *
+ * It seems that KLSI bought some silicon-design information from ScanLogic,
+ * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
+ * KLSI has firmware available for their devices; it is probable that the
+ * firmware differs from that used by KLSI in their products. If you have an
+ * original KLSI device and can provide some information on it, I would be
+ * most interested in adding support for it here. If you have any information
+ * on the protocol used (or find errors in my reverse-engineered stuff), please
+ * let me know.
+ *
+ * The code was only tested with a PalmConnect USB adapter; if you
+ * are adventurous, try it with any KLSI-based device and let me know how it
+ * breaks so that I can fix it!
+ */
+
+/* TODO:
+ *	check modem line signals
+ *	implement handshaking or decide that we do not support it
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "kl5kusb105.h"
+
+#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
+
+
+/*
+ * Function prototypes
+ */
+static int klsi_105_port_probe(struct usb_serial_port *port);
+static int klsi_105_port_remove(struct usb_serial_port *port);
+static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void klsi_105_close(struct usb_serial_port *port);
+static void klsi_105_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static int  klsi_105_tiocmget(struct tty_struct *tty);
+static void klsi_105_process_read_urb(struct urb *urb);
+static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size);
+
+/*
+ * All of the device info needed for the KLSI converters.
+ */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+	{ }		/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver kl5kusb105d_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"kl5kusb105d",
+	},
+	.description =		"KL5KUSB105D / PalmConnect",
+	.id_table =		id_table,
+	.num_ports =		1,
+	.bulk_out_size =	64,
+	.open =			klsi_105_open,
+	.close =		klsi_105_close,
+	.set_termios =		klsi_105_set_termios,
+	.tiocmget =		klsi_105_tiocmget,
+	.port_probe =		klsi_105_port_probe,
+	.port_remove =		klsi_105_port_remove,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.process_read_urb =	klsi_105_process_read_urb,
+	.prepare_write_buffer =	klsi_105_prepare_write_buffer,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&kl5kusb105d_device, NULL
+};
+
+struct klsi_105_port_settings {
+	u8	pktlen;		/* always 5, it seems */
+	u8	baudrate;
+	u8	databits;
+	u8	unknown1;
+	u8	unknown2;
+};
+
+struct klsi_105_private {
+	struct klsi_105_port_settings	cfg;
+	unsigned long			line_state; /* modem line settings */
+	spinlock_t			lock;
+};
+
+
+/*
+ * Handle vendor specific USB requests
+ */
+
+
+#define KLSI_TIMEOUT	 5000 /* default urb timeout */
+
+static int klsi_105_chg_port_settings(struct usb_serial_port *port,
+				      struct klsi_105_port_settings *settings)
+{
+	int rc;
+
+	rc = usb_control_msg(port->serial->dev,
+			usb_sndctrlpipe(port->serial->dev, 0),
+			KL5KUSB105A_SIO_SET_DATA,
+			USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
+			0, /* value */
+			0, /* index */
+			settings,
+			sizeof(struct klsi_105_port_settings),
+			KLSI_TIMEOUT);
+	if (rc < 0)
+		dev_err(&port->dev,
+			"Change port settings failed (error = %d)\n", rc);
+
+	dev_dbg(&port->dev,
+		"pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
+		settings->pktlen, settings->baudrate, settings->databits,
+		settings->unknown1, settings->unknown2);
+
+	return rc;
+}
+
+/* translate a 16-bit status value from the device to linux's TIO bits */
+static unsigned long klsi_105_status2linestate(const __u16 status)
+{
+	unsigned long res = 0;
+
+	res =   ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
+	      | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
+	      ;
+
+	return res;
+}
+
+/*
+ * Read line control via vendor command and return result through
+ * *line_state_p
+ */
+/* It seems that the status buffer has always only 2 bytes length */
+#define KLSI_STATUSBUF_LEN	2
+static int klsi_105_get_line_state(struct usb_serial_port *port,
+				   unsigned long *line_state_p)
+{
+	int rc;
+	u8 *status_buf;
+	__u16 status;
+
+	status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL);
+	if (!status_buf)
+		return -ENOMEM;
+
+	status_buf[0] = 0xff;
+	status_buf[1] = 0xff;
+	rc = usb_control_msg(port->serial->dev,
+			     usb_rcvctrlpipe(port->serial->dev, 0),
+			     KL5KUSB105A_SIO_POLL,
+			     USB_TYPE_VENDOR | USB_DIR_IN,
+			     0, /* value */
+			     0, /* index */
+			     status_buf, KLSI_STATUSBUF_LEN,
+			     10000
+			     );
+	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_dbg(&port->dev, "read status %02x %02x\n",
+			status_buf[0], status_buf[1]);
+
+		*line_state_p = klsi_105_status2linestate(status);
+	}
+
+	kfree(status_buf);
+	return rc;
+}
+
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int klsi_105_port_probe(struct usb_serial_port *port)
+{
+	struct klsi_105_private *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* set initial values for control structures */
+	priv->cfg.pktlen    = 5;
+	priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
+	priv->cfg.databits  = kl5kusb105a_dtb_8;
+	priv->cfg.unknown1  = 0;
+	priv->cfg.unknown2  = 1;
+
+	priv->line_state    = 0;
+
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int klsi_105_port_remove(struct usb_serial_port *port)
+{
+	struct klsi_105_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct klsi_105_private *priv = usb_get_serial_port_data(port);
+	int retval = 0;
+	int rc;
+	unsigned long line_state;
+	struct klsi_105_port_settings *cfg;
+	unsigned long flags;
+
+	/* Do a defined restart:
+	 * Set up sane default baud rate and send the 'READ_ON'
+	 * vendor command.
+	 * FIXME: set modem line control (how?)
+	 * Then read the modem line control and store values in
+	 * priv->line_state.
+	 */
+	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->pktlen   = 5;
+	cfg->baudrate = kl5kusb105a_sio_b9600;
+	cfg->databits = kl5kusb105a_dtb_8;
+	cfg->unknown1 = 0;
+	cfg->unknown2 = 1;
+	klsi_105_chg_port_settings(port, cfg);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->cfg.pktlen   = cfg->pktlen;
+	priv->cfg.baudrate = cfg->baudrate;
+	priv->cfg.databits = cfg->databits;
+	priv->cfg.unknown1 = cfg->unknown1;
+	priv->cfg.unknown2 = cfg->unknown2;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* READ_ON and urb submission */
+	rc = usb_serial_generic_open(tty, port);
+	if (rc) {
+		retval = rc;
+		goto err_free_cfg;
+	}
+
+	rc = usb_control_msg(port->serial->dev,
+			     usb_sndctrlpipe(port->serial->dev, 0),
+			     KL5KUSB105A_SIO_CONFIGURE,
+			     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
+			     KL5KUSB105A_SIO_CONFIGURE_READ_ON,
+			     0, /* index */
+			     NULL,
+			     0,
+			     KLSI_TIMEOUT);
+	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) {
+		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;
+
+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;
+}
+
+static void klsi_105_close(struct usb_serial_port *port)
+{
+	int rc;
+
+	/* send READ_OFF */
+	rc = 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);
+	if (rc < 0)
+		dev_err(&port->dev, "failed to disable read: %d\n", rc);
+
+	/* shutdown our bulk reads and writes */
+	usb_serial_generic_close(port);
+}
+
+/* We need to write a complete 64-byte data block and encode the
+ * number actually sent in the first double-byte, LSB-order. That
+ * leaves at most 62 bytes of payload.
+ */
+#define KLSI_HDR_LEN		2
+static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	unsigned char *buf = dest;
+	int count;
+
+	count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
+								&port->lock);
+	put_unaligned_le16(count, buf);
+
+	return count + KLSI_HDR_LEN;
+}
+
+/* The data received is preceded by a length double-byte in LSB-first order.
+ */
+static void klsi_105_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned len;
+
+	/* empty urbs seem to happen, we ignore them */
+	if (!urb->actual_length)
+		return;
+
+	if (urb->actual_length <= KLSI_HDR_LEN) {
+		dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
+		return;
+	}
+
+	len = get_unaligned_le16(data);
+	if (len > urb->actual_length - KLSI_HDR_LEN) {
+		dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
+		len = urb->actual_length - KLSI_HDR_LEN;
+	}
+
+	tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
+	tty_flip_buffer_push(&port->port);
+}
+
+static void klsi_105_set_termios(struct tty_struct *tty,
+				 struct usb_serial_port *port,
+				 struct ktermios *old_termios)
+{
+	struct klsi_105_private *priv = usb_get_serial_port_data(port);
+	struct device *dev = &port->dev;
+	unsigned int iflag = tty->termios.c_iflag;
+	unsigned int old_iflag = old_termios->c_iflag;
+	unsigned int cflag = tty->termios.c_cflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	struct klsi_105_port_settings *cfg;
+	unsigned long flags;
+	speed_t baud;
+
+	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return;
+
+	/* lock while we are modifying the settings */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/*
+	 * Update baud rate
+	 */
+	baud = tty_get_baud_rate(tty);
+
+	switch (baud) {
+	case 0: /* handled below */
+		break;
+	case 1200:
+		priv->cfg.baudrate = kl5kusb105a_sio_b1200;
+		break;
+	case 2400:
+		priv->cfg.baudrate = kl5kusb105a_sio_b2400;
+		break;
+	case 4800:
+		priv->cfg.baudrate = kl5kusb105a_sio_b4800;
+		break;
+	case 9600:
+		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+		break;
+	case 19200:
+		priv->cfg.baudrate = kl5kusb105a_sio_b19200;
+		break;
+	case 38400:
+		priv->cfg.baudrate = kl5kusb105a_sio_b38400;
+		break;
+	case 57600:
+		priv->cfg.baudrate = kl5kusb105a_sio_b57600;
+		break;
+	case 115200:
+		priv->cfg.baudrate = kl5kusb105a_sio_b115200;
+		break;
+	default:
+		dev_dbg(dev, "unsupported baudrate, using 9600\n");
+		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+		baud = 9600;
+		break;
+	}
+
+	/*
+	 * FIXME: implement B0 handling
+	 *
+	 * Maybe this should be simulated by sending read disable and read
+	 * enable messages?
+	 */
+
+	tty_encode_baud_rate(tty, baud, baud);
+
+	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+		/* set the number of data bits */
+		switch (cflag & CSIZE) {
+		case CS5:
+			dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
+			spin_unlock_irqrestore(&priv->lock, flags);
+			goto err;
+		case CS6:
+			dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
+			spin_unlock_irqrestore(&priv->lock, flags);
+			goto err;
+		case CS7:
+			priv->cfg.databits = kl5kusb105a_dtb_7;
+			break;
+		case CS8:
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		default:
+			dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		}
+	}
+
+	/*
+	 * Update line control register (LCR)
+	 */
+	if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
+	    || (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+		/* Not currently supported */
+		tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
+	}
+	/*
+	 * Set flow control: well, I do not really now how to handle DTR/RTS.
+	 * Just do what we have seen with SniffUSB on Win98.
+	 */
+	if ((iflag & IXOFF) != (old_iflag & IXOFF)
+	    || (iflag & IXON) != (old_iflag & IXON)
+	    ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+		/* Not currently supported */
+		tty->termios.c_cflag &= ~CRTSCTS;
+	}
+	memcpy(cfg, &priv->cfg, sizeof(*cfg));
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* now commit changes to device */
+	klsi_105_chg_port_settings(port, cfg);
+err:
+	kfree(cfg);
+}
+
+static int klsi_105_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct klsi_105_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int rc;
+	unsigned long line_state;
+
+	rc = klsi_105_get_line_state(port, &line_state);
+	if (rc < 0) {
+		dev_err(&port->dev,
+			"Reading line control failed (error = %d)\n", rc);
+		/* better return value? EAGAIN? */
+		return rc;
+	}
+
+	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 (int)line_state;
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/kl5kusb105.h b/drivers/usb/serial/kl5kusb105.h
new file mode 100644
index 0000000..dbe98d8
--- /dev/null
+++ b/drivers/usb/serial/kl5kusb105.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Definitions for the KLSI KL5KUSB105 serial port adapter
+ */
+
+/* vendor/product pairs that are known to contain this chipset */
+#define PALMCONNECT_VID		0x0830
+#define PALMCONNECT_PID		0x0080
+
+/* Vendor commands: */
+
+
+/* port table -- the chip supports up to 4 channels */
+
+/* baud rates */
+
+enum {
+	kl5kusb105a_sio_b115200 = 0,
+	kl5kusb105a_sio_b57600  = 1,
+	kl5kusb105a_sio_b38400  = 2,
+	kl5kusb105a_sio_b19200  = 4,
+	kl5kusb105a_sio_b14400  = 5,
+	kl5kusb105a_sio_b9600   = 6,
+	kl5kusb105a_sio_b4800   = 8,	/* unchecked */
+	kl5kusb105a_sio_b2400   = 9,	/* unchecked */
+	kl5kusb105a_sio_b1200   = 0xa,	/* unchecked */
+	kl5kusb105a_sio_b600    = 0xb	/* unchecked */
+};
+
+/* data bits */
+#define kl5kusb105a_dtb_7   7
+#define kl5kusb105a_dtb_8   8
+
+
+
+/* requests: */
+#define KL5KUSB105A_SIO_SET_DATA  1
+#define KL5KUSB105A_SIO_POLL      2
+#define KL5KUSB105A_SIO_CONFIGURE      3
+/* values used for request KL5KUSB105A_SIO_CONFIGURE */
+#define KL5KUSB105A_SIO_CONFIGURE_READ_ON      3
+#define KL5KUSB105A_SIO_CONFIGURE_READ_OFF     2
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define KL5KUSB105A_DSR			((1<<4) | (1<<5))
+#define KL5KUSB105A_CTS			((1<<5) | (1<<4))
+
+#define KL5KUSB105A_WANTS_TO_SEND	0x30
+#if 0
+#define KL5KUSB105A_DTR			/* Data Terminal Ready */
+#define KL5KUSB105A_CTS			/* Clear To Send */
+#define KL5KUSB105A_CD			/* Carrier Detect */
+#define KL5KUSB105A_DSR			/* Data Set Ready */
+#define KL5KUSB105A_RxD			/* Receive pin */
+
+#define KL5KUSB105A_LE
+#define KL5KUSB105A_RTS
+#define KL5KUSB105A_ST
+#define KL5KUSB105A_SR
+#define KL5KUSB105A_RI			/* Ring Indicator */
+#endif
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
new file mode 100644
index 0000000..e9882ba
--- /dev/null
+++ b/drivers/usb/serial/kobil_sct.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  KOBIL USB Smart Card Terminal Driver
+ *
+ *  Copyright (C) 2002  KOBIL Systems GmbH
+ *  Author: Thomas Wahrenbruch
+ *
+ *  Contact: linuxusb@kobil.de
+ *
+ *  This program is largely derived from work by the linux-usb group
+ *  and associated source files.  Please see the usb/serial files for
+ *  individual credits and copyrights.
+ *
+ *  Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
+ *  patience.
+ *
+ * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
+ * (Adapter K), B1 Professional and KAAN Professional (Adapter B)
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/ioctl.h>
+#include "kobil_sct.h"
+
+#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
+#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
+
+#define KOBIL_VENDOR_ID			0x0D46
+#define KOBIL_ADAPTER_B_PRODUCT_ID	0x2011
+#define KOBIL_ADAPTER_K_PRODUCT_ID	0x2012
+#define KOBIL_USBTWIN_PRODUCT_ID	0x0078
+#define KOBIL_KAAN_SIM_PRODUCT_ID       0x0081
+
+#define KOBIL_TIMEOUT		500
+#define KOBIL_BUF_LENGTH	300
+
+
+/* Function prototypes */
+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);
+static void kobil_close(struct usb_serial_port *port);
+static int  kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
+			 const unsigned char *buf, int count);
+static int  kobil_write_room(struct tty_struct *tty);
+static int  kobil_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+static int  kobil_tiocmget(struct tty_struct *tty);
+static int  kobil_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear);
+static void kobil_read_int_callback(struct urb *urb);
+static void kobil_write_int_callback(struct urb *urb);
+static void kobil_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static void kobil_init_termios(struct tty_struct *tty);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver kobil_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"kobil",
+	},
+	.description =		"KOBIL USB smart card terminal",
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_interrupt_out =	1,
+	.port_probe =		kobil_port_probe,
+	.port_remove =		kobil_port_remove,
+	.ioctl =		kobil_ioctl,
+	.set_termios =		kobil_set_termios,
+	.init_termios =		kobil_init_termios,
+	.tiocmget =		kobil_tiocmget,
+	.tiocmset =		kobil_tiocmset,
+	.open =			kobil_open,
+	.close =		kobil_close,
+	.write =		kobil_write,
+	.write_room =		kobil_write_room,
+	.read_int_callback =	kobil_read_int_callback,
+	.write_int_callback =	kobil_write_int_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&kobil_device, NULL
+};
+
+struct kobil_private {
+	unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */
+	int filled;  /* index of the last char in buf */
+	int cur_pos; /* index of the next char to send in buf */
+	__u16 device_type;
+};
+
+
+static int kobil_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct kobil_private *priv;
+
+	priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->filled = 0;
+	priv->cur_pos = 0;
+	priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct);
+
+	switch (priv->device_type) {
+	case KOBIL_ADAPTER_B_PRODUCT_ID:
+		dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n");
+		break;
+	case KOBIL_ADAPTER_K_PRODUCT_ID:
+		dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
+		break;
+	case KOBIL_USBTWIN_PRODUCT_ID:
+		dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n");
+		break;
+	case KOBIL_KAAN_SIM_PRODUCT_ID:
+		dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n");
+		break;
+	}
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+
+static int kobil_port_remove(struct usb_serial_port *port)
+{
+	struct kobil_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static void kobil_init_termios(struct tty_struct *tty)
+{
+	/* Default to echo off and other sane device settings */
+	tty->termios.c_lflag = 0;
+	tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
+	tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF;
+	/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
+	tty->termios.c_oflag &= ~ONLCR;
+}
+
+static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct device *dev = &port->dev;
+	int result = 0;
+	struct kobil_private *priv;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+
+	priv = usb_get_serial_port_data(port);
+
+	/* allocate memory for transfer buffer */
+	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	/* get hardware version */
+	result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_GetMisc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+			  SUSBCR_MSC_GetHWVersion,
+			  0,
+			  transfer_buffer,
+			  transfer_buffer_length,
+			  KOBIL_TIMEOUT
+	);
+	dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
+	if (result >= 3) {
+		dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
+				transfer_buffer[1], transfer_buffer[2]);
+	}
+
+	/* get firmware version */
+	result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_GetMisc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+			  SUSBCR_MSC_GetFWVersion,
+			  0,
+			  transfer_buffer,
+			  transfer_buffer_length,
+			  KOBIL_TIMEOUT
+	);
+	dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
+	if (result >= 3) {
+		dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
+				transfer_buffer[1], transfer_buffer[2]);
+	}
+
+	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
+			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
+		/* Setting Baudrate, Parity and Stopbits */
+		result = usb_control_msg(port->serial->dev,
+			  usb_sndctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_SetBaudRateParityAndStopBits,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
+							SUSBCR_SPASB_1StopBit,
+			  0,
+			  NULL,
+			  0,
+			  KOBIL_TIMEOUT
+		);
+		dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
+
+		/* reset all queues */
+		result = usb_control_msg(port->serial->dev,
+			  usb_sndctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_Misc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  SUSBCR_MSC_ResetAllQueues,
+			  0,
+			  NULL,
+			  0,
+			  KOBIL_TIMEOUT
+		);
+		dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
+	}
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
+	    priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
+	    priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+		/* start reading (Adapter B 'cause PNP string) */
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
+	}
+
+	kfree(transfer_buffer);
+	return 0;
+}
+
+
+static void kobil_close(struct usb_serial_port *port)
+{
+	/* FIXME: Add rts/dtr methods */
+	usb_kill_urb(port->interrupt_out_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+
+static void kobil_read_int_callback(struct urb *urb)
+{
+	int result;
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
+		return;
+	}
+
+	if (urb->actual_length) {
+		usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
+									data);
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
+}
+
+
+static void kobil_write_int_callback(struct urb *urb)
+{
+}
+
+
+static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	int length = 0;
+	int result = 0;
+	int todo = 0;
+	struct kobil_private *priv;
+
+	if (count == 0) {
+		dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
+		return 0;
+	}
+
+	priv = usb_get_serial_port_data(port);
+
+	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
+		dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Copy data to buffer */
+	memcpy(priv->buf + priv->filled, buf, count);
+	usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
+	priv->filled = priv->filled + count;
+
+	/* only send complete block. TWIN, KAAN SIM and adapter K
+	   use the same protocol. */
+	if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
+	     ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
+		/* stop reading (except TWIN and KAAN SIM) */
+		if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
+			|| (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
+			usb_kill_urb(port->interrupt_in_urb);
+
+		todo = priv->filled - priv->cur_pos;
+
+		while (todo > 0) {
+			/* max 8 byte in one urb (endpoint size) */
+			length = min(todo, port->interrupt_out_size);
+			/* copy data to transfer buffer */
+			memcpy(port->interrupt_out_buffer,
+					priv->buf + priv->cur_pos, length);
+			port->interrupt_out_urb->transfer_buffer_length = length;
+
+			priv->cur_pos = priv->cur_pos + length;
+			result = usb_submit_urb(port->interrupt_out_urb,
+					GFP_ATOMIC);
+			dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
+			todo = priv->filled - priv->cur_pos;
+
+			if (todo > 0)
+				msleep(24);
+		}
+
+		priv->filled = 0;
+		priv->cur_pos = 0;
+
+		/* start reading (except TWIN and KAAN SIM) */
+		if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
+			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
+			result = usb_submit_urb(port->interrupt_in_urb,
+					GFP_ATOMIC);
+			dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
+		}
+	}
+	return count;
+}
+
+
+static int kobil_write_room(struct tty_struct *tty)
+{
+	/* FIXME */
+	return 8;
+}
+
+
+static int kobil_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct kobil_private *priv;
+	int result;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+
+	priv = usb_get_serial_port_data(port);
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
+			|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+		/* This device doesn't support ioctl calls */
+		return -EINVAL;
+	}
+
+	/* allocate memory for transfer buffer */
+	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_GetStatusLineState,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+			  0,
+			  0,
+			  transfer_buffer,
+			  transfer_buffer_length,
+			  KOBIL_TIMEOUT);
+
+	dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n",
+			result);
+	if (result < 1) {
+		if (result >= 0)
+			result = -EIO;
+		goto out_free;
+	}
+
+	dev_dbg(&port->dev, "Statusline: %02x\n", transfer_buffer[0]);
+
+	result = 0;
+	if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
+		result = TIOCM_DSR;
+out_free:
+	kfree(transfer_buffer);
+	return result;
+}
+
+static int kobil_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct device *dev = &port->dev;
+	struct kobil_private *priv;
+	int result;
+	int dtr = 0;
+	int rts = 0;
+
+	/* FIXME: locking ? */
+	priv = usb_get_serial_port_data(port);
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
+		|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+		/* This device doesn't support ioctl calls */
+		return -EINVAL;
+	}
+
+	if (set & TIOCM_RTS)
+		rts = 1;
+	if (set & TIOCM_DTR)
+		dtr = 1;
+	if (clear & TIOCM_RTS)
+		rts = 0;
+	if (clear & TIOCM_DTR)
+		dtr = 0;
+
+	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
+		if (dtr != 0)
+			dev_dbg(dev, "%s - Setting DTR\n", __func__);
+		else
+			dev_dbg(dev, "%s - Clearing DTR\n", __func__);
+		result = usb_control_msg(port->serial->dev,
+			  usb_sndctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_SetStatusLinesOrQueues,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
+			  0,
+			  NULL,
+			  0,
+			  KOBIL_TIMEOUT);
+	} else {
+		if (rts != 0)
+			dev_dbg(dev, "%s - Setting RTS\n", __func__);
+		else
+			dev_dbg(dev, "%s - Clearing RTS\n", __func__);
+		result = usb_control_msg(port->serial->dev,
+			usb_sndctrlpipe(port->serial->dev, 0),
+			SUSBCRequest_SetStatusLinesOrQueues,
+			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
+			0,
+			NULL,
+			0,
+			KOBIL_TIMEOUT);
+	}
+	dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
+	return (result < 0) ? result : 0;
+}
+
+static void kobil_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old)
+{
+	struct kobil_private *priv;
+	int result;
+	unsigned short urb_val = 0;
+	int c_cflag = tty->termios.c_cflag;
+	speed_t speed;
+
+	priv = usb_get_serial_port_data(port);
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
+			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+		/* This device doesn't support ioctl calls */
+		tty_termios_copy_hw(&tty->termios, old);
+		return;
+	}
+
+	speed = tty_get_baud_rate(tty);
+	switch (speed) {
+	case 1200:
+		urb_val = SUSBCR_SBR_1200;
+		break;
+	default:
+		speed = 9600;
+		/* fall through */
+	case 9600:
+		urb_val = SUSBCR_SBR_9600;
+		break;
+	}
+	urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
+							SUSBCR_SPASB_1StopBit;
+	if (c_cflag & PARENB) {
+		if  (c_cflag & PARODD)
+			urb_val |= SUSBCR_SPASB_OddParity;
+		else
+			urb_val |= SUSBCR_SPASB_EvenParity;
+	} else
+		urb_val |= SUSBCR_SPASB_NoParity;
+	tty->termios.c_cflag &= ~CMSPAR;
+	tty_encode_baud_rate(tty, speed, speed);
+
+	result = usb_control_msg(port->serial->dev,
+		  usb_sndctrlpipe(port->serial->dev, 0),
+		  SUSBCRequest_SetBaudRateParityAndStopBits,
+		  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+		  urb_val,
+		  0,
+		  NULL,
+		  0,
+		  KOBIL_TIMEOUT
+		);
+}
+
+static int kobil_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct kobil_private *priv = usb_get_serial_port_data(port);
+	int result;
+
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
+			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
+		/* This device doesn't support ioctl calls */
+		return -ENOIOCTLCMD;
+
+	switch (cmd) {
+	case TCFLSH:
+		result = usb_control_msg(port->serial->dev,
+			  usb_sndctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_Misc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  SUSBCR_MSC_ResetAllQueues,
+			  0,
+			  NULL,
+			  0,
+			  KOBIL_TIMEOUT
+			);
+
+		dev_dbg(&port->dev,
+			"%s - Send reset_all_queues (FLUSH) URB returns: %i\n",
+			__func__, result);
+		return (result < 0) ? -EIO: 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/kobil_sct.h b/drivers/usb/serial/kobil_sct.h
new file mode 100644
index 0000000..030c1b4
--- /dev/null
+++ b/drivers/usb/serial/kobil_sct.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define SUSBCRequest_SetBaudRateParityAndStopBits       1
+#define SUSBCR_SBR_MASK				0xFF00
+#define SUSBCR_SBR_1200				0x0100
+#define SUSBCR_SBR_9600				0x0200
+#define SUSBCR_SBR_19200			0x0400
+#define SUSBCR_SBR_28800			0x0800
+#define SUSBCR_SBR_38400			0x1000
+#define SUSBCR_SBR_57600			0x2000
+#define SUSBCR_SBR_115200			0x4000
+
+#define SUSBCR_SPASB_MASK			0x0070
+#define SUSBCR_SPASB_NoParity			0x0010
+#define SUSBCR_SPASB_OddParity			0x0020
+#define SUSBCR_SPASB_EvenParity			0x0040
+
+#define SUSBCR_SPASB_STPMASK			0x0003
+#define SUSBCR_SPASB_1StopBit			0x0001
+#define SUSBCR_SPASB_2StopBits			0x0002
+
+#define SUSBCRequest_SetStatusLinesOrQueues	2
+#define SUSBCR_SSL_SETRTS			0x0001
+#define SUSBCR_SSL_CLRRTS			0x0002
+#define SUSBCR_SSL_SETDTR			0x0004
+#define SUSBCR_SSL_CLRDTR			0x0010
+
+/* Kill the pending/current writes to the comm port. */
+#define SUSBCR_SSL_PURGE_TXABORT		0x0100
+/* Kill the pending/current reads to the comm port. */
+#define SUSBCR_SSL_PURGE_RXABORT		0x0200
+/* Kill the transmit queue if there. */
+#define SUSBCR_SSL_PURGE_TXCLEAR		0x0400
+/* Kill the typeahead buffer if there. */
+#define SUSBCR_SSL_PURGE_RXCLEAR		0x0800
+
+#define SUSBCRequest_GetStatusLineState		4
+/* Any Character received */
+#define SUSBCR_GSL_RXCHAR			0x0001
+/* Transmitt Queue Empty */
+#define SUSBCR_GSL_TXEMPTY			0x0004
+/* CTS changed state */
+#define SUSBCR_GSL_CTS				0x0008
+/* DSR changed state */
+#define SUSBCR_GSL_DSR				0x0010
+/* RLSD changed state */
+#define SUSBCR_GSL_RLSD				0x0020
+/* BREAK received */
+#define SUSBCR_GSL_BREAK			0x0040
+/* Line status error occurred */
+#define SUSBCR_GSL_ERR				0x0080
+/* Ring signal detected */
+#define SUSBCR_GSL_RING				0x0100
+
+#define SUSBCRequest_Misc			8
+/* use a predefined reset sequence */
+#define SUSBCR_MSC_ResetReader			0x0001
+/* use a predefined sequence to reset the internal queues */
+#define SUSBCR_MSC_ResetAllQueues		0x0002
+
+#define SUSBCRequest_GetMisc			0x10
+
+/*
+ * get the firmware version from device, coded like this 0xHHLLBBPP with
+ * HH = Firmware Version High Byte
+ * LL = Firmware Version Low Byte
+ * BB = Build Number
+ * PP = Further Attributes
+ */
+#define SUSBCR_MSC_GetFWVersion			0x0001
+
+/*
+ * get the hardware version from device coded like this 0xHHLLPPRR with
+ * HH = Software Version High Byte
+ * LL = Software Version Low Byte
+ * PP = Further Attributes
+ * RR = Reserved for the hardware ID
+ */
+#define SUSBCR_MSC_GetHWVersion			0x0002
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
new file mode 100644
index 0000000..7887c31
--- /dev/null
+++ b/drivers/usb/serial/mct_u232.c
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
+ *
+ *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
+ *
+ * This program is largely derived from the Belkin USB Serial Adapter Driver
+ * (see belkin_sa.[ch]). All of the information about the device was acquired
+ * by using SniffUSB on Windows98. For technical details see mct_u232.h.
+ *
+ * William G. Greathouse and Greg Kroah-Hartman provided great help on how to
+ * do the reverse engineering and how to write a USB serial device driver.
+ *
+ * TO BE DONE, TO BE CHECKED:
+ *   DTR/RTS signal handling may be incomplete or incorrect. I have mainly
+ *   implemented what I have seen with SniffUSB or found in belkin_sa.c.
+ *   For further TODOs check also belkin_sa.c.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include "mct_u232.h"
+
+#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
+#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
+
+/*
+ * Function prototypes
+ */
+static int  mct_u232_port_probe(struct usb_serial_port *port);
+static int  mct_u232_port_remove(struct usb_serial_port *remove);
+static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void mct_u232_close(struct usb_serial_port *port);
+static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
+static void mct_u232_read_int_callback(struct urb *urb);
+static void mct_u232_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
+static int  mct_u232_tiocmget(struct tty_struct *tty);
+static int  mct_u232_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear);
+static void mct_u232_throttle(struct tty_struct *tty);
+static void mct_u232_unthrottle(struct tty_struct *tty);
+
+
+/*
+ * All of the device info needed for the MCT USB-RS232 converter.
+ */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
+	{ USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
+	{ USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
+	{ USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
+	{ }		/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver mct_u232_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"mct_u232",
+	},
+	.description =	     "MCT U232",
+	.id_table =	     id_table,
+	.num_ports =	     1,
+	.open =		     mct_u232_open,
+	.close =	     mct_u232_close,
+	.dtr_rts =	     mct_u232_dtr_rts,
+	.throttle =	     mct_u232_throttle,
+	.unthrottle =	     mct_u232_unthrottle,
+	.read_int_callback = mct_u232_read_int_callback,
+	.set_termios =	     mct_u232_set_termios,
+	.break_ctl =	     mct_u232_break_ctl,
+	.tiocmget =	     mct_u232_tiocmget,
+	.tiocmset =	     mct_u232_tiocmset,
+	.tiocmiwait =        usb_serial_generic_tiocmiwait,
+	.port_probe =        mct_u232_port_probe,
+	.port_remove =       mct_u232_port_remove,
+	.get_icount =        usb_serial_generic_get_icount,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&mct_u232_device, NULL
+};
+
+struct mct_u232_private {
+	struct urb *read_urb;
+	spinlock_t lock;
+	unsigned int	     control_state; /* Modem Line Setting (TIOCM) */
+	unsigned char        last_lcr;      /* Line Control Register */
+	unsigned char	     last_lsr;      /* Line Status Register */
+	unsigned char	     last_msr;      /* Modem Status Register */
+	unsigned int	     rx_flags;      /* Throttling flags */
+};
+
+#define THROTTLED		0x01
+
+/*
+ * Handle vendor specific USB requests
+ */
+
+#define WDR_TIMEOUT 5000 /* default urb timeout */
+
+/*
+ * Later day 2.6.0-test kernels have new baud rates like B230400 which
+ * we do not know how to support. We ignore them for the moment.
+ */
+static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
+					speed_t value, speed_t *result)
+{
+	*result = value;
+
+	if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
+		|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
+		switch (value) {
+		case 300:
+			return 0x01;
+		case 600:
+			return 0x02; /* this one not tested */
+		case 1200:
+			return 0x03;
+		case 2400:
+			return 0x04;
+		case 4800:
+			return 0x06;
+		case 9600:
+			return 0x08;
+		case 19200:
+			return 0x09;
+		case 38400:
+			return 0x0a;
+		case 57600:
+			return 0x0b;
+		case 115200:
+			return 0x0c;
+		default:
+			*result = 9600;
+			return 0x08;
+		}
+	} else {
+		/* FIXME: Can we use any divider - should we do
+		   divider = 115200/value;
+		   real baud = 115200/divider */
+		switch (value) {
+		case 300: break;
+		case 600: break;
+		case 1200: break;
+		case 2400: break;
+		case 4800: break;
+		case 9600: break;
+		case 19200: break;
+		case 38400: break;
+		case 57600: break;
+		case 115200: break;
+		default:
+			value = 9600;
+			*result = 9600;
+		}
+		return 115200/value;
+	}
+}
+
+static int mct_u232_set_baud_rate(struct tty_struct *tty,
+	struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
+{
+	unsigned int divisor;
+	int rc;
+	unsigned char *buf;
+	unsigned char cts_enable_byte = 0;
+	speed_t speed;
+
+	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
+	put_unaligned_le32(divisor, buf);
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				MCT_U232_SET_BAUD_RATE_REQUEST,
+				MCT_U232_SET_REQUEST_TYPE,
+				0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
+				WDR_TIMEOUT);
+	if (rc < 0)	/*FIXME: What value speed results */
+		dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
+			value, rc);
+	else
+		tty_encode_baud_rate(tty, speed, speed);
+	dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
+
+	/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
+	   always sends two extra USB 'device request' messages after the
+	   'baud rate change' message.  The actual functionality of the
+	   request codes in these messages is not fully understood but these
+	   particular codes are never seen in any operation besides a baud
+	   rate change.  Both of these messages send a single byte of data.
+	   In the first message, the value of this byte is always zero.
+
+	   The second message has been determined experimentally to control
+	   whether data will be transmitted to a device which is not asserting
+	   the 'CTS' signal.  If the second message's data byte is zero, data
+	   will be transmitted even if 'CTS' is not asserted (i.e. no hardware
+	   flow control).  if the second message's data byte is nonzero (a
+	   value of 1 is used by this driver), data will not be transmitted to
+	   a device which is not asserting 'CTS'.
+	*/
+
+	buf[0] = 0;
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				MCT_U232_SET_UNKNOWN1_REQUEST,
+				MCT_U232_SET_REQUEST_TYPE,
+				0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
+				WDR_TIMEOUT);
+	if (rc < 0)
+		dev_err(&port->dev, "Sending USB device request code %d "
+			"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
+			rc);
+
+	if (port && C_CRTSCTS(tty))
+	   cts_enable_byte = 1;
+
+	dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
+		cts_enable_byte);
+	buf[0] = cts_enable_byte;
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			MCT_U232_SET_CTS_REQUEST,
+			MCT_U232_SET_REQUEST_TYPE,
+			0, 0, buf, MCT_U232_SET_CTS_SIZE,
+			WDR_TIMEOUT);
+	if (rc < 0)
+		dev_err(&port->dev, "Sending USB device request code %d "
+			"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
+
+	kfree(buf);
+	return rc;
+} /* mct_u232_set_baud_rate */
+
+static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
+				  unsigned char lcr)
+{
+	int rc;
+	unsigned char *buf;
+
+	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = lcr;
+	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
+			MCT_U232_SET_LINE_CTRL_REQUEST,
+			MCT_U232_SET_REQUEST_TYPE,
+			0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
+			WDR_TIMEOUT);
+	if (rc < 0)
+		dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
+	dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
+	kfree(buf);
+	return rc;
+} /* mct_u232_set_line_ctrl */
+
+static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
+				   unsigned int control_state)
+{
+	int rc;
+	unsigned char mcr;
+	unsigned char *buf;
+
+	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	mcr = MCT_U232_MCR_NONE;
+	if (control_state & TIOCM_DTR)
+		mcr |= MCT_U232_MCR_DTR;
+	if (control_state & TIOCM_RTS)
+		mcr |= MCT_U232_MCR_RTS;
+
+	buf[0] = mcr;
+	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
+			MCT_U232_SET_MODEM_CTRL_REQUEST,
+			MCT_U232_SET_REQUEST_TYPE,
+			0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
+			WDR_TIMEOUT);
+	kfree(buf);
+
+	dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
+
+	if (rc < 0) {
+		dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
+		return rc;
+	}
+	return 0;
+} /* mct_u232_set_modem_ctrl */
+
+static int mct_u232_get_modem_stat(struct usb_serial_port *port,
+				   unsigned char *msr)
+{
+	int rc;
+	unsigned char *buf;
+
+	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
+	if (buf == NULL) {
+		*msr = 0;
+		return -ENOMEM;
+	}
+	rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
+			MCT_U232_GET_MODEM_STAT_REQUEST,
+			MCT_U232_GET_REQUEST_TYPE,
+			0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
+			WDR_TIMEOUT);
+	if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
+		dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
+
+		if (rc >= 0)
+			rc = -EIO;
+
+		*msr = 0;
+	} else {
+		*msr = buf[0];
+	}
+	dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
+	kfree(buf);
+	return rc;
+} /* mct_u232_get_modem_stat */
+
+static void mct_u232_msr_to_icount(struct async_icount *icount,
+						unsigned char msr)
+{
+	/* Translate Control Line states */
+	if (msr & MCT_U232_MSR_DDSR)
+		icount->dsr++;
+	if (msr & MCT_U232_MSR_DCTS)
+		icount->cts++;
+	if (msr & MCT_U232_MSR_DRI)
+		icount->rng++;
+	if (msr & MCT_U232_MSR_DCD)
+		icount->dcd++;
+} /* mct_u232_msr_to_icount */
+
+static void mct_u232_msr_to_state(struct usb_serial_port *port,
+				  unsigned int *control_state, unsigned char msr)
+{
+	/* Translate Control Line states */
+	if (msr & MCT_U232_MSR_DSR)
+		*control_state |=  TIOCM_DSR;
+	else
+		*control_state &= ~TIOCM_DSR;
+	if (msr & MCT_U232_MSR_CTS)
+		*control_state |=  TIOCM_CTS;
+	else
+		*control_state &= ~TIOCM_CTS;
+	if (msr & MCT_U232_MSR_RI)
+		*control_state |=  TIOCM_RI;
+	else
+		*control_state &= ~TIOCM_RI;
+	if (msr & MCT_U232_MSR_CD)
+		*control_state |=  TIOCM_CD;
+	else
+		*control_state &= ~TIOCM_CD;
+	dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
+} /* mct_u232_msr_to_state */
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int mct_u232_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv;
+
+	/* check first to simplify error handling */
+	if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
+		dev_err(&port->dev, "expected endpoint missing\n");
+		return -ENODEV;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* Use second interrupt-in endpoint for reading. */
+	priv->read_urb = serial->port[1]->interrupt_in_urb;
+	priv->read_urb->context = port;
+
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int mct_u232_port_remove(struct usb_serial_port *port)
+{
+	struct mct_u232_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	int retval = 0;
+	unsigned int control_state;
+	unsigned long flags;
+	unsigned char last_lcr;
+	unsigned char last_msr;
+
+	/* Compensate for a hardware bug: although the Sitecom U232-P25
+	 * device reports a maximum output packet size of 32 bytes,
+	 * it seems to be able to accept only 16 bytes (and that's what
+	 * SniffUSB says too...)
+	 */
+	if (le16_to_cpu(serial->dev->descriptor.idProduct)
+						== MCT_U232_SITECOM_PID)
+		port->bulk_out_size = 16;
+
+	/* Do a defined restart: the normal serial device seems to
+	 * always turn on DTR and RTS here, so do the same. I'm not
+	 * sure if this is really necessary. But it should not harm
+	 * either.
+	 */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (tty && C_BAUD(tty))
+		priv->control_state = TIOCM_DTR | TIOCM_RTS;
+	else
+		priv->control_state = 0;
+
+	priv->last_lcr = (MCT_U232_DATA_BITS_8 |
+			  MCT_U232_PARITY_NONE |
+			  MCT_U232_STOP_BITS_1);
+	control_state = priv->control_state;
+	last_lcr = priv->last_lcr;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	mct_u232_set_modem_ctrl(port, control_state);
+	mct_u232_set_line_ctrl(port, last_lcr);
+
+	/* Read modem status and update control state */
+	mct_u232_get_modem_stat(port, &last_msr);
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->last_msr = last_msr;
+	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&port->dev,
+			"usb_submit_urb(read) failed pipe 0x%x err %d\n",
+			port->read_urb->pipe, retval);
+		goto error;
+	}
+
+	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		usb_kill_urb(priv->read_urb);
+		dev_err(&port->dev,
+			"usb_submit_urb(read int) failed pipe 0x%x err %d",
+			port->interrupt_in_urb->pipe, retval);
+		goto error;
+	}
+	return 0;
+
+error:
+	return retval;
+} /* mct_u232_open */
+
+static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
+{
+	unsigned int control_state;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+
+	spin_lock_irq(&priv->lock);
+	if (on)
+		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+	else
+		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+	control_state = priv->control_state;
+	spin_unlock_irq(&priv->lock);
+
+	mct_u232_set_modem_ctrl(port, control_state);
+}
+
+static void mct_u232_close(struct usb_serial_port *port)
+{
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+
+	usb_kill_urb(priv->read_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	usb_serial_generic_close(port);
+} /* mct_u232_close */
+
+
+static void mct_u232_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int retval;
+	int status = urb->status;
+	unsigned long flags;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	/*
+	 * Work-a-round: handle the 'usual' bulk-in pipe here
+	 */
+	if (urb->transfer_buffer_length > 2) {
+		if (urb->actual_length) {
+			tty_insert_flip_string(&port->port, data,
+					urb->actual_length);
+			tty_flip_buffer_push(&port->port);
+		}
+		goto exit;
+	}
+
+	/*
+	 * The interrupt-in pipe signals exceptional conditions (modem line
+	 * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
+	 */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->last_msr = data[MCT_U232_MSR_INDEX];
+
+	/* Record Control Line states */
+	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
+
+	mct_u232_msr_to_icount(&port->icount, priv->last_msr);
+
+#if 0
+	/* Not yet handled. See belkin_sa.c for further information */
+	/* Now to report any errors */
+	priv->last_lsr = data[MCT_U232_LSR_INDEX];
+	/*
+	 * fill in the flip buffer here, but I do not know the relation
+	 * to the current/next receive buffer or characters.  I need
+	 * to look in to this before committing any code.
+	 */
+	if (priv->last_lsr & MCT_U232_LSR_ERR) {
+		tty = tty_port_tty_get(&port->port);
+		/* Overrun Error */
+		if (priv->last_lsr & MCT_U232_LSR_OE) {
+		}
+		/* Parity Error */
+		if (priv->last_lsr & MCT_U232_LSR_PE) {
+		}
+		/* Framing Error */
+		if (priv->last_lsr & MCT_U232_LSR_FE) {
+		}
+		/* Break Indicator */
+		if (priv->last_lsr & MCT_U232_LSR_BI) {
+		}
+		tty_kref_put(tty);
+	}
+#endif
+	wake_up_interruptible(&port->port.delta_msr_wait);
+	spin_unlock_irqrestore(&priv->lock, flags);
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&port->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+} /* mct_u232_read_int_callback */
+
+static void mct_u232_set_termios(struct tty_struct *tty,
+				 struct usb_serial_port *port,
+				 struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	struct ktermios *termios = &tty->termios;
+	unsigned int cflag = termios->c_cflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	unsigned long flags;
+	unsigned int control_state;
+	unsigned char last_lcr;
+
+	/* get a local copy of the current port settings */
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	last_lcr = 0;
+
+	/*
+	 * Update baud rate.
+	 * Do not attempt to cache old rates and skip settings,
+	 * disconnects screw such tricks up completely.
+	 * Premature optimization is the root of all evil.
+	 */
+
+	/* reassert DTR and RTS on transition from B0 */
+	if ((old_cflag & CBAUD) == B0) {
+		dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
+		control_state |= TIOCM_DTR | TIOCM_RTS;
+		mct_u232_set_modem_ctrl(port, control_state);
+	}
+
+	mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
+
+	if ((cflag & CBAUD) == B0) {
+		dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
+		/* Drop RTS and DTR */
+		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(port, control_state);
+	}
+
+	/*
+	 * Update line control register (LCR)
+	 */
+
+	/* set the parity */
+	if (cflag & PARENB)
+		last_lcr |= (cflag & PARODD) ?
+			MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
+	else
+		last_lcr |= MCT_U232_PARITY_NONE;
+
+	/* set the number of data bits */
+	switch (cflag & CSIZE) {
+	case CS5:
+		last_lcr |= MCT_U232_DATA_BITS_5; break;
+	case CS6:
+		last_lcr |= MCT_U232_DATA_BITS_6; break;
+	case CS7:
+		last_lcr |= MCT_U232_DATA_BITS_7; break;
+	case CS8:
+		last_lcr |= MCT_U232_DATA_BITS_8; break;
+	default:
+		dev_err(&port->dev,
+			"CSIZE was not CS5-CS8, using default of 8\n");
+		last_lcr |= MCT_U232_DATA_BITS_8;
+		break;
+	}
+
+	termios->c_cflag &= ~CMSPAR;
+
+	/* set the number of stop bits */
+	last_lcr |= (cflag & CSTOPB) ?
+		MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
+
+	mct_u232_set_line_ctrl(port, last_lcr);
+
+	/* save off the modified port settings */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->control_state = control_state;
+	priv->last_lcr = last_lcr;
+	spin_unlock_irqrestore(&priv->lock, flags);
+} /* mct_u232_set_termios */
+
+static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned char lcr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	lcr = priv->last_lcr;
+
+	if (break_state)
+		lcr |= MCT_U232_SET_BREAK;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	mct_u232_set_line_ctrl(port, lcr);
+} /* mct_u232_break_ctl */
+
+
+static int mct_u232_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned int control_state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return control_state;
+}
+
+static int mct_u232_tiocmset(struct tty_struct *tty,
+			      unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned int control_state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	control_state = priv->control_state;
+
+	if (set & TIOCM_RTS)
+		control_state |= TIOCM_RTS;
+	if (set & TIOCM_DTR)
+		control_state |= TIOCM_DTR;
+	if (clear & TIOCM_RTS)
+		control_state &= ~TIOCM_RTS;
+	if (clear & TIOCM_DTR)
+		control_state &= ~TIOCM_DTR;
+
+	priv->control_state = control_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return mct_u232_set_modem_ctrl(port, control_state);
+}
+
+static void mct_u232_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned int control_state;
+
+	spin_lock_irq(&priv->lock);
+	priv->rx_flags |= THROTTLED;
+	if (C_CRTSCTS(tty)) {
+		priv->control_state &= ~TIOCM_RTS;
+		control_state = priv->control_state;
+		spin_unlock_irq(&priv->lock);
+		mct_u232_set_modem_ctrl(port, control_state);
+	} else {
+		spin_unlock_irq(&priv->lock);
+	}
+}
+
+static void mct_u232_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned int control_state;
+
+	spin_lock_irq(&priv->lock);
+	if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
+		priv->rx_flags &= ~THROTTLED;
+		priv->control_state |= TIOCM_RTS;
+		control_state = priv->control_state;
+		spin_unlock_irq(&priv->lock);
+		mct_u232_set_modem_ctrl(port, control_state);
+	} else {
+		spin_unlock_irq(&priv->lock);
+	}
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h
new file mode 100644
index 0000000..0084edf
--- /dev/null
+++ b/drivers/usb/serial/mct_u232.h
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver
+ *
+ *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
+ *
+ * This driver is for the device MCT USB-RS232 Converter (25 pin, Model No.
+ * U232-P25) from Magic Control Technology Corp. (there is also a 9 pin
+ * Model No. U232-P9). See http://www.mct.com.tw/products/product_us232.html 
+ * for further information. The properties of this device are listed at the end 
+ * of this file. This device was used in the Dlink DSB-S25.
+ *
+ * All of the information about the device was acquired by using SniffUSB
+ * on Windows98. The technical details of the reverse engineering are
+ * summarized at the end of this file.
+ */
+
+#ifndef __LINUX_USB_SERIAL_MCT_U232_H
+#define __LINUX_USB_SERIAL_MCT_U232_H
+
+#define MCT_U232_VID	                0x0711	/* Vendor Id */
+#define MCT_U232_PID	                0x0210	/* Original MCT Product Id */
+
+/* U232-P25, Sitecom */
+#define MCT_U232_SITECOM_PID		0x0230	/* Sitecom Product Id */
+
+/* DU-H3SP USB BAY hub */
+#define MCT_U232_DU_H3SP_PID		0x0200	/* D-Link DU-H3SP USB BAY */
+
+/* Belkin badge the MCT U232-P9 as the F5U109 */
+#define MCT_U232_BELKIN_F5U109_VID	0x050d	/* Vendor Id */
+#define MCT_U232_BELKIN_F5U109_PID	0x0109	/* Product Id */
+
+/*
+ * Vendor Request Interface
+ */
+#define MCT_U232_SET_REQUEST_TYPE	0x40
+#define MCT_U232_GET_REQUEST_TYPE	0xc0
+
+/* Get Modem Status Register (MSR) */
+#define MCT_U232_GET_MODEM_STAT_REQUEST	2
+#define MCT_U232_GET_MODEM_STAT_SIZE	1
+
+/* Get Line Control Register (LCR) */
+/* ... not used by this driver */
+#define MCT_U232_GET_LINE_CTRL_REQUEST	6
+#define MCT_U232_GET_LINE_CTRL_SIZE	1
+
+/* Set Baud Rate Divisor */
+#define MCT_U232_SET_BAUD_RATE_REQUEST	5
+#define MCT_U232_SET_BAUD_RATE_SIZE	4
+
+/* Set Line Control Register (LCR) */
+#define MCT_U232_SET_LINE_CTRL_REQUEST	7
+#define MCT_U232_SET_LINE_CTRL_SIZE	1
+
+/* Set Modem Control Register (MCR) */
+#define MCT_U232_SET_MODEM_CTRL_REQUEST	10
+#define MCT_U232_SET_MODEM_CTRL_SIZE	1
+
+/*
+ * This USB device request code is not well understood.  It is transmitted by
+ * the MCT-supplied Windows driver whenever the baud rate changes.
+ */
+#define MCT_U232_SET_UNKNOWN1_REQUEST	11  /* Unknown functionality */
+#define MCT_U232_SET_UNKNOWN1_SIZE	1
+
+/*
+ * This USB device request code appears to control whether CTS is required
+ * during transmission.
+ *
+ * Sending a zero byte allows data transmission to a device which is not
+ * asserting CTS.  Sending a '1' byte will cause transmission to be deferred
+ * until the device asserts CTS.
+ */
+#define MCT_U232_SET_CTS_REQUEST	12
+#define MCT_U232_SET_CTS_SIZE		1
+
+#define MCT_U232_MAX_SIZE		4	/* of MCT_XXX_SIZE */
+
+/*
+ * Baud rate (divisor)
+ * Actually, there are two of them, MCT website calls them "Philips solution"
+ * and "Intel solution". They are the regular MCT and "Sitecom" for us.
+ * This is pointless to document in the header, see the code for the bits.
+ */
+static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
+					speed_t value, speed_t *result);
+
+/*
+ * Line Control Register (LCR)
+ */
+#define MCT_U232_SET_BREAK              0x40
+
+#define MCT_U232_PARITY_SPACE		0x38
+#define MCT_U232_PARITY_MARK		0x28
+#define MCT_U232_PARITY_EVEN		0x18
+#define MCT_U232_PARITY_ODD		0x08
+#define MCT_U232_PARITY_NONE		0x00
+
+#define MCT_U232_DATA_BITS_5            0x00
+#define MCT_U232_DATA_BITS_6            0x01
+#define MCT_U232_DATA_BITS_7            0x02
+#define MCT_U232_DATA_BITS_8            0x03
+
+#define MCT_U232_STOP_BITS_2            0x04
+#define MCT_U232_STOP_BITS_1            0x00
+
+/*
+ * Modem Control Register (MCR)
+ */
+#define MCT_U232_MCR_NONE               0x8     /* Deactivate DTR and RTS */
+#define MCT_U232_MCR_RTS                0xa     /* Activate RTS */
+#define MCT_U232_MCR_DTR                0x9     /* Activate DTR */
+
+/*
+ * Modem Status Register (MSR)
+ */
+#define MCT_U232_MSR_INDEX              0x0     /* data[index] */
+#define MCT_U232_MSR_CD                 0x80    /* Current CD */
+#define MCT_U232_MSR_RI                 0x40    /* Current RI */
+#define MCT_U232_MSR_DSR                0x20    /* Current DSR */
+#define MCT_U232_MSR_CTS                0x10    /* Current CTS */
+#define MCT_U232_MSR_DCD                0x08    /* Delta CD */
+#define MCT_U232_MSR_DRI                0x04    /* Delta RI */
+#define MCT_U232_MSR_DDSR               0x02    /* Delta DSR */
+#define MCT_U232_MSR_DCTS               0x01    /* Delta CTS */
+
+/*
+ * Line Status Register (LSR)
+ */
+#define MCT_U232_LSR_INDEX	1	/* data[index] */
+#define MCT_U232_LSR_ERR	0x80	/* OE | PE | FE | BI */
+#define MCT_U232_LSR_TEMT	0x40	/* transmit register empty */
+#define MCT_U232_LSR_THRE	0x20	/* transmit holding register empty */
+#define MCT_U232_LSR_BI		0x10	/* break indicator */
+#define MCT_U232_LSR_FE		0x08	/* framing error */
+#define MCT_U232_LSR_OE		0x02	/* overrun error */
+#define MCT_U232_LSR_PE		0x04	/* parity error */
+#define MCT_U232_LSR_OE		0x02	/* overrun error */
+#define MCT_U232_LSR_DR		0x01	/* receive data ready */
+
+
+/* -----------------------------------------------------------------------------
+ * Technical Specification reverse engineered with SniffUSB on Windows98
+ * =====================================================================
+ *
+ *  The technical details of the device have been acquired be using "SniffUSB"
+ *  and the vendor-supplied device driver (version 2.3A) under Windows98. To
+ *  identify the USB vendor-specific requests and to assign them to terminal
+ *  settings (flow control, baud rate, etc.) the program "SerialSettings" from
+ *  William G. Greathouse has been proven to be very useful. I also used the
+ *  Win98 "HyperTerminal" and "usb-robot" on Linux for testing. The results and
+ *  observations are summarized below:
+ *
+ *  The USB requests seem to be directly mapped to the registers of a 8250,
+ *  16450 or 16550 UART. The FreeBSD handbook (appendix F.4 "Input/Output
+ *  devices") contains a comprehensive description of UARTs and its registers.
+ *  The bit descriptions are actually taken from there.
+ *
+ *
+ * Baud rate (divisor)
+ * -------------------
+ *
+ *   BmRequestType:  0x40 (0100 0000B)
+ *   bRequest:       0x05
+ *   wValue:         0x0000
+ *   wIndex:         0x0000
+ *   wLength:        0x0004
+ *   Data:           divisor = 115200 / baud_rate
+ *
+ *   SniffUSB observations (Nov 2003): Contrary to the 'wLength' value of 4
+ *   shown above, observations with a Belkin F5U109 adapter, using the
+ *   MCT-supplied Windows98 driver (U2SPORT.VXD, "File version: 1.21P.0104 for
+ *   Win98/Me"), show this request has a length of 1 byte, presumably because
+ *   of the fact that the Belkin adapter and the 'Sitecom U232-P25' adapter
+ *   use a baud-rate code instead of a conventional RS-232 baud rate divisor.
+ *   The current source code for this driver does not reflect this fact, but
+ *   the driver works fine with this adapter/driver combination nonetheless.
+ *
+ *
+ * Line Control Register (LCR)
+ * ---------------------------
+ *
+ *  BmRequestType:  0x40 (0100 0000B)    0xc0 (1100 0000B)
+ *  bRequest:       0x07                 0x06
+ *  wValue:         0x0000
+ *  wIndex:         0x0000
+ *  wLength:        0x0001
+ *  Data:           LCR (see below)
+ *
+ *  Bit 7: Divisor Latch Access Bit (DLAB). When set, access to the data
+ *	   transmit/receive register (THR/RBR) and the Interrupt Enable Register
+ *	   (IER) is disabled. Any access to these ports is now redirected to the
+ *	   Divisor Latch Registers. Setting this bit, loading the Divisor
+ *	   Registers, and clearing DLAB should be done with interrupts disabled.
+ *  Bit 6: Set Break. When set to "1", the transmitter begins to transmit
+ *	   continuous Spacing until this bit is set to "0". This overrides any
+ *	   bits of characters that are being transmitted.
+ *  Bit 5: Stick Parity. When parity is enabled, setting this bit causes parity
+ *	   to always be "1" or "0", based on the value of Bit 4.
+ *  Bit 4: Even Parity Select (EPS). When parity is enabled and Bit 5 is "0",
+ *	   setting this bit causes even parity to be transmitted and expected.
+ *	   Otherwise, odd parity is used.
+ *  Bit 3: Parity Enable (PEN). When set to "1", a parity bit is inserted
+ *	   between the last bit of the data and the Stop Bit. The UART will also
+ *	   expect parity to be present in the received data.
+ *  Bit 2: Number of Stop Bits (STB). If set to "1" and using 5-bit data words,
+ *	   1.5 Stop Bits are transmitted and expected in each data word. For
+ *	   6, 7 and 8-bit data words, 2 Stop Bits are transmitted and expected.
+ *	   When this bit is set to "0", one Stop Bit is used on each data word.
+ *  Bit 1: Word Length Select Bit #1 (WLSB1)
+ *  Bit 0: Word Length Select Bit #0 (WLSB0)
+ *	   Together these bits specify the number of bits in each data word.
+ *	     1 0  Word Length
+ *	     0 0  5 Data Bits
+ *	     0 1  6 Data Bits
+ *	     1 0  7 Data Bits
+ *	     1 1  8 Data Bits
+ *
+ *  SniffUSB observations: Bit 7 seems not to be used. There seem to be two bugs
+ *  in the Win98 driver: the break does not work (bit 6 is not asserted) and the
+ *  stick parity bit is not cleared when set once. The LCR can also be read
+ *  back with USB request 6 but this has never been observed with SniffUSB.
+ *
+ *
+ * Modem Control Register (MCR)
+ * ----------------------------
+ *
+ *  BmRequestType:  0x40  (0100 0000B)
+ *  bRequest:       0x0a
+ *  wValue:         0x0000
+ *  wIndex:         0x0000
+ *  wLength:        0x0001
+ *  Data:           MCR (Bit 4..7, see below)
+ *
+ *  Bit 7: Reserved, always 0.
+ *  Bit 6: Reserved, always 0.
+ *  Bit 5: Reserved, always 0.
+ *  Bit 4: Loop-Back Enable. When set to "1", the UART transmitter and receiver
+ *	   are internally connected together to allow diagnostic operations. In
+ *	   addition, the UART modem control outputs are connected to the UART
+ *	   modem control inputs. CTS is connected to RTS, DTR is connected to
+ *	   DSR, OUT1 is connected to RI, and OUT 2 is connected to DCD.
+ *  Bit 3: OUT 2. An auxiliary output that the host processor may set high or
+ *	   low. In the IBM PC serial adapter (and most clones), OUT 2 is used
+ *	   to tri-state (disable) the interrupt signal from the
+ *	   8250/16450/16550 UART.
+ *  Bit 2: OUT 1. An auxiliary output that the host processor may set high or
+ *	   low. This output is not used on the IBM PC serial adapter.
+ *  Bit 1: Request to Send (RTS). When set to "1", the output of the UART -RTS
+ *	   line is Low (Active).
+ *  Bit 0: Data Terminal Ready (DTR). When set to "1", the output of the UART
+ *	   -DTR line is Low (Active).
+ *
+ *  SniffUSB observations: Bit 2 and 4 seem not to be used but bit 3 has been
+ *  seen _always_ set.
+ *
+ *
+ * Modem Status Register (MSR)
+ * ---------------------------
+ *
+ *  BmRequestType:  0xc0  (1100 0000B)
+ *  bRequest:       0x02
+ *  wValue:         0x0000
+ *  wIndex:         0x0000
+ *  wLength:        0x0001
+ *  Data:           MSR (see below)
+ *
+ *  Bit 7: Data Carrier Detect (CD). Reflects the state of the DCD line on the
+ *	   UART.
+ *  Bit 6: Ring Indicator (RI). Reflects the state of the RI line on the UART.
+ *  Bit 5: Data Set Ready (DSR). Reflects the state of the DSR line on the UART.
+ *  Bit 4: Clear To Send (CTS). Reflects the state of the CTS line on the UART.
+ *  Bit 3: Delta Data Carrier Detect (DDCD). Set to "1" if the -DCD line has
+ *	   changed state one more more times since the last time the MSR was
+ *	   read by the host.
+ *  Bit 2: Trailing Edge Ring Indicator (TERI). Set to "1" if the -RI line has
+ *	   had a low to high transition since the last time the MSR was read by
+ *	   the host.
+ *  Bit 1: Delta Data Set Ready (DDSR). Set to "1" if the -DSR line has changed
+ *	   state one more more times since the last time the MSR was read by the
+ *	   host.
+ *  Bit 0: Delta Clear To Send (DCTS). Set to "1" if the -CTS line has changed
+ *	   state one more times since the last time the MSR was read by the
+ *	   host.
+ *
+ *  SniffUSB observations: the MSR is also returned as first byte on the
+ *  interrupt-in endpoint 0x83 to signal changes of modem status lines. The USB
+ *  request to read MSR cannot be applied during normal device operation.
+ *
+ *
+ * Line Status Register (LSR)
+ * --------------------------
+ *
+ *  Bit 7   Error in Receiver FIFO. On the 8250/16450 UART, this bit is zero.
+ *	    This bit is set to "1" when any of the bytes in the FIFO have one
+ *	    or more of the following error conditions: PE, FE, or BI.
+ *  Bit 6   Transmitter Empty (TEMT). When set to "1", there are no words
+ *	    remaining in the transmit FIFO or the transmit shift register. The
+ *	    transmitter is completely idle.
+ *  Bit 5   Transmitter Holding Register Empty (THRE). When set to "1", the
+ *	    FIFO (or holding register) now has room for at least one additional
+ *	    word to transmit. The transmitter may still be transmitting when
+ *	    this bit is set to "1".
+ *  Bit 4   Break Interrupt (BI). The receiver has detected a Break signal.
+ *  Bit 3   Framing Error (FE). A Start Bit was detected but the Stop Bit did
+ *	    not appear at the expected time. The received word is probably
+ *	    garbled.
+ *  Bit 2   Parity Error (PE). The parity bit was incorrect for the word
+ *	    received.
+ *  Bit 1   Overrun Error (OE). A new word was received and there was no room
+ *	    in the receive buffer. The newly-arrived word in the shift register
+ *	    is discarded. On 8250/16450 UARTs, the word in the holding register
+ *	    is discarded and the newly- arrived word is put in the holding
+ *	    register.
+ *  Bit 0   Data Ready (DR). One or more words are in the receive FIFO that the
+ *	    host may read. A word must be completely received and moved from
+ *	    the shift register into the FIFO (or holding register for
+ *	    8250/16450 designs) before this bit is set.
+ *
+ *  SniffUSB observations: the LSR is returned as second byte on the
+ *  interrupt-in endpoint 0x83 to signal error conditions. Such errors have
+ *  been seen with minicom/zmodem transfers (CRC errors).
+ *
+ *
+ * Unknown #1
+ * -------------------
+ *
+ *   BmRequestType:  0x40 (0100 0000B)
+ *   bRequest:       0x0b
+ *   wValue:         0x0000
+ *   wIndex:         0x0000
+ *   wLength:        0x0001
+ *   Data:           0x00
+ *
+ *   SniffUSB observations (Nov 2003): With the MCT-supplied Windows98 driver
+ *   (U2SPORT.VXD, "File version: 1.21P.0104 for Win98/Me"), this request
+ *   occurs immediately after a "Baud rate (divisor)" message.  It was not
+ *   observed at any other time.  It is unclear what purpose this message
+ *   serves.
+ *
+ *
+ * Unknown #2
+ * -------------------
+ *
+ *   BmRequestType:  0x40 (0100 0000B)
+ *   bRequest:       0x0c
+ *   wValue:         0x0000
+ *   wIndex:         0x0000
+ *   wLength:        0x0001
+ *   Data:           0x00
+ *
+ *   SniffUSB observations (Nov 2003): With the MCT-supplied Windows98 driver
+ *   (U2SPORT.VXD, "File version: 1.21P.0104 for Win98/Me"), this request
+ *   occurs immediately after the 'Unknown #1' message (see above).  It was
+ *   not observed at any other time.  It is unclear what other purpose (if
+ *   any) this message might serve, but without it, the USB/RS-232 adapter
+ *   will not write to RS-232 devices which do not assert the 'CTS' signal.
+ *
+ *
+ * Flow control
+ * ------------
+ *
+ *  SniffUSB observations: no flow control specific requests have been realized
+ *  apart from DTR/RTS settings. Both signals are dropped for no flow control
+ *  but asserted for hardware or software flow control.
+ *
+ *
+ * Endpoint usage
+ * --------------
+ *
+ *  SniffUSB observations: the bulk-out endpoint 0x1 and interrupt-in endpoint
+ *  0x81 is used to transmit and receive characters. The second interrupt-in
+ *  endpoint 0x83 signals exceptional conditions like modem line changes and
+ *  errors. The first byte returned is the MSR and the second byte the LSR.
+ *
+ *
+ * Other observations
+ * ------------------
+ *
+ *  Queued bulk transfers like used in visor.c did not work.
+ *
+ *
+ * Properties of the USB device used (as found in /var/log/messages)
+ * -----------------------------------------------------------------
+ *
+ *  Manufacturer: MCT Corporation.
+ *  Product: USB-232 Interfact Controller
+ *  SerialNumber: U2S22050
+ *
+ *    Length              = 18
+ *    DescriptorType      = 01
+ *    USB version         = 1.00
+ *    Vendor:Product      = 0711:0210
+ *    MaxPacketSize0      = 8
+ *    NumConfigurations   = 1
+ *    Device version      = 1.02
+ *    Device Class:SubClass:Protocol = 00:00:00
+ *      Per-interface classes
+ *  Configuration:
+ *    bLength             =    9
+ *    bDescriptorType     =   02
+ *    wTotalLength        = 0027
+ *    bNumInterfaces      =   01
+ *    bConfigurationValue =   01
+ *    iConfiguration      =   00
+ *    bmAttributes        =   c0
+ *    MaxPower            =  100mA
+ *
+ *    Interface: 0
+ *    Alternate Setting:  0
+ *      bLength             =    9
+ *      bDescriptorType     =   04
+ *      bInterfaceNumber    =   00
+ *      bAlternateSetting   =   00
+ *      bNumEndpoints       =   03
+ *      bInterface Class:SubClass:Protocol =   00:00:00
+ *      iInterface          =   00
+ *      Endpoint:
+ *	  bLength             =    7
+ *	  bDescriptorType     =   05
+ *	  bEndpointAddress    =   81 (in)
+ *	  bmAttributes        =   03 (Interrupt)
+ *	  wMaxPacketSize      = 0040
+ *	  bInterval           =   02
+ *      Endpoint:
+ *	  bLength             =    7
+ *	  bDescriptorType     =   05
+ *	  bEndpointAddress    =   01 (out)
+ *	  bmAttributes        =   02 (Bulk)
+ *	  wMaxPacketSize      = 0040
+ *	  bInterval           =   00
+ *      Endpoint:
+ *	  bLength             =    7
+ *	  bDescriptorType     =   05
+ *	  bEndpointAddress    =   83 (in)
+ *	  bmAttributes        =   03 (Interrupt)
+ *	  wMaxPacketSize      = 0002
+ *	  bInterval           =   02
+ *
+ *
+ * Hardware details (added by Martin Hamilton, 2001/12/06)
+ * -----------------------------------------------------------------
+ *
+ * This info was gleaned from opening a Belkin F5U109 DB9 USB serial
+ * adaptor, which turns out to simply be a re-badged U232-P9.  We
+ * know this because there is a sticky label on the circuit board
+ * which says "U232-P9" ;-)
+ *
+ * The circuit board inside the adaptor contains a Philips PDIUSBD12
+ * USB endpoint chip and a Philips P87C52UBAA microcontroller with
+ * embedded UART.  Exhaustive documentation for these is available at:
+ *
+ *   http://www.semiconductors.philips.com/pip/p87c52ubaa
+ *   http://www.nxp.com/acrobat_download/various/PDIUSBD12_PROGRAMMING_GUIDE.pdf
+ *
+ * Thanks to Julian Highfield for the pointer to the Philips database.
+ *
+ */
+
+#endif /* __LINUX_USB_SERIAL_MCT_U232_H */
+
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
new file mode 100644
index 0000000..e63cea0
--- /dev/null
+++ b/drivers/usb/serial/metro-usb.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+  Some of this code is credited to Linux USB open source files that are
+  distributed with Linux.
+
+  Copyright:	2007 Metrologic Instruments. All rights reserved.
+  Copyright:	2011 Azimut Ltd. <http://azimutrzn.ru/>
+*/
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver"
+
+/* Product information. */
+#define FOCUS_VENDOR_ID			0x0C2E
+#define FOCUS_PRODUCT_ID_BI		0x0720
+#define FOCUS_PRODUCT_ID_UNI		0x0700
+
+#define METROUSB_SET_REQUEST_TYPE	0x40
+#define METROUSB_SET_MODEM_CTRL_REQUEST	10
+#define METROUSB_SET_BREAK_REQUEST	0x40
+#define METROUSB_MCR_NONE		0x08	/* Deactivate DTR and RTS. */
+#define METROUSB_MCR_RTS		0x0a	/* Activate RTS. */
+#define METROUSB_MCR_DTR		0x09	/* Activate DTR. */
+#define WDR_TIMEOUT			5000	/* default urb timeout. */
+
+/* Private data structure. */
+struct metrousb_private {
+	spinlock_t lock;
+	int throttled;
+	unsigned long control_state;
+};
+
+/* Device table list. */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) },
+	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x0c2e, 0x0730, 0xff) },	/* MS7820 */
+	{ }, /* Terminating entry. */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* UNI-Directional mode commands for device configure */
+#define UNI_CMD_OPEN	0x80
+#define UNI_CMD_CLOSE	0xFF
+
+static int metrousb_is_unidirectional_mode(struct usb_serial *serial)
+{
+	u16 product_id = le16_to_cpu(serial->dev->descriptor.idProduct);
+
+	return product_id == FOCUS_PRODUCT_ID_UNI;
+}
+
+static int metrousb_calc_num_ports(struct usb_serial *serial,
+				   struct usb_serial_endpoints *epds)
+{
+	if (metrousb_is_unidirectional_mode(serial)) {
+		if (epds->num_interrupt_out == 0) {
+			dev_err(&serial->interface->dev, "interrupt-out endpoint missing\n");
+			return -ENODEV;
+		}
+	}
+
+	return 1;
+}
+
+static int metrousb_send_unidirectional_cmd(u8 cmd, struct usb_serial_port *port)
+{
+	int ret;
+	int actual_len;
+	u8 *buffer_cmd = NULL;
+
+	if (!metrousb_is_unidirectional_mode(port->serial))
+		return 0;
+
+	buffer_cmd = kzalloc(sizeof(cmd), GFP_KERNEL);
+	if (!buffer_cmd)
+		return -ENOMEM;
+
+	*buffer_cmd = cmd;
+
+	ret = usb_interrupt_msg(port->serial->dev,
+		usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+		buffer_cmd, sizeof(cmd),
+		&actual_len, USB_CTRL_SET_TIMEOUT);
+
+	kfree(buffer_cmd);
+
+	if (ret < 0)
+		return ret;
+	else if (actual_len != sizeof(cmd))
+		return -EIO;
+	return 0;
+}
+
+static void metrousb_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int throttled = 0;
+	int result = 0;
+	unsigned long flags = 0;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	switch (urb->status) {
+	case 0:
+		/* Success status, read from the port. */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* urb has been terminated. */
+		dev_dbg(&port->dev,
+			"%s - urb shutting down, error code=%d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(&port->dev,
+			"%s - non-zero urb received, error code=%d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+
+	/* Set the data read from the usb port into the serial port buffer. */
+	if (urb->actual_length) {
+		/* Loop through the data copying each byte to the tty layer. */
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+
+		/* Force the data to the tty layer. */
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* Set any port variables. */
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	throttled = metro_priv->throttled;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+
+	if (throttled)
+		return;
+exit:
+	/* Try to resubmit the urb. */
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&port->dev,
+			"%s - failed submitting interrupt in urb, error code=%d\n",
+			__func__, result);
+}
+
+static void metrousb_cleanup(struct usb_serial_port *port)
+{
+	usb_kill_urb(port->interrupt_in_urb);
+
+	metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port);
+}
+
+static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+	int result = 0;
+
+	/* Set the private data information for the port. */
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	metro_priv->control_state = 0;
+	metro_priv->throttled = 0;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+
+	/* Clear the urb pipe. */
+	usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe);
+
+	/* Start reading from the device */
+	usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
+			  usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
+			   port->interrupt_in_urb->transfer_buffer,
+			   port->interrupt_in_urb->transfer_buffer_length,
+			   metrousb_read_int_callback, port, 1);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+
+	if (result) {
+		dev_err(&port->dev,
+			"%s - failed submitting interrupt in urb, error code=%d\n",
+			__func__, result);
+		return result;
+	}
+
+	/* Send activate cmd to device */
+	result = metrousb_send_unidirectional_cmd(UNI_CMD_OPEN, port);
+	if (result) {
+		dev_err(&port->dev,
+			"%s - failed to configure device, error code=%d\n",
+			__func__, result);
+		goto err_kill_urb;
+	}
+
+	return 0;
+
+err_kill_urb:
+	usb_kill_urb(port->interrupt_in_urb);
+
+	return result;
+}
+
+static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state)
+{
+	int retval = 0;
+	unsigned char mcr = METROUSB_MCR_NONE;
+
+	dev_dbg(&serial->dev->dev, "%s - control state = %d\n",
+		__func__, control_state);
+
+	/* Set the modem control value. */
+	if (control_state & TIOCM_DTR)
+		mcr |= METROUSB_MCR_DTR;
+	if (control_state & TIOCM_RTS)
+		mcr |= METROUSB_MCR_RTS;
+
+	/* Send the command to the usb port. */
+	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				METROUSB_SET_REQUEST_TYPE, METROUSB_SET_MODEM_CTRL_REQUEST,
+				control_state, 0, NULL, 0, WDR_TIMEOUT);
+	if (retval < 0)
+		dev_err(&serial->dev->dev,
+			"%s - set modem ctrl=0x%x failed, error code=%d\n",
+			__func__, mcr, retval);
+
+	return retval;
+}
+
+static int metrousb_port_probe(struct usb_serial_port *port)
+{
+	struct metrousb_private *metro_priv;
+
+	metro_priv = kzalloc(sizeof(*metro_priv), GFP_KERNEL);
+	if (!metro_priv)
+		return -ENOMEM;
+
+	spin_lock_init(&metro_priv->lock);
+
+	usb_set_serial_port_data(port, metro_priv);
+
+	return 0;
+}
+
+static int metrousb_port_remove(struct usb_serial_port *port)
+{
+	struct metrousb_private *metro_priv;
+
+	metro_priv = usb_get_serial_port_data(port);
+	kfree(metro_priv);
+
+	return 0;
+}
+
+static void metrousb_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+
+	/* Set the private information for the port to stop reading data. */
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	metro_priv->throttled = 1;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+}
+
+static int metrousb_tiocmget(struct tty_struct *tty)
+{
+	unsigned long control_state = 0;
+	struct usb_serial_port *port = tty->driver_data;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	control_state = metro_priv->control_state;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+
+	return control_state;
+}
+
+static int metrousb_tiocmset(struct tty_struct *tty,
+			     unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+	unsigned long control_state = 0;
+
+	dev_dbg(tty->dev, "%s - set=%d, clear=%d\n", __func__, set, clear);
+
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	control_state = metro_priv->control_state;
+
+	/* Set the RTS and DTR values. */
+	if (set & TIOCM_RTS)
+		control_state |= TIOCM_RTS;
+	if (set & TIOCM_DTR)
+		control_state |= TIOCM_DTR;
+	if (clear & TIOCM_RTS)
+		control_state &= ~TIOCM_RTS;
+	if (clear & TIOCM_DTR)
+		control_state &= ~TIOCM_DTR;
+
+	metro_priv->control_state = control_state;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+	return metrousb_set_modem_ctrl(serial, control_state);
+}
+
+static void metrousb_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
+	unsigned long flags = 0;
+	int result = 0;
+
+	/* Set the private information for the port to resume reading data. */
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	metro_priv->throttled = 0;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+
+	/* Submit the urb to read from the port. */
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result)
+		dev_err(tty->dev,
+			"failed submitting interrupt in urb error code=%d\n",
+			result);
+}
+
+static struct usb_serial_driver metrousb_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"metro-usb",
+	},
+	.description		= "Metrologic USB to Serial",
+	.id_table		= id_table,
+	.num_interrupt_in	= 1,
+	.calc_num_ports		= metrousb_calc_num_ports,
+	.open			= metrousb_open,
+	.close			= metrousb_cleanup,
+	.read_int_callback	= metrousb_read_int_callback,
+	.port_probe		= metrousb_port_probe,
+	.port_remove		= metrousb_port_remove,
+	.throttle		= metrousb_throttle,
+	.unthrottle		= metrousb_unthrottle,
+	.tiocmget		= metrousb_tiocmget,
+	.tiocmset		= metrousb_tiocmset,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&metrousb_device,
+	NULL,
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Philip Nicastro");
+MODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
new file mode 100644
index 0000000..2710952
--- /dev/null
+++ b/drivers/usb/serial/mos7720.c
@@ -0,0 +1,2035 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mos7720.c
+ *   Controls the Moschip 7720 usb to dual port serial converter
+ *
+ * Copyright 2006 Moschip Semiconductor Tech. Ltd.
+ *
+ * Developed by:
+ * 	Vijaya Kumar <vijaykumar.gn@gmail.com>
+ *	Ajay Kumar <naanuajay@yahoo.com>
+ *	Gurudeva <ngurudeva@yahoo.com>
+ *
+ * Cleaned up from the original by:
+ *	Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ *	Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ *	Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+#include <linux/parport.h>
+
+#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
+#define DRIVER_DESC "Moschip USB Serial Driver"
+
+/* default urb timeout */
+#define MOS_WDR_TIMEOUT	5000
+
+#define MOS_MAX_PORT	0x02
+#define MOS_WRITE	0x0E
+#define MOS_READ	0x0D
+
+/* Interrupt Routines Defines	*/
+#define SERIAL_IIR_RLS	0x06
+#define SERIAL_IIR_RDA	0x04
+#define SERIAL_IIR_CTI	0x0c
+#define SERIAL_IIR_THR	0x02
+#define SERIAL_IIR_MS	0x00
+
+#define NUM_URBS			16	/* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE	32	/* URB Size */
+
+/* This structure holds all of the local serial port information */
+struct moschip_port {
+	__u8	shadowLCR;		/* last LCR value received */
+	__u8	shadowMCR;		/* last MCR value received */
+	__u8	shadowMSR;		/* last MSR value received */
+	char			open;
+	struct usb_serial_port	*port;	/* loop back to the owner */
+	struct urb		*write_urb_pool[NUM_URBS];
+};
+
+#define USB_VENDOR_ID_MOSCHIP		0x9710
+#define MOSCHIP_DEVICE_ID_7720		0x7720
+#define MOSCHIP_DEVICE_ID_7715		0x7715
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) },
+	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) },
+	{ } /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+
+/* initial values for parport regs */
+#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 */
+	PPF = 2<<5,	 /* moschip calls this 'CB-FIFO mode */
+};
+
+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 usb_serial       *serial;       /* back to containing struct */
+	__u8	                shadowECR;     /* parallel port regs... */
+	__u8	                shadowDCR;
+	atomic_t                shadowDSR;     /* updated in int-in callback */
+};
+
+/* lock guards against dereferencing NULL ptr in parport ops callbacks */
+static DEFINE_SPINLOCK(release_lock);
+
+#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
+
+static const unsigned int dummy; /* for clarity in register access fns */
+
+enum mos_regs {
+	MOS7720_THR,		  /* serial port regs */
+	MOS7720_RHR,
+	MOS7720_IER,
+	MOS7720_FCR,
+	MOS7720_ISR,
+	MOS7720_LCR,
+	MOS7720_MCR,
+	MOS7720_LSR,
+	MOS7720_MSR,
+	MOS7720_SPR,
+	MOS7720_DLL,
+	MOS7720_DLM,
+	MOS7720_DPR,		  /* parallel port regs */
+	MOS7720_DSR,
+	MOS7720_DCR,
+	MOS7720_ECR,
+	MOS7720_SP1_REG,	  /* device control regs */
+	MOS7720_SP2_REG,	  /* serial port 2 (7720 only) */
+	MOS7720_PP_REG,
+	MOS7720_SP_CONTROL_REG,
+};
+
+/*
+ * Return the correct value for the Windex field of the setup packet
+ * for a control endpoint message.  See the 7715 datasheet.
+ */
+static inline __u16 get_reg_index(enum mos_regs reg)
+{
+	static const __u16 mos7715_index_lookup_table[] = {
+		0x00,		/* MOS7720_THR */
+		0x00,		/* MOS7720_RHR */
+		0x01,		/* MOS7720_IER */
+		0x02,		/* MOS7720_FCR */
+		0x02,		/* MOS7720_ISR */
+		0x03,		/* MOS7720_LCR */
+		0x04,		/* MOS7720_MCR */
+		0x05,		/* MOS7720_LSR */
+		0x06,		/* MOS7720_MSR */
+		0x07,		/* MOS7720_SPR */
+		0x00,		/* MOS7720_DLL */
+		0x01,		/* MOS7720_DLM */
+		0x00,		/* MOS7720_DPR */
+		0x01,		/* MOS7720_DSR */
+		0x02,		/* MOS7720_DCR */
+		0x0a,		/* MOS7720_ECR */
+		0x01,		/* MOS7720_SP1_REG */
+		0x02,		/* MOS7720_SP2_REG (7720 only) */
+		0x04,		/* MOS7720_PP_REG (7715 only) */
+		0x08,		/* MOS7720_SP_CONTROL_REG */
+	};
+	return mos7715_index_lookup_table[reg];
+}
+
+/*
+ * Return the correct value for the upper byte of the Wvalue field of
+ * the setup packet for a control endpoint message.
+ */
+static inline __u16 get_reg_value(enum mos_regs reg,
+				  unsigned int serial_portnum)
+{
+	if (reg >= MOS7720_SP1_REG)	/* control reg */
+		return 0x0000;
+
+	else if (reg >= MOS7720_DPR)	/* parallel port reg (7715 only) */
+		return 0x0100;
+
+	else			      /* serial port reg */
+		return (serial_portnum + 2) << 8;
+}
+
+/*
+ * Write data byte to the specified device register.  The data is embedded in
+ * the value field of the setup packet. serial_portnum is ignored for registers
+ * not specific to a particular serial port.
+ */
+static int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
+			 enum mos_regs reg, __u8 data)
+{
+	struct usb_device *usbdev = serial->dev;
+	unsigned int pipe = usb_sndctrlpipe(usbdev, 0);
+	__u8 request = (__u8)0x0e;
+	__u8 requesttype = (__u8)0x40;
+	__u16 index = get_reg_index(reg);
+	__u16 value = get_reg_value(reg, serial_portnum) + data;
+	int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
+				     index, NULL, 0, MOS_WDR_TIMEOUT);
+	if (status < 0)
+		dev_err(&usbdev->dev,
+			"mos7720: usb_control_msg() failed: %d\n", status);
+	return status;
+}
+
+/*
+ * Read data byte from the specified device register.  The data returned by the
+ * device is embedded in the value field of the setup packet.  serial_portnum is
+ * ignored for registers that are not specific to a particular serial port.
+ */
+static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
+			enum mos_regs reg, __u8 *data)
+{
+	struct usb_device *usbdev = serial->dev;
+	unsigned int pipe = usb_rcvctrlpipe(usbdev, 0);
+	__u8 request = (__u8)0x0d;
+	__u8 requesttype = (__u8)0xc0;
+	__u16 index = get_reg_index(reg);
+	__u16 value = get_reg_value(reg, serial_portnum);
+	u8 *buf;
+	int status;
+
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	status = usb_control_msg(usbdev, pipe, request, requesttype, value,
+				     index, buf, 1, MOS_WDR_TIMEOUT);
+	if (status == 1) {
+		*data = *buf;
+	} else {
+		dev_err(&usbdev->dev,
+			"mos7720: usb_control_msg() failed: %d\n", status);
+		if (status >= 0)
+			status = -EIO;
+		*data = 0;
+	}
+
+	kfree(buf);
+
+	return status;
+}
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+
+static inline int mos7715_change_mode(struct mos7715_parport *mos_parport,
+				      enum mos7715_pp_modes mode)
+{
+	mos_parport->shadowECR = mode;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
+		      mos_parport->shadowECR);
+	return 0;
+}
+
+static void destroy_mos_parport(struct kref *kref)
+{
+	struct mos7715_parport *mos_parport =
+		container_of(kref, struct mos7715_parport, ref_count);
+
+	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(unsigned long _mos_parport)
+{
+	int ret_val;
+	unsigned long flags;
+	struct mos7715_parport *mos_parport = (void *)_mos_parport;
+	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;
+
+	kref_get(&mos_parport->ref_count);
+	urbtrack->mos_parport = mos_parport;
+	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_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
+ * 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
+ * been destroyed, and (2) the device has been disconnected, but usbserial has
+ * not called the release function yet because someone has a serial port open.
+ * The shared release_lock prevents the first, and the mutex and disconnected
+ * flag maintained by usbserial covers the second.  We also use the msg_pending
+ * flag to ensure that all synchronous usb message calls have completed before
+ * our release function can return.
+ */
+static int parport_prologue(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport;
+
+	spin_lock(&release_lock);
+	mos_parport = pp->private_data;
+	if (unlikely(mos_parport == NULL)) {
+		/* release fn called, port struct destroyed */
+		spin_unlock(&release_lock);
+		return -1;
+	}
+	mos_parport->msg_pending = true;   /* synch usb call pending */
+	reinit_completion(&mos_parport->syncmsg_compl);
+	spin_unlock(&release_lock);
+
+	mutex_lock(&mos_parport->serial->disc_mutex);
+	if (mos_parport->serial->disconnected) {
+		/* device disconnected */
+		mutex_unlock(&mos_parport->serial->disc_mutex);
+		mos_parport->msg_pending = false;
+		complete(&mos_parport->syncmsg_compl);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * This is the common bottom part of all parallel port functions that send
+ * synchronous messages to the device.
+ */
+static inline void parport_epilogue(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	mutex_unlock(&mos_parport->serial->disc_mutex);
+	mos_parport->msg_pending = false;
+	complete(&mos_parport->syncmsg_compl);
+}
+
+static void parport_mos7715_write_data(struct parport *pp, unsigned char d)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, SPP);
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, (__u8)d);
+	parport_epilogue(pp);
+}
+
+static unsigned char parport_mos7715_read_data(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	unsigned char d;
+
+	if (parport_prologue(pp) < 0)
+		return 0;
+	read_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, &d);
+	parport_epilogue(pp);
+	return d;
+}
+
+static void parport_mos7715_write_control(struct parport *pp, unsigned char d)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	__u8 data;
+
+	if (parport_prologue(pp) < 0)
+		return;
+	data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, data);
+	mos_parport->shadowDCR = data;
+	parport_epilogue(pp);
+}
+
+static unsigned char parport_mos7715_read_control(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport;
+	__u8 dcr;
+
+	spin_lock(&release_lock);
+	mos_parport = pp->private_data;
+	if (unlikely(mos_parport == NULL)) {
+		spin_unlock(&release_lock);
+		return 0;
+	}
+	dcr = mos_parport->shadowDCR & 0x0f;
+	spin_unlock(&release_lock);
+	return dcr;
+}
+
+static unsigned char parport_mos7715_frob_control(struct parport *pp,
+						  unsigned char mask,
+						  unsigned char val)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	__u8 dcr;
+
+	mask &= 0x0f;
+	val &= 0x0f;
+	if (parport_prologue(pp) < 0)
+		return 0;
+	mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
+		      mos_parport->shadowDCR);
+	dcr = mos_parport->shadowDCR & 0x0f;
+	parport_epilogue(pp);
+	return dcr;
+}
+
+static unsigned char parport_mos7715_read_status(struct parport *pp)
+{
+	unsigned char status;
+	struct mos7715_parport *mos_parport;
+
+	spin_lock(&release_lock);
+	mos_parport = pp->private_data;
+	if (unlikely(mos_parport == NULL)) {	/* release called */
+		spin_unlock(&release_lock);
+		return 0;
+	}
+	status = atomic_read(&mos_parport->shadowDSR) & 0xf8;
+	spin_unlock(&release_lock);
+	return status;
+}
+
+static void parport_mos7715_enable_irq(struct parport *pp)
+{
+}
+
+static void parport_mos7715_disable_irq(struct parport *pp)
+{
+}
+
+static void parport_mos7715_data_forward(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, PS2);
+	mos_parport->shadowDCR &=  ~0x20;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
+		      mos_parport->shadowDCR);
+	parport_epilogue(pp);
+}
+
+static void parport_mos7715_data_reverse(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, PS2);
+	mos_parport->shadowDCR |= 0x20;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
+		      mos_parport->shadowDCR);
+	parport_epilogue(pp);
+}
+
+static void parport_mos7715_init_state(struct pardevice *dev,
+				       struct parport_state *s)
+{
+	s->u.pc.ctr = DCR_INIT_VAL;
+	s->u.pc.ecr = ECR_INIT_VAL;
+}
+
+/* N.B. Parport core code requires that this function not block */
+static void parport_mos7715_save_state(struct parport *pp,
+				       struct parport_state *s)
+{
+	struct mos7715_parport *mos_parport;
+
+	spin_lock(&release_lock);
+	mos_parport = pp->private_data;
+	if (unlikely(mos_parport == NULL)) {	/* release called */
+		spin_unlock(&release_lock);
+		return;
+	}
+	s->u.pc.ctr = mos_parport->shadowDCR;
+	s->u.pc.ecr = mos_parport->shadowECR;
+	spin_unlock(&release_lock);
+}
+
+/* N.B. Parport core code requires that this function not block */
+static void parport_mos7715_restore_state(struct parport *pp,
+					  struct parport_state *s)
+{
+	struct mos7715_parport *mos_parport;
+
+	spin_lock(&release_lock);
+	mos_parport = pp->private_data;
+	if (unlikely(mos_parport == NULL)) {	/* release called */
+		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);
+	spin_unlock(&release_lock);
+}
+
+static size_t parport_mos7715_write_compat(struct parport *pp,
+					   const void *buffer,
+					   size_t len, int flags)
+{
+	int retval;
+	struct mos7715_parport *mos_parport = pp->private_data;
+	int actual_len;
+
+	if (parport_prologue(pp) < 0)
+		return 0;
+	mos7715_change_mode(mos_parport, PPF);
+	retval = usb_bulk_msg(mos_parport->serial->dev,
+			      usb_sndbulkpipe(mos_parport->serial->dev, 2),
+			      (void *)buffer, len, &actual_len,
+			      MOS_WDR_TIMEOUT);
+	parport_epilogue(pp);
+	if (retval) {
+		dev_err(&mos_parport->serial->dev->dev,
+			"mos7720: usb_bulk_msg() failed: %d\n", retval);
+		return 0;
+	}
+	return actual_len;
+}
+
+static struct parport_operations parport_mos7715_ops = {
+	.owner =		THIS_MODULE,
+	.write_data =		parport_mos7715_write_data,
+	.read_data =		parport_mos7715_read_data,
+
+	.write_control =	parport_mos7715_write_control,
+	.read_control =		parport_mos7715_read_control,
+	.frob_control =		parport_mos7715_frob_control,
+
+	.read_status =		parport_mos7715_read_status,
+
+	.enable_irq =		parport_mos7715_enable_irq,
+	.disable_irq =		parport_mos7715_disable_irq,
+
+	.data_forward =		parport_mos7715_data_forward,
+	.data_reverse =		parport_mos7715_data_reverse,
+
+	.init_state =		parport_mos7715_init_state,
+	.save_state =		parport_mos7715_save_state,
+	.restore_state =	parport_mos7715_restore_state,
+
+	.compat_write_data =	parport_mos7715_write_compat,
+
+	.nibble_read_data =	parport_ieee1284_read_nibble,
+	.byte_read_data =	parport_ieee1284_read_byte,
+};
+
+/*
+ * Allocate and initialize parallel port control struct, initialize
+ * the parallel port hardware device, and register with the parport subsystem.
+ */
+static int mos7715_parport_init(struct usb_serial *serial)
+{
+	struct mos7715_parport *mos_parport;
+
+	/* allocate and initialize parallel port control struct */
+	mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
+	if (!mos_parport)
+		return -ENOMEM;
+
+	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_init(&mos_parport->urb_tasklet, send_deferred_urbs,
+		     (unsigned long) mos_parport);
+	init_completion(&mos_parport->syncmsg_compl);
+
+	/* cycle parallel port reset bit */
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x80);
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x00);
+
+	/* initialize device registers */
+	mos_parport->shadowDCR = DCR_INIT_VAL;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
+		      mos_parport->shadowDCR);
+	mos_parport->shadowECR = ECR_INIT_VAL;
+	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
+		      mos_parport->shadowECR);
+
+	/* register with parport core */
+	mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE,
+						PARPORT_DMA_NONE,
+						&parport_mos7715_ops);
+	if (mos_parport->pp == NULL) {
+		dev_err(&serial->interface->dev,
+			"Could not register parport\n");
+		kref_put(&mos_parport->ref_count, destroy_mos_parport);
+		return -EIO;
+	}
+	mos_parport->pp->private_data = mos_parport;
+	mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP;
+	mos_parport->pp->dev = &serial->interface->dev;
+	parport_announce_port(mos_parport->pp);
+
+	return 0;
+}
+#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
+
+/*
+ * mos7720_interrupt_callback
+ *	this is the callback function for when we have received data on the
+ *	interrupt endpoint.
+ */
+static void mos7720_interrupt_callback(struct urb *urb)
+{
+	int result;
+	int length;
+	int status = urb->status;
+	struct device *dev = &urb->dev->dev;
+	__u8 *data;
+	__u8 sp1;
+	__u8 sp2;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
+		goto exit;
+	}
+
+	length = urb->actual_length;
+	data = urb->transfer_buffer;
+
+	/* Moschip get 4 bytes
+	 * Byte 1 IIR Port 1 (port.number is 0)
+	 * Byte 2 IIR Port 2 (port.number is 1)
+	 * Byte 3 --------------
+	 * Byte 4 FIFO status for both */
+
+	/* the above description is inverted
+	 * 	oneukum 2007-03-14 */
+
+	if (unlikely(length != 4)) {
+		dev_dbg(dev, "Wrong data !!!\n");
+		return;
+	}
+
+	sp1 = data[3];
+	sp2 = data[2];
+
+	if ((sp1 | sp2) & 0x01) {
+		/* No Interrupt Pending in both the ports */
+		dev_dbg(dev, "No Interrupt !!!\n");
+	} else {
+		switch (sp1 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dev_dbg(dev, "Serial Port 1: Receiver time out\n");
+			break;
+		case SERIAL_IIR_MS:
+			/* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */
+			break;
+		}
+
+		switch (sp2 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dev_dbg(dev, "Serial Port 2: Receiver time out\n");
+			break;
+		case SERIAL_IIR_MS:
+			/* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */
+			break;
+		}
+	}
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
+}
+
+/*
+ * mos7715_interrupt_callback
+ *	this is the 7715's callback function for when we have received data on
+ *	the interrupt endpoint.
+ */
+static void mos7715_interrupt_callback(struct urb *urb)
+{
+	int result;
+	int length;
+	int status = urb->status;
+	struct device *dev = &urb->dev->dev;
+	__u8 *data;
+	__u8 iir;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -ENODEV:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
+		goto exit;
+	}
+
+	length = urb->actual_length;
+	data = urb->transfer_buffer;
+
+	/* Structure of data from 7715 device:
+	 * Byte 1: IIR serial Port
+	 * Byte 2: unused
+	 * Byte 2: DSR parallel port
+	 * Byte 4: FIFO status for both */
+
+	if (unlikely(length != 4)) {
+		dev_dbg(dev, "Wrong data !!!\n");
+		return;
+	}
+
+	iir = data[0];
+	if (!(iir & 0x01)) {	/* serial port interrupt pending */
+		switch (iir & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dev_dbg(dev, "Serial Port: Receiver time out\n");
+			break;
+		case SERIAL_IIR_MS:
+			/* dev_dbg(dev, "Serial Port: Modem status change\n"); */
+			break;
+		}
+	}
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+	{       /* update local copy of DSR reg */
+		struct usb_serial_port *port = urb->context;
+		struct mos7715_parport *mos_parport = port->serial->private;
+		if (unlikely(mos_parport == NULL))
+			return;
+		atomic_set(&mos_parport->shadowDSR, data[2]);
+	}
+#endif
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
+}
+
+/*
+ * mos7720_bulk_in_callback
+ *	this is the callback function for when we have received data on the
+ *	bulk in endpoint.
+ */
+static void mos7720_bulk_in_callback(struct urb *urb)
+{
+	int retval;
+	unsigned char *data ;
+	struct usb_serial_port *port;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
+		return;
+	}
+
+	port = urb->context;
+
+	dev_dbg(&port->dev, "Entering...%s\n", __func__);
+
+	data = urb->transfer_buffer;
+
+	if (urb->actual_length) {
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (retval)
+			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
+	}
+}
+
+/*
+ * mos7720_bulk_out_data_callback
+ *	this is the callback function for when we have finished sending serial
+ *	data on the bulk out endpoint.
+ */
+static void mos7720_bulk_out_data_callback(struct urb *urb)
+{
+	struct moschip_port *mos7720_port;
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
+		return;
+	}
+
+	mos7720_port = urb->context;
+	if (!mos7720_port) {
+		dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n");
+		return ;
+	}
+
+	if (mos7720_port->open)
+		tty_port_tty_wakeup(&mos7720_port->port->port);
+}
+
+static int mos77xx_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+
+	if (product == MOSCHIP_DEVICE_ID_7715) {
+		/*
+		 * The 7715 uses the first bulk in/out endpoint pair for the
+		 * parallel port, and the second for the serial port. We swap
+		 * the endpoint descriptors here so that the the first and
+		 * only registered port structure uses the serial-port
+		 * endpoints.
+		 */
+		swap(epds->bulk_in[0], epds->bulk_in[1]);
+		swap(epds->bulk_out[0], epds->bulk_out[1]);
+
+		return 1;
+	}
+
+	return 2;
+}
+
+static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct urb *urb;
+	struct moschip_port *mos7720_port;
+	int response;
+	int port_number;
+	__u8 data;
+	int allocated_urbs = 0;
+	int j;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	/* Initialising the write urb pool */
+	for (j = 0; j < NUM_URBS; ++j) {
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		mos7720_port->write_urb_pool[j] = urb;
+		if (!urb)
+			continue;
+
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			usb_free_urb(mos7720_port->write_urb_pool[j]);
+			mos7720_port->write_urb_pool[j] = NULL;
+			continue;
+		}
+		allocated_urbs++;
+	}
+
+	if (!allocated_urbs)
+		return -ENOMEM;
+
+	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
+	  *
+	  * Register Index
+	  * 0 : MOS7720_THR/MOS7720_RHR
+	  * 1 : MOS7720_IER
+	  * 2 : MOS7720_FCR
+	  * 3 : MOS7720_LCR
+	  * 4 : MOS7720_MCR
+	  * 5 : MOS7720_LSR
+	  * 6 : MOS7720_MSR
+	  * 7 : MOS7720_SPR
+	  *
+	  * 0x08 : SP1/2 Control Reg
+	  */
+	port_number = port->port_number;
+	read_mos_reg(serial, port_number, MOS7720_LSR, &data);
+
+	dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data);
+
+	write_mos_reg(serial, dummy, MOS7720_SP1_REG, 0x02);
+	write_mos_reg(serial, dummy, MOS7720_SP2_REG, 0x02);
+
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
+
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
+	mos7720_port->shadowLCR = 0x03;
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	write_mos_reg(serial, port_number, MOS7720_SP_CONTROL_REG, 0x00);
+	read_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, &data);
+	data = data | (port->port_number + 1);
+	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, data);
+	mos7720_port->shadowLCR = 0x83;
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, MOS7720_THR, 0x0c);
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
+	mos7720_port->shadowLCR = 0x03;
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
+
+	response = usb_submit_urb(port->read_urb, GFP_KERNEL);
+	if (response)
+		dev_err(&port->dev, "%s - Error %d submitting read urb\n",
+							__func__, response);
+
+	/* initialize our port settings */
+	mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
+
+	/* send a open port command */
+	mos7720_port->open = 1;
+
+	return 0;
+}
+
+/*
+ * mos7720_chars_in_buffer
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we currently have outstanding in the port (data that has
+ *	been written, but hasn't made it out the port yet)
+ *	If successful, we return the number of bytes left to be written in the
+ *	system,
+ *	Otherwise we return a negative error number.
+ */
+static int mos7720_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int i;
+	int chars = 0;
+	struct moschip_port *mos7720_port;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return 0;
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i] &&
+		    mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
+			chars += URB_TRANSFER_BUFFER_SIZE;
+	}
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+}
+
+static void mos7720_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+	int j;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return;
+
+	for (j = 0; j < NUM_URBS; ++j)
+		usb_kill_urb(mos7720_port->write_urb_pool[j]);
+
+	/* Freeing Write URBs */
+	for (j = 0; j < NUM_URBS; ++j) {
+		if (mos7720_port->write_urb_pool[j]) {
+			kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
+			usb_free_urb(mos7720_port->write_urb_pool[j]);
+		}
+	}
+
+	/* While closing port, shutdown all bulk read, write  *
+	 * and interrupt read if they exists, otherwise nop   */
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+
+	write_mos_reg(serial, port->port_number, MOS7720_MCR, 0x00);
+	write_mos_reg(serial, port->port_number, MOS7720_IER, 0x00);
+
+	mos7720_port->open = 0;
+}
+
+static void mos7720_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned char data;
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return;
+
+	if (break_state == -1)
+		data = mos7720_port->shadowLCR | UART_LCR_SBC;
+	else
+		data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
+
+	mos7720_port->shadowLCR  = data;
+	write_mos_reg(serial, port->port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+}
+
+/*
+ * mos7720_write_room
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we can accept for a specific port.
+ *	If successful, we return the amount of room that we have for this port
+ *	Otherwise we return a negative error number.
+ */
+static int mos7720_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port;
+	int room = 0;
+	int i;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	/* FIXME: Locking */
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i] &&
+		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
+			room += URB_TRANSFER_BUFFER_SIZE;
+	}
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
+				 const unsigned char *data, int count)
+{
+	int status;
+	int i;
+	int bytes_sent = 0;
+	int transfer_size;
+
+	struct moschip_port *mos7720_port;
+	struct usb_serial *serial;
+	struct urb    *urb;
+	const unsigned char *current_position = data;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	/* try to find a free urb in the list */
+	urb = NULL;
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i] &&
+		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
+			urb = mos7720_port->write_urb_pool[i];
+			dev_dbg(&port->dev, "URB:%d\n", i);
+			break;
+		}
+	}
+
+	if (urb == NULL) {
+		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
+		goto exit;
+	}
+
+	if (urb->transfer_buffer == NULL) {
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_ATOMIC);
+		if (!urb->transfer_buffer)
+			goto exit;
+	}
+	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+	memcpy(urb->transfer_buffer, current_position, transfer_size);
+	usb_serial_debug_data(&port->dev, __func__, transfer_size,
+			      urb->transfer_buffer);
+
+	/* fill urb with data and submit  */
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev,
+					port->bulk_out_endpointAddress),
+			  urb->transfer_buffer, transfer_size,
+			  mos7720_bulk_out_data_callback, mos7720_port);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
+			"with status = %d\n", __func__, status);
+		bytes_sent = status;
+		goto exit;
+	}
+	bytes_sent = transfer_size;
+
+exit:
+	return bytes_sent;
+}
+
+static void mos7720_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port;
+	int status;
+
+	mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the stop character */
+	if (I_IXOFF(tty)) {
+		unsigned char stop_char = STOP_CHAR(tty);
+		status = mos7720_write(tty, port, &stop_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
+		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
+			      mos7720_port->shadowMCR);
+	}
+}
+
+static void mos7720_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
+	int status;
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the start character */
+	if (I_IXOFF(tty)) {
+		unsigned char start_char = START_CHAR(tty);
+		status = mos7720_write(tty, port, &start_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		mos7720_port->shadowMCR |= UART_MCR_RTS;
+		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
+			      mos7720_port->shadowMCR);
+	}
+}
+
+/* FIXME: this function does not work */
+static int set_higher_rates(struct moschip_port *mos7720_port,
+			    unsigned int baud)
+{
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	int port_number;
+	enum mos_regs sp_reg;
+	if (mos7720_port == NULL)
+		return -EINVAL;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+
+	 /***********************************************
+	 *      Init Sequence for higher rates
+	 ***********************************************/
+	dev_dbg(&port->dev, "Sending Setting Commands ..........\n");
+	port_number = port->port_number;
+
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x00);
+
+	/***********************************************
+	 *              Set for higher rates           *
+	 ***********************************************/
+	/* writing baud rate verbatum into uart clock field clearly not right */
+	if (port_number == 0)
+		sp_reg = MOS7720_SP1_REG;
+	else
+		sp_reg = MOS7720_SP2_REG;
+	write_mos_reg(serial, dummy, sp_reg, baud * 0x10);
+	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x03);
+	mos7720_port->shadowMCR = 0x2b;
+	write_mos_reg(serial, port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	/***********************************************
+	 *              Set DLL/DLM
+	 ***********************************************/
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, MOS7720_DLL, 0x01);
+	write_mos_reg(serial, port_number, MOS7720_DLM, 0x00);
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+
+	return 0;
+}
+
+/* baud rate information */
+struct divisor_table_entry {
+	__u32  baudrate;
+	__u16  divisor;
+};
+
+/* Define table of divisors for moschip 7720 hardware	   *
+ * These assume a 3.6864MHz crystal, the standard /16, and *
+ * MCR.7 = 0.						   */
+static const struct divisor_table_entry divisor_table[] = {
+	{   50,		2304},
+	{   110,	1047},	/* 2094.545455 => 230450   => .0217 % over */
+	{   134,	857},	/* 1713.011152 => 230398.5 => .00065% under */
+	{   150,	768},
+	{   300,	384},
+	{   600,	192},
+	{   1200,	96},
+	{   1800,	64},
+	{   2400,	48},
+	{   4800,	24},
+	{   7200,	16},
+	{   9600,	12},
+	{   19200,	6},
+	{   38400,	3},
+	{   57600,	2},
+	{   115200,	1},
+};
+
+/*****************************************************************************
+ * calc_baud_rate_divisor
+ *	this function calculates the proper baud rate divisor for the specified
+ *	baud rate.
+ *****************************************************************************/
+static int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor)
+{
+	int i;
+	__u16 custom;
+	__u16 round1;
+	__u16 round;
+
+
+	dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate);
+
+	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
+		if (divisor_table[i].baudrate == baudrate) {
+			*divisor = divisor_table[i].divisor;
+			return 0;
+		}
+	}
+
+	/* After trying for all the standard baud rates    *
+	 * Try calculating the divisor for this baud rate  */
+	if (baudrate > 75 &&  baudrate < 230400) {
+		/* get the divisor */
+		custom = (__u16)(230400L  / baudrate);
+
+		/* Check for round off */
+		round1 = (__u16)(2304000L / baudrate);
+		round = (__u16)(round1 - (custom * 10));
+		if (round > 4)
+			custom++;
+		*divisor = custom;
+
+		dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom);
+		return 0;
+	}
+
+	dev_dbg(&port->dev, "Baud calculation Failed...\n");
+	return -EINVAL;
+}
+
+/*
+ * send_cmd_write_baud_rate
+ *	this function sends the proper command to change the baud rate of the
+ *	specified port.
+ */
+static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
+				    int baudrate)
+{
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	int divisor;
+	int status;
+	unsigned char number;
+
+	if (mos7720_port == NULL)
+		return -1;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+
+	number = port->port_number;
+	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate);
+
+	/* Calculate the Divisor */
+	status = calc_baud_rate_divisor(port, baudrate, &divisor);
+	if (status) {
+		dev_err(&port->dev, "%s - bad baud rate\n", __func__);
+		return status;
+	}
+
+	/* Enable access to divisor latch */
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
+	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
+
+	/* Write the divisor */
+	write_mos_reg(serial, number, MOS7720_DLL, (__u8)(divisor & 0xff));
+	write_mos_reg(serial, number, MOS7720_DLM,
+		      (__u8)((divisor & 0xff00) >> 8));
+
+	/* Disable access to divisor latch */
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
+
+	return status;
+}
+
+/*
+ * change_port_settings
+ *	This routine is called to set the UART on the device to match
+ *      the specified new settings.
+ */
+static void change_port_settings(struct tty_struct *tty,
+				 struct moschip_port *mos7720_port,
+				 struct ktermios *old_termios)
+{
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	int baud;
+	unsigned cflag;
+	__u8 lData;
+	__u8 lParity;
+	__u8 lStop;
+	int status;
+	int port_number;
+
+	if (mos7720_port == NULL)
+		return ;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+	port_number = port->port_number;
+
+	if (!mos7720_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	lData = UART_LCR_WLEN8;
+	lStop = 0x00;	/* 1 stop bit */
+	lParity = 0x00;	/* No parity */
+
+	cflag = tty->termios.c_cflag;
+
+	/* Change the number of bits */
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = UART_LCR_WLEN5;
+		break;
+
+	case CS6:
+		lData = UART_LCR_WLEN6;
+		break;
+
+	case CS7:
+		lData = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		lData = UART_LCR_WLEN8;
+		break;
+	}
+
+	/* Change the Parity bit */
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			lParity = UART_LCR_PARITY;
+			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
+		} else {
+			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
+			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
+		}
+
+	} else {
+		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
+	}
+
+	if (cflag & CMSPAR)
+		lParity = lParity | 0x20;
+
+	/* Change the Stop bit */
+	if (cflag & CSTOPB) {
+		lStop = UART_LCR_STOP;
+		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
+	} else {
+		lStop = 0x00;
+		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
+	}
+
+#define LCR_BITS_MASK		0x03	/* Mask for bits/char field */
+#define LCR_STOP_MASK		0x04	/* Mask for stop bits field */
+#define LCR_PAR_MASK		0x38	/* Mask for parity field */
+
+	/* Update the LCR with the correct value */
+	mos7720_port->shadowLCR &=
+		~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+	mos7720_port->shadowLCR |= (lData | lParity | lStop);
+
+
+	/* Disable Interrupts */
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
+	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
+
+	/* Send the updated LCR value to the mos7720 */
+	write_mos_reg(serial, port_number, MOS7720_LCR,
+		      mos7720_port->shadowLCR);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	/* set up the MCR register and send it to the mos7720 */
+	mos7720_port->shadowMCR = UART_MCR_OUT2;
+	if (cflag & CBAUD)
+		mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
+
+	if (cflag & CRTSCTS) {
+		mos7720_port->shadowMCR |= (UART_MCR_XONANY);
+		/* To set hardware flow control to the specified *
+		 * serial port, in SP1/2_CONTROL_REG             */
+		if (port_number)
+			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
+				      0x01);
+		else
+			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
+				      0x02);
+
+	} else
+		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
+
+	write_mos_reg(serial, port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (!baud) {
+		/* pick a default, any default... */
+		dev_dbg(&port->dev, "Picked default baud...\n");
+		baud = 9600;
+	}
+
+	if (baud >= 230400) {
+		set_higher_rates(mos7720_port, baud);
+		/* Enable Interrupts */
+		write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
+		return;
+	}
+
+	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
+	status = send_cmd_write_baud_rate(mos7720_port, baud);
+	/* FIXME: needs to write actual resulting baud back not just
+	   blindly do so */
+	if (cflag & CBAUD)
+		tty_encode_baud_rate(tty, baud, baud);
+	/* Enable Interrupts */
+	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
+		if (status)
+			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
+	}
+}
+
+/*
+ * mos7720_set_termios
+ *	this function is called by the tty driver when it wants to change the
+ *	termios structure.
+ */
+static void mos7720_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	int status;
+	struct moschip_port *mos7720_port;
+
+	mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* change the port settings to the new ones specified */
+	change_port_settings(tty, mos7720_port, old_termios);
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
+		if (status)
+			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
+	}
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct tty_struct *tty,
+		struct moschip_port *mos7720_port, unsigned int __user *value)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int result = 0;
+	unsigned char data = 0;
+	int port_number = port->port_number;
+	int count;
+
+	count = mos7720_chars_in_buffer(tty);
+	if (count == 0) {
+		read_mos_reg(port->serial, port_number, MOS7720_LSR, &data);
+		if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
+					== (UART_LSR_TEMT | UART_LSR_THRE)) {
+			dev_dbg(&port->dev, "%s -- Empty\n", __func__);
+			result = TIOCSER_TEMT;
+		}
+	}
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mos7720_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
+	unsigned int result = 0;
+	unsigned int mcr ;
+	unsigned int msr ;
+
+	mcr = mos7720_port->shadowMCR;
+	msr = mos7720_port->shadowMSR;
+
+	result = ((mcr & UART_MCR_DTR)  ? TIOCM_DTR : 0)   /* 0x002 */
+	  | ((mcr & UART_MCR_RTS)   ? TIOCM_RTS : 0)   /* 0x004 */
+	  | ((msr & UART_MSR_CTS)   ? TIOCM_CTS : 0)   /* 0x020 */
+	  | ((msr & UART_MSR_DCD)   ? TIOCM_CAR : 0)   /* 0x040 */
+	  | ((msr & UART_MSR_RI)    ? TIOCM_RI :  0)   /* 0x080 */
+	  | ((msr & UART_MSR_DSR)   ? TIOCM_DSR : 0);  /* 0x100 */
+
+	return result;
+}
+
+static int mos7720_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
+	unsigned int mcr ;
+
+	mcr = mos7720_port->shadowMCR;
+
+	if (set & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (set & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (set & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	if (clear & TIOCM_RTS)
+		mcr &= ~UART_MCR_RTS;
+	if (clear & TIOCM_DTR)
+		mcr &= ~UART_MCR_DTR;
+	if (clear & TIOCM_LOOP)
+		mcr &= ~UART_MCR_LOOP;
+
+	mos7720_port->shadowMCR = mcr;
+	write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	return 0;
+}
+
+static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,
+			  unsigned int __user *value)
+{
+	unsigned int mcr;
+	unsigned int arg;
+
+	struct usb_serial_port *port;
+
+	if (mos7720_port == NULL)
+		return -1;
+
+	port = (struct usb_serial_port *)mos7720_port->port;
+	mcr = mos7720_port->shadowMCR;
+
+	if (copy_from_user(&arg, value, sizeof(int)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS)
+			mcr |= UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			mcr |= UART_MCR_RTS;
+		if (arg & TIOCM_LOOP)
+			mcr |= UART_MCR_LOOP;
+		break;
+
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			mcr &= ~UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			mcr &= ~UART_MCR_RTS;
+		if (arg & TIOCM_LOOP)
+			mcr &= ~UART_MCR_LOOP;
+		break;
+
+	}
+
+	mos7720_port->shadowMCR = mcr;
+	write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
+		      mos7720_port->shadowMCR);
+
+	return 0;
+}
+
+static int get_serial_info(struct moschip_port *mos7720_port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= mos7720_port->port->minor;
+	tmp.port		= mos7720_port->port->port_number;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mos7720_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	switch (cmd) {
+	case TIOCSERGETLSR:
+		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
+		return get_lsr_info(tty, mos7720_port,
+					(unsigned int __user *)arg);
+
+	/* FIXME: These should be using the mode methods */
+	case TIOCMBIS:
+	case TIOCMBIC:
+		dev_dbg(&port->dev, "%s TIOCMSET/TIOCMBIC/TIOCMSET\n", __func__);
+		return set_modem_info(mos7720_port, cmd,
+				      (unsigned int __user *)arg);
+
+	case TIOCGSERIAL:
+		dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
+		return get_serial_info(mos7720_port,
+				       (struct serial_struct __user *)arg);
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int mos7720_startup(struct usb_serial *serial)
+{
+	struct usb_device *dev;
+	char data;
+	u16 product;
+	int ret_val;
+
+	product = le16_to_cpu(serial->dev->descriptor.idProduct);
+	dev = serial->dev;
+
+	/* setting configuration feature to one */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
+
+	if (product == MOSCHIP_DEVICE_ID_7715) {
+		struct urb *urb = serial->port[0]->interrupt_in_urb;
+
+		urb->complete = mos7715_interrupt_callback;
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+		ret_val = mos7715_parport_init(serial);
+		if (ret_val < 0)
+			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);
+
+	return 0;
+}
+
+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 */
+
+	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);
+
+		/* prevent NULL ptr dereference in port callbacks */
+		spin_lock(&release_lock);
+		mos_parport->pp->private_data = NULL;
+		spin_unlock(&release_lock);
+
+		/* wait for synchronous usb calls to return */
+		if (mos_parport->msg_pending)
+			wait_for_completion_timeout(&mos_parport->syncmsg_compl,
+					    msecs_to_jiffies(MOS_WDR_TIMEOUT));
+
+		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);
+	}
+#endif
+}
+
+static int mos7720_port_probe(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7720_port;
+
+	mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL);
+	if (!mos7720_port)
+		return -ENOMEM;
+
+	mos7720_port->port = port;
+
+	usb_set_serial_port_data(port, mos7720_port);
+
+	return 0;
+}
+
+static int mos7720_port_remove(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7720_port;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	kfree(mos7720_port);
+
+	return 0;
+}
+
+static struct usb_serial_driver moschip7720_2port_driver = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"moschip7720",
+	},
+	.description		= "Moschip 2 port adapter",
+	.id_table		= id_table,
+	.num_bulk_in		= 2,
+	.num_bulk_out		= 2,
+	.num_interrupt_in	= 1,
+	.calc_num_ports		= mos77xx_calc_num_ports,
+	.open			= mos7720_open,
+	.close			= mos7720_close,
+	.throttle		= mos7720_throttle,
+	.unthrottle		= mos7720_unthrottle,
+	.attach			= mos7720_startup,
+	.release		= mos7720_release,
+	.port_probe		= mos7720_port_probe,
+	.port_remove		= mos7720_port_remove,
+	.ioctl			= mos7720_ioctl,
+	.tiocmget		= mos7720_tiocmget,
+	.tiocmset		= mos7720_tiocmset,
+	.set_termios		= mos7720_set_termios,
+	.write			= mos7720_write,
+	.write_room		= mos7720_write_room,
+	.chars_in_buffer	= mos7720_chars_in_buffer,
+	.break_ctl		= mos7720_break,
+	.read_bulk_callback	= mos7720_bulk_in_callback,
+	.read_int_callback	= mos7720_interrupt_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&moschip7720_2port_driver, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
new file mode 100644
index 0000000..b42bad8
--- /dev/null
+++ b/drivers/usb/serial/mos7840.c
@@ -0,0 +1,2398 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Clean ups from Moschip version and a few ioctl implementations by:
+ *	Paul B Schroeder <pschroeder "at" uplogix "dot" com>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ *      Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ *      Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
+
+/*
+ * 16C50 UART register defines
+ */
+
+#define LCR_BITS_5             0x00	/* 5 bits/char */
+#define LCR_BITS_6             0x01	/* 6 bits/char */
+#define LCR_BITS_7             0x02	/* 7 bits/char */
+#define LCR_BITS_8             0x03	/* 8 bits/char */
+#define LCR_BITS_MASK          0x03	/* Mask for bits/char field */
+
+#define LCR_STOP_1             0x00	/* 1 stop bit */
+#define LCR_STOP_1_5           0x04	/* 1.5 stop bits (if 5   bits/char) */
+#define LCR_STOP_2             0x04	/* 2 stop bits   (if 6-8 bits/char) */
+#define LCR_STOP_MASK          0x04	/* Mask for stop bits field */
+
+#define LCR_PAR_NONE           0x00	/* No parity */
+#define LCR_PAR_ODD            0x08	/* Odd parity */
+#define LCR_PAR_EVEN           0x18	/* Even parity */
+#define LCR_PAR_MARK           0x28	/* Force parity bit to 1 */
+#define LCR_PAR_SPACE          0x38	/* Force parity bit to 0 */
+#define LCR_PAR_MASK           0x38	/* Mask for parity field */
+
+#define LCR_SET_BREAK          0x40	/* Set Break condition */
+#define LCR_DL_ENABLE          0x80	/* Enable access to divisor latch */
+
+#define MCR_DTR                0x01	/* Assert DTR */
+#define MCR_RTS                0x02	/* Assert RTS */
+#define MCR_OUT1               0x04	/* Loopback only: Sets state of RI */
+#define MCR_MASTER_IE          0x08	/* Enable interrupt outputs */
+#define MCR_LOOPBACK           0x10	/* Set internal (digital) loopback mode */
+#define MCR_XON_ANY            0x20	/* Enable any char to exit XOFF mode */
+
+#define MOS7840_MSR_CTS        0x10	/* Current state of CTS */
+#define MOS7840_MSR_DSR        0x20	/* Current state of DSR */
+#define MOS7840_MSR_RI         0x40	/* Current state of RI */
+#define MOS7840_MSR_CD         0x80	/* Current state of CD */
+
+/*
+ * Defines used for sending commands to port
+ */
+
+#define MOS_WDR_TIMEOUT		5000	/* default urb timeout */
+
+#define MOS_PORT1       0x0200
+#define MOS_PORT2       0x0300
+#define MOS_VENREG      0x0000
+#define MOS_MAX_PORT	0x02
+#define MOS_WRITE       0x0E
+#define MOS_READ        0x0D
+
+/* Requests */
+#define MCS_RD_RTYPE    0xC0
+#define MCS_WR_RTYPE    0x40
+#define MCS_RDREQ       0x0D
+#define MCS_WRREQ       0x0E
+#define MCS_CTRL_TIMEOUT        500
+#define VENDOR_READ_LENGTH      (0x01)
+
+#define MAX_NAME_LEN    64
+
+#define ZLP_REG1  0x3A		/* Zero_Flag_Reg1    58 */
+#define ZLP_REG5  0x3E		/* Zero_Flag_Reg5    62 */
+
+/* For higher baud Rates use TIOCEXBAUD */
+#define TIOCEXBAUD     0x5462
+
+/* vendor id and device id defines */
+
+/* The native mos7840/7820 component */
+#define USB_VENDOR_ID_MOSCHIP           0x9710
+#define MOSCHIP_DEVICE_ID_7840          0x7840
+#define MOSCHIP_DEVICE_ID_7820          0x7820
+#define MOSCHIP_DEVICE_ID_7810          0x7810
+/* The native component can have its vendor/device id's overridden
+ * in vendor-specific implementations.  Such devices can be handled
+ * by making a change here, in id_table.
+ */
+#define USB_VENDOR_ID_BANDB              0x0856
+#define BANDB_DEVICE_ID_USO9ML2_2        0xAC22
+#define BANDB_DEVICE_ID_USO9ML2_2P       0xBC00
+#define BANDB_DEVICE_ID_USO9ML2_4        0xAC24
+#define BANDB_DEVICE_ID_USO9ML2_4P       0xBC01
+#define BANDB_DEVICE_ID_US9ML2_2         0xAC29
+#define BANDB_DEVICE_ID_US9ML2_4         0xAC30
+#define BANDB_DEVICE_ID_USPTL4_2         0xAC31
+#define BANDB_DEVICE_ID_USPTL4_4         0xAC32
+#define BANDB_DEVICE_ID_USOPTL4_2        0xAC42
+#define BANDB_DEVICE_ID_USOPTL4_2P       0xBC02
+#define BANDB_DEVICE_ID_USOPTL4_4        0xAC44
+#define BANDB_DEVICE_ID_USOPTL4_4P       0xBC03
+#define BANDB_DEVICE_ID_USOPTL2_4        0xAC24
+
+/* This driver also supports
+ * ATEN UC2324 device using Moschip MCS7840
+ * ATEN UC2322 device using Moschip MCS7820
+ */
+#define USB_VENDOR_ID_ATENINTL		0x0557
+#define ATENINTL_DEVICE_ID_UC2324	0x2011
+#define ATENINTL_DEVICE_ID_UC2322	0x7820
+
+/* Interrupt Routine Defines    */
+
+#define SERIAL_IIR_RLS      0x06
+#define SERIAL_IIR_MS       0x00
+
+/*
+ *  Emulation of the bit mask on the LINE STATUS REGISTER.
+ */
+#define SERIAL_LSR_DR       0x0001
+#define SERIAL_LSR_OE       0x0002
+#define SERIAL_LSR_PE       0x0004
+#define SERIAL_LSR_FE       0x0008
+#define SERIAL_LSR_BI       0x0010
+
+#define MOS_MSR_DELTA_CTS   0x10
+#define MOS_MSR_DELTA_DSR   0x20
+#define MOS_MSR_DELTA_RI    0x40
+#define MOS_MSR_DELTA_CD    0x80
+
+/* Serial Port register Address */
+#define INTERRUPT_ENABLE_REGISTER  ((__u16)(0x01))
+#define FIFO_CONTROL_REGISTER      ((__u16)(0x02))
+#define LINE_CONTROL_REGISTER      ((__u16)(0x03))
+#define MODEM_CONTROL_REGISTER     ((__u16)(0x04))
+#define LINE_STATUS_REGISTER       ((__u16)(0x05))
+#define MODEM_STATUS_REGISTER      ((__u16)(0x06))
+#define SCRATCH_PAD_REGISTER       ((__u16)(0x07))
+#define DIVISOR_LATCH_LSB          ((__u16)(0x00))
+#define DIVISOR_LATCH_MSB          ((__u16)(0x01))
+
+#define CLK_MULTI_REGISTER         ((__u16)(0x02))
+#define CLK_START_VALUE_REGISTER   ((__u16)(0x03))
+#define GPIO_REGISTER              ((__u16)(0x07))
+
+#define SERIAL_LCR_DLAB            ((__u16)(0x0080))
+
+/*
+ * URB POOL related defines
+ */
+#define NUM_URBS                        16	/* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE        32	/* URB Size  */
+
+/* LED on/off milliseconds*/
+#define LED_ON_MS	500
+#define LED_OFF_MS	500
+
+enum mos7840_flag {
+	MOS7840_FLAG_CTRL_BUSY,
+	MOS7840_FLAG_LED_BUSY,
+};
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P)},
+	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4)},
+	{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)},
+	{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)},
+	{}			/* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* This structure holds all of the local port information */
+
+struct moschip_port {
+	int port_num;		/*Actual port number in the device(1,2,etc) */
+	struct urb *read_urb;	/* read URB for this port */
+	__u8 shadowLCR;		/* last LCR value received */
+	__u8 shadowMCR;		/* last MCR value received */
+	char open;
+	char open_ports;
+	struct usb_serial_port *port;	/* loop back to the owner of this object */
+
+	/* Offsets */
+	__u8 SpRegOffset;
+	__u8 ControlRegOffset;
+	__u8 DcrRegOffset;
+	/* for processing control URBS in interrupt context */
+	struct urb *control_urb;
+	struct usb_ctrlrequest *dr;
+	char *ctrl_buf;
+	int MsrLsr;
+
+	spinlock_t pool_lock;
+	struct urb *write_urb_pool[NUM_URBS];
+	char busy[NUM_URBS];
+	bool read_urb_busy;
+
+	/* For device(s) with LED indicator */
+	bool has_led;
+	struct timer_list led_timer1;	/* Timer for LED on */
+	struct timer_list led_timer2;	/* Timer for LED off */
+	struct urb *led_urb;
+	struct usb_ctrlrequest *led_dr;
+
+	unsigned long flags;
+};
+
+/*
+ * mos7840_set_reg_sync
+ * 	To set the Control register by calling usb_fill_control_urb function
+ *	by passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
+				__u16 val)
+{
+	struct usb_device *dev = port->serial->dev;
+	val = val & 0x00ff;
+	dev_dbg(&port->dev, "mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
+
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+			       MCS_WR_RTYPE, val, reg, NULL, 0,
+			       MOS_WDR_TIMEOUT);
+}
+
+/*
+ * mos7840_get_reg_sync
+ * 	To set the Uart register by calling usb_fill_control_urb function by
+ *	passing usb_rcvctrlpipe function as parameter.
+ */
+
+static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
+				__u16 *val)
+{
+	struct usb_device *dev = port->serial->dev;
+	int ret = 0;
+	u8 *buf;
+
+	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+			      MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
+			      MOS_WDR_TIMEOUT);
+	if (ret < VENDOR_READ_LENGTH) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+
+	*val = buf[0];
+	dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
+out:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * mos7840_set_uart_reg
+ *	To set the Uart register by calling usb_fill_control_urb function by
+ *	passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
+				__u16 val)
+{
+
+	struct usb_device *dev = port->serial->dev;
+	val = val & 0x00ff;
+	/* For the UART control registers, the application number need
+	   to be Or'ed */
+	if (port->serial->num_ports == 4) {
+		val |= ((__u16)port->port_number + 1) << 8;
+	} else {
+		if (port->port_number == 0) {
+			val |= ((__u16)port->port_number + 1) << 8;
+		} else {
+			val |= ((__u16)port->port_number + 2) << 8;
+		}
+	}
+	dev_dbg(&port->dev, "%s application number is %x\n", __func__, val);
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+			       MCS_WR_RTYPE, val, reg, NULL, 0,
+			       MOS_WDR_TIMEOUT);
+
+}
+
+/*
+ * mos7840_get_uart_reg
+ *	To set the Control register by calling usb_fill_control_urb function
+ *	by passing usb_rcvctrlpipe function as parameter.
+ */
+static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
+				__u16 *val)
+{
+	struct usb_device *dev = port->serial->dev;
+	int ret = 0;
+	__u16 Wval;
+	u8 *buf;
+
+	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Wval  is same as application number */
+	if (port->serial->num_ports == 4) {
+		Wval = ((__u16)port->port_number + 1) << 8;
+	} else {
+		if (port->port_number == 0) {
+			Wval = ((__u16)port->port_number + 1) << 8;
+		} else {
+			Wval = ((__u16)port->port_number + 2) << 8;
+		}
+	}
+	dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval);
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+			      MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
+			      MOS_WDR_TIMEOUT);
+	if (ret < VENDOR_READ_LENGTH) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+	*val = buf[0];
+out:
+	kfree(buf);
+	return ret;
+}
+
+static void mos7840_dump_serial_port(struct usb_serial_port *port,
+				     struct moschip_port *mos7840_port)
+{
+
+	dev_dbg(&port->dev, "SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
+	dev_dbg(&port->dev, "ControlRegOffset is %2x\n", mos7840_port->ControlRegOffset);
+	dev_dbg(&port->dev, "DCRRegOffset is %2x\n", mos7840_port->DcrRegOffset);
+
+}
+
+/************************************************************************/
+/************************************************************************/
+/*             I N T E R F A C E   F U N C T I O N S			*/
+/*             I N T E R F A C E   F U N C T I O N S			*/
+/************************************************************************/
+/************************************************************************/
+
+static inline void mos7840_set_port_private(struct usb_serial_port *port,
+					    struct moschip_port *data)
+{
+	usb_set_serial_port_data(port, (void *)data);
+}
+
+static inline struct moschip_port *mos7840_get_port_private(struct
+							    usb_serial_port
+							    *port)
+{
+	return (struct moschip_port *)usb_get_serial_port_data(port);
+}
+
+static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+{
+	struct moschip_port *mos7840_port;
+	struct async_icount *icount;
+	mos7840_port = port;
+	if (new_msr &
+	    (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
+	     MOS_MSR_DELTA_CD)) {
+		icount = &mos7840_port->port->icount;
+
+		/* update input line counters */
+		if (new_msr & MOS_MSR_DELTA_CTS)
+			icount->cts++;
+		if (new_msr & MOS_MSR_DELTA_DSR)
+			icount->dsr++;
+		if (new_msr & MOS_MSR_DELTA_CD)
+			icount->dcd++;
+		if (new_msr & MOS_MSR_DELTA_RI)
+			icount->rng++;
+
+		wake_up_interruptible(&port->port->port.delta_msr_wait);
+	}
+}
+
+static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+{
+	struct async_icount *icount;
+
+	if (new_lsr & SERIAL_LSR_BI) {
+		/*
+		 * Parity and Framing errors only count if they
+		 * occur exclusive of a break being
+		 * received.
+		 */
+		new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
+	}
+
+	/* update input line counters */
+	icount = &port->port->icount;
+	if (new_lsr & SERIAL_LSR_BI)
+		icount->brk++;
+	if (new_lsr & SERIAL_LSR_OE)
+		icount->overrun++;
+	if (new_lsr & SERIAL_LSR_PE)
+		icount->parity++;
+	if (new_lsr & SERIAL_LSR_FE)
+		icount->frame++;
+}
+
+/************************************************************************/
+/************************************************************************/
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/************************************************************************/
+/************************************************************************/
+
+static void mos7840_control_callback(struct urb *urb)
+{
+	unsigned char *data;
+	struct moschip_port *mos7840_port;
+	struct device *dev = &urb->dev->dev;
+	__u8 regval = 0x0;
+	int status = urb->status;
+
+	mos7840_port = urb->context;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
+		goto out;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
+		goto out;
+	}
+
+	dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
+	if (urb->actual_length < 1)
+		goto out;
+
+	dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__,
+		mos7840_port->MsrLsr, mos7840_port->port_num);
+	data = urb->transfer_buffer;
+	regval = (__u8) data[0];
+	dev_dbg(dev, "%s data is %x\n", __func__, regval);
+	if (mos7840_port->MsrLsr == 0)
+		mos7840_handle_new_msr(mos7840_port, regval);
+	else if (mos7840_port->MsrLsr == 1)
+		mos7840_handle_new_lsr(mos7840_port, regval);
+out:
+	clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags);
+}
+
+static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
+			   __u16 *val)
+{
+	struct usb_device *dev = mcs->port->serial->dev;
+	struct usb_ctrlrequest *dr = mcs->dr;
+	unsigned char *buffer = mcs->ctrl_buf;
+	int ret;
+
+	if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags))
+		return -EBUSY;
+
+	dr->bRequestType = MCS_RD_RTYPE;
+	dr->bRequest = MCS_RDREQ;
+	dr->wValue = cpu_to_le16(Wval);	/* 0 */
+	dr->wIndex = cpu_to_le16(reg);
+	dr->wLength = cpu_to_le16(2);
+
+	usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
+			     (unsigned char *)dr, buffer, 2,
+			     mos7840_control_callback, mcs);
+	mcs->control_urb->transfer_buffer_length = 2;
+	ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+	if (ret)
+		clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags);
+
+	return ret;
+}
+
+static void mos7840_set_led_callback(struct urb *urb)
+{
+	switch (urb->status) {
+	case 0:
+		/* Success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
+			__func__, urb->status);
+		break;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
+			__func__, urb->status);
+	}
+}
+
+static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
+				__u16 reg)
+{
+	struct usb_device *dev = mcs->port->serial->dev;
+	struct usb_ctrlrequest *dr = mcs->led_dr;
+
+	dr->bRequestType = MCS_WR_RTYPE;
+	dr->bRequest = MCS_WRREQ;
+	dr->wValue = cpu_to_le16(wval);
+	dr->wIndex = cpu_to_le16(reg);
+	dr->wLength = cpu_to_le16(0);
+
+	usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0),
+		(unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);
+
+	usb_submit_urb(mcs->led_urb, GFP_ATOMIC);
+}
+
+static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
+				__u16 val)
+{
+	struct usb_device *dev = port->serial->dev;
+
+	usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE,
+			val, reg, NULL, 0, MOS_WDR_TIMEOUT);
+}
+
+static void mos7840_led_off(struct timer_list *t)
+{
+	struct moschip_port *mcs = from_timer(mcs, t, led_timer1);
+
+	/* Turn off LED */
+	mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER);
+	mod_timer(&mcs->led_timer2,
+				jiffies + msecs_to_jiffies(LED_OFF_MS));
+}
+
+static void mos7840_led_flag_off(struct timer_list *t)
+{
+	struct moschip_port *mcs = from_timer(mcs, t, led_timer2);
+
+	clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
+}
+
+static void mos7840_led_activity(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
+
+	if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags))
+		return;
+
+	mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER);
+	mod_timer(&mos7840_port->led_timer1,
+				jiffies + msecs_to_jiffies(LED_ON_MS));
+}
+
+/*****************************************************************************
+ * mos7840_interrupt_callback
+ *	this is the callback function for when we have received data on the
+ *	interrupt endpoint.
+ *****************************************************************************/
+
+static void mos7840_interrupt_callback(struct urb *urb)
+{
+	int result;
+	int length;
+	struct moschip_port *mos7840_port;
+	struct usb_serial *serial;
+	__u16 Data;
+	unsigned char *data;
+	__u8 sp[5], st;
+	int i, rv = 0;
+	__u16 wval, wreg = 0;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	length = urb->actual_length;
+	data = urb->transfer_buffer;
+
+	serial = urb->context;
+
+	/* Moschip get 5 bytes
+	 * Byte 1 IIR Port 1 (port.number is 0)
+	 * Byte 2 IIR Port 2 (port.number is 1)
+	 * Byte 3 IIR Port 3 (port.number is 2)
+	 * Byte 4 IIR Port 4 (port.number is 3)
+	 * Byte 5 FIFO status for both */
+
+	if (length > 5) {
+		dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
+		return;
+	}
+
+	sp[0] = (__u8) data[0];
+	sp[1] = (__u8) data[1];
+	sp[2] = (__u8) data[2];
+	sp[3] = (__u8) data[3];
+	st = (__u8) data[4];
+
+	for (i = 0; i < serial->num_ports; i++) {
+		mos7840_port = mos7840_get_port_private(serial->port[i]);
+		wval = ((__u16)serial->port[i]->port_number + 1) << 8;
+		if (mos7840_port->open) {
+			if (sp[i] & 0x01) {
+				dev_dbg(&urb->dev->dev, "SP%d No Interrupt !!!\n", i);
+			} else {
+				switch (sp[i] & 0x0f) {
+				case SERIAL_IIR_RLS:
+					dev_dbg(&urb->dev->dev, "Serial Port %d: Receiver status error or \n", i);
+					dev_dbg(&urb->dev->dev, "address bit detected in 9-bit mode\n");
+					mos7840_port->MsrLsr = 1;
+					wreg = LINE_STATUS_REGISTER;
+					break;
+				case SERIAL_IIR_MS:
+					dev_dbg(&urb->dev->dev, "Serial Port %d: Modem status change\n", i);
+					mos7840_port->MsrLsr = 0;
+					wreg = MODEM_STATUS_REGISTER;
+					break;
+				}
+				rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
+			}
+		}
+	}
+	if (!(rv < 0))
+		/* the completion handler for the control urb will resubmit */
+		return;
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result) {
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting interrupt urb\n",
+			__func__, result);
+	}
+}
+
+static int mos7840_port_paranoia_check(struct usb_serial_port *port,
+				       const char *function)
+{
+	if (!port) {
+		pr_debug("%s - port == NULL\n", function);
+		return -1;
+	}
+	if (!port->serial) {
+		pr_debug("%s - port->serial == NULL\n", function);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static int mos7840_serial_paranoia_check(struct usb_serial *serial,
+					 const char *function)
+{
+	if (!serial) {
+		pr_debug("%s - serial == NULL\n", function);
+		return -1;
+	}
+	if (!serial->type) {
+		pr_debug("%s - serial->type == NULL!\n", function);
+		return -1;
+	}
+
+	return 0;
+}
+
+static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
+						 const char *function)
+{
+	/* if no port was specified, or it fails a paranoia check */
+	if (!port ||
+	    mos7840_port_paranoia_check(port, function) ||
+	    mos7840_serial_paranoia_check(port->serial, function)) {
+		/* then say that we don't have a valid usb_serial thing,
+		 * which will end up genrating -ENODEV return values */
+		return NULL;
+	}
+
+	return port->serial;
+}
+
+/*****************************************************************************
+ * mos7840_bulk_in_callback
+ *	this is the callback function for when we have received data on the
+ *	bulk in endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_in_callback(struct urb *urb)
+{
+	int retval;
+	unsigned char *data;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	struct moschip_port *mos7840_port;
+	int status = urb->status;
+
+	mos7840_port = urb->context;
+	if (!mos7840_port)
+		return;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	port = mos7840_port->port;
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial) {
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	data = urb->transfer_buffer;
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	if (urb->actual_length) {
+		struct tty_port *tport = &mos7840_port->port->port;
+		tty_insert_flip_string(tport, data, urb->actual_length);
+		tty_flip_buffer_push(tport);
+		port->icount.rx += urb->actual_length;
+		dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx);
+	}
+
+	if (!mos7840_port->read_urb) {
+		dev_dbg(&port->dev, "%s", "URB KILLED !!!\n");
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	if (mos7840_port->has_led)
+		mos7840_led_activity(port);
+
+	mos7840_port->read_urb_busy = true;
+	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+	if (retval) {
+		dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
+		mos7840_port->read_urb_busy = false;
+	}
+}
+
+/*****************************************************************************
+ * mos7840_bulk_out_data_callback
+ *	this is the callback function for when we have finished sending
+ *	serial data on the bulk out endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_out_data_callback(struct urb *urb)
+{
+	struct moschip_port *mos7840_port;
+	struct usb_serial_port *port;
+	int status = urb->status;
+	unsigned long flags;
+	int i;
+
+	mos7840_port = urb->context;
+	port = mos7840_port->port;
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+	for (i = 0; i < NUM_URBS; i++) {
+		if (urb == mos7840_port->write_urb_pool[i]) {
+			mos7840_port->busy[i] = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+
+	if (status) {
+		dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status);
+		return;
+	}
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	if (mos7840_port->open)
+		tty_port_tty_wakeup(&port->port);
+
+}
+
+/************************************************************************/
+/*       D R I V E R  T T Y  I N T E R F A C E  F U N C T I O N S       */
+/************************************************************************/
+
+/*****************************************************************************
+ * mos7840_open
+ *	this function is called by the tty driver when a port is opened
+ *	If successful, we return 0
+ *	Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int response;
+	int j;
+	struct usb_serial *serial;
+	struct urb *urb;
+	__u16 Data;
+	int status;
+	struct moschip_port *mos7840_port;
+	struct moschip_port *port0;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return -ENODEV;
+
+	serial = port->serial;
+
+	if (mos7840_serial_paranoia_check(serial, __func__))
+		return -ENODEV;
+
+	mos7840_port = mos7840_get_port_private(port);
+	port0 = mos7840_get_port_private(serial->port[0]);
+
+	if (mos7840_port == NULL || port0 == NULL)
+		return -ENODEV;
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+	port0->open_ports++;
+
+	/* Initialising the write urb pool */
+	for (j = 0; j < NUM_URBS; ++j) {
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		mos7840_port->write_urb_pool[j] = urb;
+		if (!urb)
+			continue;
+
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+								GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			usb_free_urb(urb);
+			mos7840_port->write_urb_pool[j] = NULL;
+			continue;
+		}
+	}
+
+/*****************************************************************************
+ * Initialize MCS7840 -- Write Init values to corresponding Registers
+ *
+ * Register Index
+ * 1 : IER
+ * 2 : FCR
+ * 3 : LCR
+ * 4 : MCR
+ *
+ * 0x08 : SP1/2 Control Reg
+ *****************************************************************************/
+
+	/* NEED to check the following Block */
+
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Reading Spreg failed\n");
+		goto err;
+	}
+	Data |= 0x80;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "writing Spreg failed\n");
+		goto err;
+	}
+
+	Data &= ~0x80;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "writing Spreg failed\n");
+		goto err;
+	}
+	/* End of block to be checked */
+
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
+									&Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Reading Controlreg failed\n");
+		goto err;
+	}
+	Data |= 0x08;		/* Driver done bit */
+	Data |= 0x20;		/* rx_disable */
+	status = mos7840_set_reg_sync(port,
+				mos7840_port->ControlRegOffset, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "writing Controlreg failed\n");
+		goto err;
+	}
+	/* do register settings here */
+	/* Set all regs to the device default values. */
+	/***********************************
+	 * First Disable all interrupts.
+	 ***********************************/
+	Data = 0x00;
+	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "disabling interrupts failed\n");
+		goto err;
+	}
+	/* Set FIFO_CONTROL_REGISTER to the default value */
+	Data = 0x00;
+	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
+		goto err;
+	}
+
+	Data = 0xcf;
+	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
+		goto err;
+	}
+
+	Data = 0x03;
+	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+	mos7840_port->shadowLCR = Data;
+
+	Data = 0x0b;
+	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+	mos7840_port->shadowMCR = Data;
+
+	Data = 0x00;
+	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+	mos7840_port->shadowLCR = Data;
+
+	Data |= SERIAL_LCR_DLAB;	/* data latch enable in LCR 0x80 */
+	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+	Data = 0x0c;
+	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+	Data = 0x0;
+	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+	Data = 0x00;
+	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+
+	Data = Data & ~SERIAL_LCR_DLAB;
+	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+	mos7840_port->shadowLCR = Data;
+
+	/* clearing Bulkin and Bulkout Fifo */
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+
+	Data = Data | 0x0c;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+
+	Data = Data & ~0x0c;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+	/* Finally enable all interrupts */
+	Data = 0x0c;
+	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+	/* clearing rx_disable */
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
+									&Data);
+	Data = Data & ~0x20;
+	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
+									Data);
+
+	/* rx_negate */
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
+									&Data);
+	Data = Data | 0x10;
+	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
+									Data);
+
+	/* Check to see if we've set up our endpoint info yet    *
+	 * (can't set it up in mos7840_startup as the structures *
+	 * were not set up at that time.)                        */
+	if (port0->open_ports == 1) {
+		/* FIXME: Buffer never NULL, so URB is not submitted. */
+		if (serial->port[0]->interrupt_in_buffer == NULL) {
+			/* set up interrupt urb */
+			usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
+				serial->dev,
+				usb_rcvintpipe(serial->dev,
+				serial->port[0]->interrupt_in_endpointAddress),
+				serial->port[0]->interrupt_in_buffer,
+				serial->port[0]->interrupt_in_urb->
+				transfer_buffer_length,
+				mos7840_interrupt_callback,
+				serial,
+				serial->port[0]->interrupt_in_urb->interval);
+
+			/* start interrupt read for mos7840 */
+			response =
+			    usb_submit_urb(serial->port[0]->interrupt_in_urb,
+					   GFP_KERNEL);
+			if (response) {
+				dev_err(&port->dev, "%s - Error %d submitting "
+					"interrupt urb\n", __func__, response);
+			}
+
+		}
+
+	}
+
+	/* see if we've set up our endpoint info yet   *
+	 * (can't set it up in mos7840_startup as the  *
+	 * structures were not set up at that time.)   */
+
+	dev_dbg(&port->dev, "port number is %d\n", port->port_number);
+	dev_dbg(&port->dev, "minor number is %d\n", port->minor);
+	dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
+	dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
+	dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
+	dev_dbg(&port->dev, "port's number in the device is %d\n", mos7840_port->port_num);
+	mos7840_port->read_urb = port->read_urb;
+
+	/* set up our bulk in urb */
+	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
+		usb_fill_bulk_urb(mos7840_port->read_urb,
+			serial->dev,
+			usb_rcvbulkpipe(serial->dev,
+				(port->bulk_in_endpointAddress) + 2),
+			port->bulk_in_buffer,
+			mos7840_port->read_urb->transfer_buffer_length,
+			mos7840_bulk_in_callback, mos7840_port);
+	} else {
+		usb_fill_bulk_urb(mos7840_port->read_urb,
+			serial->dev,
+			usb_rcvbulkpipe(serial->dev,
+				port->bulk_in_endpointAddress),
+			port->bulk_in_buffer,
+			mos7840_port->read_urb->transfer_buffer_length,
+			mos7840_bulk_in_callback, mos7840_port);
+	}
+
+	dev_dbg(&port->dev, "%s: bulkin endpoint is %d\n", __func__, port->bulk_in_endpointAddress);
+	mos7840_port->read_urb_busy = true;
+	response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+	if (response) {
+		dev_err(&port->dev, "%s - Error %d submitting control urb\n",
+			__func__, response);
+		mos7840_port->read_urb_busy = false;
+	}
+
+	/* initialize our port settings */
+	/* Must set to enable ints! */
+	mos7840_port->shadowMCR = MCR_MASTER_IE;
+	/* send a open port command */
+	mos7840_port->open = 1;
+	/* mos7840_change_port_settings(mos7840_port,old_termios); */
+
+	return 0;
+err:
+	for (j = 0; j < NUM_URBS; ++j) {
+		urb = mos7840_port->write_urb_pool[j];
+		if (!urb)
+			continue;
+		kfree(urb->transfer_buffer);
+		usb_free_urb(urb);
+	}
+	return status;
+}
+
+/*****************************************************************************
+ * mos7840_chars_in_buffer
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we currently have outstanding in the port (data that has
+ *	been written, but hasn't made it out the port yet)
+ *	If successful, we return the number of bytes left to be written in the
+ *	system,
+ *	Otherwise we return zero.
+ *****************************************************************************/
+
+static int mos7840_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int i;
+	int chars = 0;
+	unsigned long flags;
+	struct moschip_port *mos7840_port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return 0;
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL)
+		return 0;
+
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7840_port->busy[i]) {
+			struct urb *urb = mos7840_port->write_urb_pool[i];
+			chars += urb->transfer_buffer_length;
+		}
+	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+
+}
+
+/*****************************************************************************
+ * mos7840_close
+ *	this function is called by the tty driver when a port is closed
+ *****************************************************************************/
+
+static void mos7840_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct moschip_port *mos7840_port;
+	struct moschip_port *port0;
+	int j;
+	__u16 Data;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial)
+		return;
+
+	mos7840_port = mos7840_get_port_private(port);
+	port0 = mos7840_get_port_private(serial->port[0]);
+
+	if (mos7840_port == NULL || port0 == NULL)
+		return;
+
+	for (j = 0; j < NUM_URBS; ++j)
+		usb_kill_urb(mos7840_port->write_urb_pool[j]);
+
+	/* Freeing Write URBs */
+	for (j = 0; j < NUM_URBS; ++j) {
+		if (mos7840_port->write_urb_pool[j]) {
+			kfree(mos7840_port->write_urb_pool[j]->transfer_buffer);
+			usb_free_urb(mos7840_port->write_urb_pool[j]);
+		}
+	}
+
+	usb_kill_urb(mos7840_port->read_urb);
+	mos7840_port->read_urb_busy = false;
+
+	port0->open_ports--;
+	dev_dbg(&port->dev, "%s in close%d\n", __func__, port0->open_ports);
+	if (port0->open_ports == 0) {
+		if (serial->port[0]->interrupt_in_urb) {
+			dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n");
+			usb_kill_urb(serial->port[0]->interrupt_in_urb);
+		}
+	}
+
+	Data = 0x0;
+	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+	Data = 0x00;
+	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+	mos7840_port->open = 0;
+}
+
+/*****************************************************************************
+ * mos7840_break
+ *	this function sends a break to the port
+ *****************************************************************************/
+static void mos7840_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned char data;
+	struct usb_serial *serial;
+	struct moschip_port *mos7840_port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial)
+		return;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (break_state == -1)
+		data = mos7840_port->shadowLCR | LCR_SET_BREAK;
+	else
+		data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
+
+	/* FIXME: no locking on shadowLCR anywhere in driver */
+	mos7840_port->shadowLCR = data;
+	dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
+	mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
+			     mos7840_port->shadowLCR);
+}
+
+/*****************************************************************************
+ * mos7840_write_room
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we can accept for a specific port.
+ *	If successful, we return the amount of room that we have for this port
+ *	Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int i;
+	int room = 0;
+	unsigned long flags;
+	struct moschip_port *mos7840_port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return -1;
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL)
+		return -1;
+
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (!mos7840_port->busy[i])
+			room += URB_TRANSFER_BUFFER_SIZE;
+	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+
+	room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
+	dev_dbg(&mos7840_port->port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+
+}
+
+/*****************************************************************************
+ * mos7840_write
+ *	this function is called by the tty driver when data should be written to
+ *	the port.
+ *	If successful, we return the number of bytes written, otherwise we
+ *      return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
+			 const unsigned char *data, int count)
+{
+	int status;
+	int i;
+	int bytes_sent = 0;
+	int transfer_size;
+	unsigned long flags;
+
+	struct moschip_port *mos7840_port;
+	struct usb_serial *serial;
+	struct urb *urb;
+	/* __u16 Data; */
+	const unsigned char *current_position = data;
+	unsigned char *data1;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return -1;
+
+	serial = port->serial;
+	if (mos7840_serial_paranoia_check(serial, __func__))
+		return -1;
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL)
+		return -1;
+
+	/* try to find a free urb in the list */
+	urb = NULL;
+
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (!mos7840_port->busy[i]) {
+			mos7840_port->busy[i] = 1;
+			urb = mos7840_port->write_urb_pool[i];
+			dev_dbg(&port->dev, "URB:%d\n", i);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+
+	if (urb == NULL) {
+		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
+		goto exit;
+	}
+
+	if (urb->transfer_buffer == NULL) {
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_ATOMIC);
+		if (!urb->transfer_buffer)
+			goto exit;
+	}
+	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+	memcpy(urb->transfer_buffer, current_position, transfer_size);
+
+	/* fill urb with data and submit  */
+	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
+		usb_fill_bulk_urb(urb,
+			serial->dev,
+			usb_sndbulkpipe(serial->dev,
+				(port->bulk_out_endpointAddress) + 2),
+			urb->transfer_buffer,
+			transfer_size,
+			mos7840_bulk_out_data_callback, mos7840_port);
+	} else {
+		usb_fill_bulk_urb(urb,
+			serial->dev,
+			usb_sndbulkpipe(serial->dev,
+				port->bulk_out_endpointAddress),
+			urb->transfer_buffer,
+			transfer_size,
+			mos7840_bulk_out_data_callback, mos7840_port);
+	}
+
+	data1 = urb->transfer_buffer;
+	dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
+
+	if (mos7840_port->has_led)
+		mos7840_led_activity(port);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (status) {
+		mos7840_port->busy[i] = 0;
+		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
+			"with status = %d\n", __func__, status);
+		bytes_sent = status;
+		goto exit;
+	}
+	bytes_sent = transfer_size;
+	port->icount.tx += transfer_size;
+	dev_dbg(&port->dev, "icount.tx is %d:\n", port->icount.tx);
+exit:
+	return bytes_sent;
+
+}
+
+/*****************************************************************************
+ * mos7840_throttle
+ *	this function is called by the tty driver when it wants to stop the data
+ *	being read from the port.
+ *****************************************************************************/
+
+static void mos7840_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7840_port;
+	int status;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dev_dbg(&port->dev, "%s", "port not opened\n");
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the stop character */
+	if (I_IXOFF(tty)) {
+		unsigned char stop_char = STOP_CHAR(tty);
+		status = mos7840_write(tty, port, &stop_char, 1);
+		if (status <= 0)
+			return;
+	}
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		mos7840_port->shadowMCR &= ~MCR_RTS;
+		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+					 mos7840_port->shadowMCR);
+		if (status < 0)
+			return;
+	}
+}
+
+/*****************************************************************************
+ * mos7840_unthrottle
+ *	this function is called by the tty driver when it wants to resume
+ *	the data being read from the port (called after mos7840_throttle is
+ *	called)
+ *****************************************************************************/
+static void mos7840_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int status;
+	struct moschip_port *mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the start character */
+	if (I_IXOFF(tty)) {
+		unsigned char start_char = START_CHAR(tty);
+		status = mos7840_write(tty, port, &start_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (C_CRTSCTS(tty)) {
+		mos7840_port->shadowMCR |= MCR_RTS;
+		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+					 mos7840_port->shadowMCR);
+		if (status < 0)
+			return;
+	}
+}
+
+static int mos7840_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7840_port;
+	unsigned int result;
+	__u16 msr;
+	__u16 mcr;
+	int status;
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return -ENODEV;
+
+	status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+	if (status < 0)
+		return -EIO;
+	status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+	if (status < 0)
+		return -EIO;
+	result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
+	    | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
+	    | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
+	    | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
+	    | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
+	    | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
+	    | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
+
+	dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
+
+	return result;
+}
+
+static int mos7840_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7840_port;
+	unsigned int mcr;
+	int status;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return -ENODEV;
+
+	/* FIXME: What locks the port registers ? */
+	mcr = mos7840_port->shadowMCR;
+	if (clear & TIOCM_RTS)
+		mcr &= ~MCR_RTS;
+	if (clear & TIOCM_DTR)
+		mcr &= ~MCR_DTR;
+	if (clear & TIOCM_LOOP)
+		mcr &= ~MCR_LOOPBACK;
+
+	if (set & TIOCM_RTS)
+		mcr |= MCR_RTS;
+	if (set & TIOCM_DTR)
+		mcr |= MCR_DTR;
+	if (set & TIOCM_LOOP)
+		mcr |= MCR_LOOPBACK;
+
+	mos7840_port->shadowMCR = mcr;
+
+	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
+	if (status < 0) {
+		dev_dbg(&port->dev, "setting MODEM_CONTROL_REGISTER Failed\n");
+		return status;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+ * mos7840_calc_baud_rate_divisor
+ *	this function calculates the proper baud rate divisor for the specified
+ *	baud rate.
+ *****************************************************************************/
+static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
+					  int baudRate, int *divisor,
+					  __u16 *clk_sel_val)
+{
+	dev_dbg(&port->dev, "%s - %d\n", __func__, baudRate);
+
+	if (baudRate <= 115200) {
+		*divisor = 115200 / baudRate;
+		*clk_sel_val = 0x0;
+	}
+	if ((baudRate > 115200) && (baudRate <= 230400)) {
+		*divisor = 230400 / baudRate;
+		*clk_sel_val = 0x10;
+	} else if ((baudRate > 230400) && (baudRate <= 403200)) {
+		*divisor = 403200 / baudRate;
+		*clk_sel_val = 0x20;
+	} else if ((baudRate > 403200) && (baudRate <= 460800)) {
+		*divisor = 460800 / baudRate;
+		*clk_sel_val = 0x30;
+	} else if ((baudRate > 460800) && (baudRate <= 806400)) {
+		*divisor = 806400 / baudRate;
+		*clk_sel_val = 0x40;
+	} else if ((baudRate > 806400) && (baudRate <= 921600)) {
+		*divisor = 921600 / baudRate;
+		*clk_sel_val = 0x50;
+	} else if ((baudRate > 921600) && (baudRate <= 1572864)) {
+		*divisor = 1572864 / baudRate;
+		*clk_sel_val = 0x60;
+	} else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
+		*divisor = 3145728 / baudRate;
+		*clk_sel_val = 0x70;
+	}
+	return 0;
+}
+
+/*****************************************************************************
+ * mos7840_send_cmd_write_baud_rate
+ *	this function sends the proper command to change the baud rate of the
+ *	specified port.
+ *****************************************************************************/
+
+static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
+					    int baudRate)
+{
+	int divisor = 0;
+	int status;
+	__u16 Data;
+	unsigned char number;
+	__u16 clk_sel_val;
+	struct usb_serial_port *port;
+
+	if (mos7840_port == NULL)
+		return -1;
+
+	port = mos7840_port->port;
+	if (mos7840_port_paranoia_check(port, __func__))
+		return -1;
+
+	if (mos7840_serial_paranoia_check(port->serial, __func__))
+		return -1;
+
+	number = mos7840_port->port->port_number;
+
+	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate);
+	/* reset clk_uart_sel in spregOffset */
+	if (baudRate > 115200) {
+#ifdef HW_flow_control
+		/* NOTE: need to see the pther register to modify */
+		/* setting h/w flow control bit to 1 */
+		Data = 0x2b;
+		mos7840_port->shadowMCR = Data;
+		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+									Data);
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
+			return -1;
+		}
+#endif
+
+	} else {
+#ifdef HW_flow_control
+		/* setting h/w flow control bit to 0 */
+		Data = 0xb;
+		mos7840_port->shadowMCR = Data;
+		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+									Data);
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
+			return -1;
+		}
+#endif
+
+	}
+
+	if (1) {		/* baudRate <= 115200) */
+		clk_sel_val = 0x0;
+		Data = 0x0;
+		status = mos7840_calc_baud_rate_divisor(port, baudRate, &divisor,
+						   &clk_sel_val);
+		status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
+								 &Data);
+		if (status < 0) {
+			dev_dbg(&port->dev, "reading spreg failed in set_serial_baud\n");
+			return -1;
+		}
+		Data = (Data & 0x8f) | clk_sel_val;
+		status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
+								Data);
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
+			return -1;
+		}
+		/* Calculate the Divisor */
+
+		if (status) {
+			dev_err(&port->dev, "%s - bad baud rate\n", __func__);
+			return status;
+		}
+		/* Enable access to divisor latch */
+		Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
+		mos7840_port->shadowLCR = Data;
+		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+		/* Write the divisor */
+		Data = (unsigned char)(divisor & 0xff);
+		dev_dbg(&port->dev, "set_serial_baud Value to write DLL is %x\n", Data);
+		mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+		Data = (unsigned char)((divisor & 0xff00) >> 8);
+		dev_dbg(&port->dev, "set_serial_baud Value to write DLM is %x\n", Data);
+		mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+		/* Disable access to divisor latch */
+		Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
+		mos7840_port->shadowLCR = Data;
+		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+	}
+	return status;
+}
+
+/*****************************************************************************
+ * mos7840_change_port_settings
+ *	This routine is called to set the UART on the device to match
+ *      the specified new settings.
+ *****************************************************************************/
+
+static void mos7840_change_port_settings(struct tty_struct *tty,
+	struct moschip_port *mos7840_port, struct ktermios *old_termios)
+{
+	int baud;
+	unsigned cflag;
+	unsigned iflag;
+	__u8 lData;
+	__u8 lParity;
+	__u8 lStop;
+	int status;
+	__u16 Data;
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+
+	if (mos7840_port == NULL)
+		return;
+
+	port = mos7840_port->port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	if (mos7840_serial_paranoia_check(port->serial, __func__))
+		return;
+
+	serial = port->serial;
+
+	if (!mos7840_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	lData = LCR_BITS_8;
+	lStop = LCR_STOP_1;
+	lParity = LCR_PAR_NONE;
+
+	cflag = tty->termios.c_cflag;
+	iflag = tty->termios.c_iflag;
+
+	/* Change the number of bits */
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = LCR_BITS_5;
+		break;
+
+	case CS6:
+		lData = LCR_BITS_6;
+		break;
+
+	case CS7:
+		lData = LCR_BITS_7;
+		break;
+
+	default:
+	case CS8:
+		lData = LCR_BITS_8;
+		break;
+	}
+
+	/* Change the Parity bit */
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			lParity = LCR_PAR_ODD;
+			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
+		} else {
+			lParity = LCR_PAR_EVEN;
+			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
+		}
+
+	} else {
+		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
+	}
+
+	if (cflag & CMSPAR)
+		lParity = lParity | 0x20;
+
+	/* Change the Stop bit */
+	if (cflag & CSTOPB) {
+		lStop = LCR_STOP_2;
+		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
+	} else {
+		lStop = LCR_STOP_1;
+		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
+	}
+
+	/* Update the LCR with the correct value */
+	mos7840_port->shadowLCR &=
+	    ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+	mos7840_port->shadowLCR |= (lData | lParity | lStop);
+
+	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is %x\n", __func__,
+		mos7840_port->shadowLCR);
+	/* Disable Interrupts */
+	Data = 0x00;
+	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+	Data = 0x00;
+	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+	Data = 0xcf;
+	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+	/* Send the updated LCR value to the mos7840 */
+	Data = mos7840_port->shadowLCR;
+
+	mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+	Data = 0x00b;
+	mos7840_port->shadowMCR = Data;
+	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+	Data = 0x00b;
+	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+	/* set up the MCR register and send it to the mos7840 */
+
+	mos7840_port->shadowMCR = MCR_MASTER_IE;
+	if (cflag & CBAUD)
+		mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+
+	if (cflag & CRTSCTS)
+		mos7840_port->shadowMCR |= (MCR_XON_ANY);
+	else
+		mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
+
+	Data = mos7840_port->shadowMCR;
+	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+
+	if (!baud) {
+		/* pick a default, any default... */
+		dev_dbg(&port->dev, "%s", "Picked default baud...\n");
+		baud = 9600;
+	}
+
+	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
+	status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
+
+	/* Enable Interrupts */
+	Data = 0x0c;
+	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+	if (!mos7840_port->read_urb_busy) {
+		mos7840_port->read_urb_busy = true;
+		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+		if (status) {
+			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
+			    status);
+			mos7840_port->read_urb_busy = false;
+		}
+	}
+	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,
+		mos7840_port->shadowLCR);
+}
+
+/*****************************************************************************
+ * mos7840_set_termios
+ *	this function is called by the tty driver when it wants to change
+ *	the termios structure
+ *****************************************************************************/
+
+static void mos7840_set_termios(struct tty_struct *tty,
+				struct usb_serial_port *port,
+				struct ktermios *old_termios)
+{
+	int status;
+	struct usb_serial *serial;
+	struct moschip_port *mos7840_port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return;
+
+	serial = port->serial;
+
+	if (mos7840_serial_paranoia_check(serial, __func__))
+		return;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
+		return;
+	}
+
+	/* change the port settings to the new ones specified */
+
+	mos7840_change_port_settings(tty, mos7840_port, old_termios);
+
+	if (!mos7840_port->read_urb) {
+		dev_dbg(&port->dev, "%s", "URB KILLED !!!!!\n");
+		return;
+	}
+
+	if (!mos7840_port->read_urb_busy) {
+		mos7840_port->read_urb_busy = true;
+		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+		if (status) {
+			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
+			    status);
+			mos7840_port->read_urb_busy = false;
+		}
+	}
+}
+
+/*****************************************************************************
+ * mos7840_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ *****************************************************************************/
+
+static int mos7840_get_lsr_info(struct tty_struct *tty,
+				unsigned int __user *value)
+{
+	int count;
+	unsigned int result = 0;
+
+	count = mos7840_chars_in_buffer(tty);
+	if (count == 0)
+		result = TIOCSER_TEMT;
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_serial_info
+ *      function to get information about serial port
+ *****************************************************************************/
+
+static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
+				   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (mos7840_port == NULL)
+		return -1;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type = PORT_16550A;
+	tmp.line = mos7840_port->port->minor;
+	tmp.port = mos7840_port->port->port_number;
+	tmp.irq = 0;
+	tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+	tmp.baud_base = 9600;
+	tmp.close_delay = 5 * HZ;
+	tmp.closing_wait = 30 * HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+/*****************************************************************************
+ * SerialIoctl
+ *	this function handles any ioctl calls to the driver
+ *****************************************************************************/
+
+static int mos7840_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	void __user *argp = (void __user *)arg;
+	struct moschip_port *mos7840_port;
+
+	if (mos7840_port_paranoia_check(port, __func__))
+		return -1;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return -1;
+
+	switch (cmd) {
+		/* return number of bytes available */
+
+	case TIOCSERGETLSR:
+		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
+		return mos7840_get_lsr_info(tty, argp);
+
+	case TIOCGSERIAL:
+		dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
+		return mos7840_get_serial_info(mos7840_port, argp);
+
+	case TIOCSSERIAL:
+		dev_dbg(&port->dev, "%s TIOCSSERIAL\n", __func__);
+		break;
+	default:
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int mos7810_check(struct usb_serial *serial)
+{
+	int i, pass_count = 0;
+	u8 *buf;
+	__u16 data = 0, mcr_data = 0;
+	__u16 test_pattern = 0x55AA;
+	int res;
+
+	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
+	if (!buf)
+		return 0;	/* failed to identify 7810 */
+
+	/* Store MCR setting */
+	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+		MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER,
+		buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
+	if (res == VENDOR_READ_LENGTH)
+		mcr_data = *buf;
+
+	for (i = 0; i < 16; i++) {
+		/* Send the 1-bit test pattern out to MCS7810 test pin */
+		usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			MCS_WRREQ, MCS_WR_RTYPE,
+			(0x0300 | (((test_pattern >> i) & 0x0001) << 1)),
+			MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT);
+
+		/* Read the test pattern back */
+		res = usb_control_msg(serial->dev,
+				usb_rcvctrlpipe(serial->dev, 0), MCS_RDREQ,
+				MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
+				VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
+		if (res == VENDOR_READ_LENGTH)
+			data = *buf;
+
+		/* If this is a MCS7810 device, both test patterns must match */
+		if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001)
+			break;
+
+		pass_count++;
+	}
+
+	/* Restore MCR setting */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ,
+		MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL,
+		0, MOS_WDR_TIMEOUT);
+
+	kfree(buf);
+
+	if (pass_count == 16)
+		return 1;
+
+	return 0;
+}
+
+static int mos7840_probe(struct usb_serial *serial,
+				const struct usb_device_id *id)
+{
+	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+	u8 *buf;
+	int device_type;
+
+	if (product == MOSCHIP_DEVICE_ID_7810 ||
+		product == MOSCHIP_DEVICE_ID_7820) {
+		device_type = product;
+		goto out;
+	}
+
+	buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
+			VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
+
+	/* For a MCS7840 device GPIO0 must be set to 1 */
+	if (buf[0] & 0x01)
+		device_type = MOSCHIP_DEVICE_ID_7840;
+	else if (mos7810_check(serial))
+		device_type = MOSCHIP_DEVICE_ID_7810;
+	else
+		device_type = MOSCHIP_DEVICE_ID_7820;
+
+	kfree(buf);
+out:
+	usb_set_serial_data(serial, (void *)(unsigned long)device_type);
+
+	return 0;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	int device_type = (unsigned long)usb_get_serial_data(serial);
+	int num_ports;
+
+	num_ports = (device_type >> 4) & 0x000F;
+
+	/*
+	 * num_ports is currently never zero as device_type is one of
+	 * MOSCHIP_DEVICE_ID_78{1,2,4}0.
+	 */
+	if (num_ports == 0)
+		return -ENODEV;
+
+	if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) {
+		dev_err(&serial->interface->dev, "missing endpoints\n");
+		return -ENODEV;
+	}
+
+	return num_ports;
+}
+
+static int mos7840_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	int device_type = (unsigned long)usb_get_serial_data(serial);
+	struct moschip_port *mos7840_port;
+	int status;
+	int pnum;
+	__u16 Data;
+
+	/* we set up the pointers to the endpoints in the mos7840_open *
+	 * function, as the structures aren't created yet.             */
+
+	pnum = port->port_number;
+
+	dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum);
+	mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
+	if (!mos7840_port)
+		return -ENOMEM;
+
+	/* Initialize all port interrupt end point to port 0 int
+	 * endpoint. Our device has only one interrupt end point
+	 * common to all port */
+
+	mos7840_port->port = port;
+	mos7840_set_port_private(port, mos7840_port);
+	spin_lock_init(&mos7840_port->pool_lock);
+
+	/* minor is not initialised until later by
+	 * usb-serial.c:get_free_serial() and cannot therefore be used
+	 * to index device instances */
+	mos7840_port->port_num = pnum + 1;
+	dev_dbg(&port->dev, "port->minor = %d\n", port->minor);
+	dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num);
+
+	if (mos7840_port->port_num == 1) {
+		mos7840_port->SpRegOffset = 0x0;
+		mos7840_port->ControlRegOffset = 0x1;
+		mos7840_port->DcrRegOffset = 0x4;
+	} else if ((mos7840_port->port_num == 2) && (serial->num_ports == 4)) {
+		mos7840_port->SpRegOffset = 0x8;
+		mos7840_port->ControlRegOffset = 0x9;
+		mos7840_port->DcrRegOffset = 0x16;
+	} else if ((mos7840_port->port_num == 2) && (serial->num_ports == 2)) {
+		mos7840_port->SpRegOffset = 0xa;
+		mos7840_port->ControlRegOffset = 0xb;
+		mos7840_port->DcrRegOffset = 0x19;
+	} else if ((mos7840_port->port_num == 3) && (serial->num_ports == 4)) {
+		mos7840_port->SpRegOffset = 0xa;
+		mos7840_port->ControlRegOffset = 0xb;
+		mos7840_port->DcrRegOffset = 0x19;
+	} else if ((mos7840_port->port_num == 4) && (serial->num_ports == 4)) {
+		mos7840_port->SpRegOffset = 0xc;
+		mos7840_port->ControlRegOffset = 0xd;
+		mos7840_port->DcrRegOffset = 0x1c;
+	}
+	mos7840_dump_serial_port(port, mos7840_port);
+	mos7840_set_port_private(port, mos7840_port);
+
+	/* enable rx_disable bit in control register */
+	status = mos7840_get_reg_sync(port,
+			mos7840_port->ControlRegOffset, &Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status);
+	Data |= 0x08;	/* setting driver done bit */
+	Data |= 0x04;	/* sp1_bit to have cts change reflect in
+			   modem status reg */
+
+	/* Data |= 0x20; //rx_disable bit */
+	status = mos7840_set_reg_sync(port,
+			mos7840_port->ControlRegOffset, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status);
+
+	/* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2
+	   and 0x24 in DCR3 */
+	Data = 0x01;
+	status = mos7840_set_reg_sync(port,
+			(__u16) (mos7840_port->DcrRegOffset + 0), Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status);
+
+	Data = 0x05;
+	status = mos7840_set_reg_sync(port,
+			(__u16) (mos7840_port->DcrRegOffset + 1), Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status);
+
+	Data = 0x24;
+	status = mos7840_set_reg_sync(port,
+			(__u16) (mos7840_port->DcrRegOffset + 2), Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status);
+
+	/* write values in clkstart0x0 and clkmulti 0x20 */
+	Data = 0x0;
+	status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status);
+
+	Data = 0x20;
+	status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status);
+		goto error;
+	} else
+		dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status);
+
+	/* write value 0x0 to scratchpad register */
+	Data = 0x00;
+	status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data);
+	if (status < 0) {
+		dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status);
+		goto out;
+	} else
+		dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status);
+
+	/* Zero Length flag register */
+	if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) {
+		Data = 0xff;
+		status = mos7840_set_reg_sync(port,
+				(__u16) (ZLP_REG1 +
+					((__u16)mos7840_port->port_num)), Data);
+		dev_dbg(&port->dev, "ZLIP offset %x\n",
+				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num)));
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status);
+			goto out;
+		} else
+			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status);
+	} else {
+		Data = 0xff;
+		status = mos7840_set_reg_sync(port,
+				(__u16) (ZLP_REG1 +
+					((__u16)mos7840_port->port_num) - 0x1), Data);
+		dev_dbg(&port->dev, "ZLIP offset %x\n",
+				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1));
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status);
+			goto out;
+		} else
+			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status);
+
+	}
+	mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
+	mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
+	mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest),
+			GFP_KERNEL);
+	if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf ||
+			!mos7840_port->dr) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	mos7840_port->has_led = false;
+
+	/* Initialize LED timers */
+	if (device_type == MOSCHIP_DEVICE_ID_7810) {
+		mos7840_port->has_led = true;
+
+		mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
+		mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
+								GFP_KERNEL);
+		if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
+			status = -ENOMEM;
+			goto error;
+		}
+
+		timer_setup(&mos7840_port->led_timer1, mos7840_led_off, 0);
+		mos7840_port->led_timer1.expires =
+			jiffies + msecs_to_jiffies(LED_ON_MS);
+		timer_setup(&mos7840_port->led_timer2, mos7840_led_flag_off,
+			    0);
+		mos7840_port->led_timer2.expires =
+			jiffies + msecs_to_jiffies(LED_OFF_MS);
+
+		/* Turn off LED */
+		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
+	}
+out:
+	if (pnum == serial->num_ports - 1) {
+		/* Zero Length flag enable */
+		Data = 0x0f;
+		status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
+		if (status < 0) {
+			dev_dbg(&port->dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
+			goto error;
+		} else
+			dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status);
+
+		/* setting configuration feature to one */
+		usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				0x03, 0x00, 0x01, 0x00, NULL, 0x00,
+				MOS_WDR_TIMEOUT);
+	}
+	return 0;
+error:
+	kfree(mos7840_port->led_dr);
+	usb_free_urb(mos7840_port->led_urb);
+	kfree(mos7840_port->dr);
+	kfree(mos7840_port->ctrl_buf);
+	usb_free_urb(mos7840_port->control_urb);
+	kfree(mos7840_port);
+
+	return status;
+}
+
+static int mos7840_port_remove(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7840_port;
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port->has_led) {
+		/* Turn off LED */
+		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
+
+		del_timer_sync(&mos7840_port->led_timer1);
+		del_timer_sync(&mos7840_port->led_timer2);
+
+		usb_kill_urb(mos7840_port->led_urb);
+		usb_free_urb(mos7840_port->led_urb);
+		kfree(mos7840_port->led_dr);
+	}
+	usb_kill_urb(mos7840_port->control_urb);
+	usb_free_urb(mos7840_port->control_urb);
+	kfree(mos7840_port->ctrl_buf);
+	kfree(mos7840_port->dr);
+	kfree(mos7840_port);
+
+	return 0;
+}
+
+static struct usb_serial_driver moschip7840_4port_device = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "mos7840",
+		   },
+	.description = DRIVER_DESC,
+	.id_table = id_table,
+	.num_interrupt_in = 1,
+	.open = mos7840_open,
+	.close = mos7840_close,
+	.write = mos7840_write,
+	.write_room = mos7840_write_room,
+	.chars_in_buffer = mos7840_chars_in_buffer,
+	.throttle = mos7840_throttle,
+	.unthrottle = mos7840_unthrottle,
+	.calc_num_ports = mos7840_calc_num_ports,
+	.probe = mos7840_probe,
+	.ioctl = mos7840_ioctl,
+	.set_termios = mos7840_set_termios,
+	.break_ctl = mos7840_break,
+	.tiocmget = mos7840_tiocmget,
+	.tiocmset = mos7840_tiocmset,
+	.tiocmiwait = usb_serial_generic_tiocmiwait,
+	.get_icount = usb_serial_generic_get_icount,
+	.port_probe = mos7840_port_probe,
+	.port_remove = mos7840_port_remove,
+	.read_bulk_callback = mos7840_bulk_in_callback,
+	.read_int_callback = mos7840_interrupt_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&moschip7840_4port_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
new file mode 100644
index 0000000..2513ee9
--- /dev/null
+++ b/drivers/usb/serial/mxuport.c
@@ -0,0 +1,1325 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	mxuport.c - MOXA UPort series driver
+ *
+ *	Copyright (c) 2006 Moxa Technologies Co., Ltd.
+ *	Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch>
+ *
+ *	Supports the following Moxa USB to serial converters:
+ *	 2 ports : UPort 1250, UPort 1250I
+ *	 4 ports : UPort 1410, UPort 1450, UPort 1450I
+ *	 8 ports : UPort 1610-8, UPort 1650-8
+ *	16 ports : UPort 1610-16, UPort 1650-16
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/unaligned.h>
+
+/* Definitions for the vendor ID and device ID */
+#define MX_USBSERIAL_VID	0x110A
+#define MX_UPORT1250_PID	0x1250
+#define MX_UPORT1251_PID	0x1251
+#define MX_UPORT1410_PID	0x1410
+#define MX_UPORT1450_PID	0x1450
+#define MX_UPORT1451_PID	0x1451
+#define MX_UPORT1618_PID	0x1618
+#define MX_UPORT1658_PID	0x1658
+#define MX_UPORT1613_PID	0x1613
+#define MX_UPORT1653_PID	0x1653
+
+/* Definitions for USB info */
+#define HEADER_SIZE		4
+#define EVENT_LENGTH		8
+#define DOWN_BLOCK_SIZE		64
+
+/* Definitions for firmware info */
+#define VER_ADDR_1		0x20
+#define VER_ADDR_2		0x24
+#define VER_ADDR_3		0x28
+
+/* Definitions for USB vendor request */
+#define RQ_VENDOR_NONE			0x00
+#define RQ_VENDOR_SET_BAUD		0x01 /* Set baud rate */
+#define RQ_VENDOR_SET_LINE		0x02 /* Set line status */
+#define RQ_VENDOR_SET_CHARS		0x03 /* Set Xon/Xoff chars */
+#define RQ_VENDOR_SET_RTS		0x04 /* Set RTS */
+#define RQ_VENDOR_SET_DTR		0x05 /* Set DTR */
+#define RQ_VENDOR_SET_XONXOFF		0x06 /* Set auto Xon/Xoff */
+#define RQ_VENDOR_SET_RX_HOST_EN	0x07 /* Set RX host enable */
+#define RQ_VENDOR_SET_OPEN		0x08 /* Set open/close port */
+#define RQ_VENDOR_PURGE			0x09 /* Purge Rx/Tx buffer */
+#define RQ_VENDOR_SET_MCR		0x0A /* Set MCR register */
+#define RQ_VENDOR_SET_BREAK		0x0B /* Set Break signal */
+
+#define RQ_VENDOR_START_FW_DOWN		0x0C /* Start firmware download */
+#define RQ_VENDOR_STOP_FW_DOWN		0x0D /* Stop firmware download */
+#define RQ_VENDOR_QUERY_FW_READY	0x0E /* Query if new firmware ready */
+
+#define RQ_VENDOR_SET_FIFO_DISABLE	0x0F /* Set fifo disable */
+#define RQ_VENDOR_SET_INTERFACE		0x10 /* Set interface */
+#define RQ_VENDOR_SET_HIGH_PERFOR	0x11 /* Set hi-performance */
+
+#define RQ_VENDOR_ERASE_BLOCK		0x12 /* Erase flash block */
+#define RQ_VENDOR_WRITE_PAGE		0x13 /* Write flash page */
+#define RQ_VENDOR_PREPARE_WRITE		0x14 /* Prepare write flash */
+#define RQ_VENDOR_CONFIRM_WRITE		0x15 /* Confirm write flash */
+#define RQ_VENDOR_LOCATE		0x16 /* Locate the device */
+
+#define RQ_VENDOR_START_ROM_DOWN	0x17 /* Start firmware download */
+#define RQ_VENDOR_ROM_DATA		0x18 /* Rom file data */
+#define RQ_VENDOR_STOP_ROM_DOWN		0x19 /* Stop firmware download */
+#define RQ_VENDOR_FW_DATA		0x20 /* Firmware data */
+
+#define RQ_VENDOR_RESET_DEVICE		0x23 /* Try to reset the device */
+#define RQ_VENDOR_QUERY_FW_CONFIG	0x24
+
+#define RQ_VENDOR_GET_VERSION		0x81 /* Get firmware version */
+#define RQ_VENDOR_GET_PAGE		0x82 /* Read flash page */
+#define RQ_VENDOR_GET_ROM_PROC		0x83 /* Get ROM process state */
+
+#define RQ_VENDOR_GET_INQUEUE		0x84 /* Data in input buffer */
+#define RQ_VENDOR_GET_OUTQUEUE		0x85 /* Data in output buffer */
+
+#define RQ_VENDOR_GET_MSR		0x86 /* Get modem status register */
+
+/* Definitions for UPort event type */
+#define UPORT_EVENT_NONE		0 /* None */
+#define UPORT_EVENT_TXBUF_THRESHOLD	1 /* Tx buffer threshold */
+#define UPORT_EVENT_SEND_NEXT		2 /* Send next */
+#define UPORT_EVENT_MSR			3 /* Modem status */
+#define UPORT_EVENT_LSR			4 /* Line status */
+#define UPORT_EVENT_MCR			5 /* Modem control */
+
+/* Definitions for serial event type */
+#define SERIAL_EV_CTS			0x0008	/* CTS changed state */
+#define SERIAL_EV_DSR			0x0010	/* DSR changed state */
+#define SERIAL_EV_RLSD			0x0020	/* RLSD changed state */
+
+/* Definitions for modem control event type */
+#define SERIAL_EV_XOFF			0x40	/* XOFF received */
+
+/* Definitions for line control of communication */
+#define MX_WORDLENGTH_5			5
+#define MX_WORDLENGTH_6			6
+#define MX_WORDLENGTH_7			7
+#define MX_WORDLENGTH_8			8
+
+#define MX_PARITY_NONE			0
+#define MX_PARITY_ODD			1
+#define MX_PARITY_EVEN			2
+#define MX_PARITY_MARK			3
+#define MX_PARITY_SPACE			4
+
+#define MX_STOP_BITS_1			0
+#define MX_STOP_BITS_1_5		1
+#define MX_STOP_BITS_2			2
+
+#define MX_RTS_DISABLE			0x0
+#define MX_RTS_ENABLE			0x1
+#define MX_RTS_HW			0x2
+#define MX_RTS_NO_CHANGE		0x3 /* Flag, not valid register value*/
+
+#define MX_INT_RS232			0
+#define MX_INT_2W_RS485			1
+#define MX_INT_RS422			2
+#define MX_INT_4W_RS485			3
+
+/* Definitions for holding reason */
+#define MX_WAIT_FOR_CTS			0x0001
+#define MX_WAIT_FOR_DSR			0x0002
+#define MX_WAIT_FOR_DCD			0x0004
+#define MX_WAIT_FOR_XON			0x0008
+#define MX_WAIT_FOR_START_TX		0x0010
+#define MX_WAIT_FOR_UNTHROTTLE		0x0020
+#define MX_WAIT_FOR_LOW_WATER		0x0040
+#define MX_WAIT_FOR_SEND_NEXT		0x0080
+
+#define MX_UPORT_2_PORT			BIT(0)
+#define MX_UPORT_4_PORT			BIT(1)
+#define MX_UPORT_8_PORT			BIT(2)
+#define MX_UPORT_16_PORT		BIT(3)
+
+/* This structure holds all of the local port information */
+struct mxuport_port {
+	u8 mcr_state;		/* Last MCR state */
+	u8 msr_state;		/* Last MSR state */
+	struct mutex mutex;	/* Protects mcr_state */
+	spinlock_t spinlock;	/* Protects msr_state */
+};
+
+/* Table of devices that work with this driver */
+static const struct usb_device_id mxuport_idtable[] = {
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1250_PID),
+	  .driver_info = MX_UPORT_2_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1251_PID),
+	  .driver_info = MX_UPORT_2_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1410_PID),
+	  .driver_info = MX_UPORT_4_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1450_PID),
+	  .driver_info = MX_UPORT_4_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1451_PID),
+	  .driver_info = MX_UPORT_4_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1618_PID),
+	  .driver_info = MX_UPORT_8_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1658_PID),
+	  .driver_info = MX_UPORT_8_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1613_PID),
+	  .driver_info = MX_UPORT_16_PORT },
+	{ USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1653_PID),
+	  .driver_info = MX_UPORT_16_PORT },
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, mxuport_idtable);
+
+/*
+ * Add a four byte header containing the port number and the number of
+ * bytes of data in the message. Return the number of bytes in the
+ * buffer.
+ */
+static int mxuport_prepare_write_buffer(struct usb_serial_port *port,
+					void *dest, size_t size)
+{
+	u8 *buf = dest;
+	int count;
+
+	count = kfifo_out_locked(&port->write_fifo, buf + HEADER_SIZE,
+				 size - HEADER_SIZE,
+				 &port->lock);
+
+	put_unaligned_be16(port->port_number, buf);
+	put_unaligned_be16(count, buf + 2);
+
+	dev_dbg(&port->dev, "%s - size %zd count %d\n", __func__,
+		size, count);
+
+	return count + HEADER_SIZE;
+}
+
+/* Read the given buffer in from the control pipe. */
+static int mxuport_recv_ctrl_urb(struct usb_serial *serial,
+				 u8 request, u16 value, u16 index,
+				 u8 *data, size_t size)
+{
+	int status;
+
+	status = usb_control_msg(serial->dev,
+				 usb_rcvctrlpipe(serial->dev, 0),
+				 request,
+				 (USB_DIR_IN | USB_TYPE_VENDOR |
+				  USB_RECIP_DEVICE), value, index,
+				 data, size,
+				 USB_CTRL_GET_TIMEOUT);
+	if (status < 0) {
+		dev_err(&serial->interface->dev,
+			"%s - usb_control_msg failed (%d)\n",
+			__func__, status);
+		return status;
+	}
+
+	if (status != size) {
+		dev_err(&serial->interface->dev,
+			"%s - short read (%d / %zd)\n",
+			__func__, status, size);
+		return -EIO;
+	}
+
+	return status;
+}
+
+/* Write the given buffer out to the control pipe.  */
+static int mxuport_send_ctrl_data_urb(struct usb_serial *serial,
+				      u8 request,
+				      u16 value, u16 index,
+				      u8 *data, size_t size)
+{
+	int status;
+
+	status = usb_control_msg(serial->dev,
+				 usb_sndctrlpipe(serial->dev, 0),
+				 request,
+				 (USB_DIR_OUT | USB_TYPE_VENDOR |
+				  USB_RECIP_DEVICE), value, index,
+				 data, size,
+				 USB_CTRL_SET_TIMEOUT);
+	if (status < 0) {
+		dev_err(&serial->interface->dev,
+			"%s - usb_control_msg failed (%d)\n",
+			__func__, status);
+		return status;
+	}
+
+	if (status != size) {
+		dev_err(&serial->interface->dev,
+			"%s - short write (%d / %zd)\n",
+			__func__, status, size);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* Send a vendor request without any data */
+static int mxuport_send_ctrl_urb(struct usb_serial *serial,
+				 u8 request, u16 value, u16 index)
+{
+	return mxuport_send_ctrl_data_urb(serial, request, value, index,
+					  NULL, 0);
+}
+
+/*
+ * mxuport_throttle - throttle function of driver
+ *
+ * This function is called by the tty driver when it wants to stop the
+ * data being read from the port. Since all the data comes over one
+ * bulk in endpoint, we cannot stop submitting urbs by setting
+ * port->throttle. Instead tell the device to stop sending us data for
+ * the port.
+ */
+static void mxuport_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN,
+			      0, port->port_number);
+}
+
+/*
+ * mxuport_unthrottle - unthrottle function of driver
+ *
+ * This function is called by the tty driver when it wants to resume
+ * the data being read from the port. Tell the device it can resume
+ * sending us received data from the port.
+ */
+static void mxuport_unthrottle(struct tty_struct *tty)
+{
+
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN,
+			      1, port->port_number);
+}
+
+/*
+ * Processes one chunk of data received for a port.  Mostly a copy of
+ * usb_serial_generic_process_read_urb().
+ */
+static void mxuport_process_read_urb_data(struct usb_serial_port *port,
+					  char *data, int size)
+{
+	int i;
+
+	if (!port->port.console || !port->sysrq) {
+		tty_insert_flip_string(&port->port, data, size);
+	} else {
+		for (i = 0; i < size; i++, data++) {
+			if (!usb_serial_handle_sysrq_char(port, *data))
+				tty_insert_flip_char(&port->port, *data,
+						     TTY_NORMAL);
+		}
+	}
+	tty_flip_buffer_push(&port->port);
+}
+
+static void mxuport_msr_event(struct usb_serial_port *port, u8 buf[4])
+{
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	u8 rcv_msr_hold = buf[2] & 0xF0;
+	u16 rcv_msr_event = get_unaligned_be16(buf);
+	unsigned long flags;
+
+	if (rcv_msr_event == 0)
+		return;
+
+	/* Update MSR status */
+	spin_lock_irqsave(&mxport->spinlock, flags);
+
+	dev_dbg(&port->dev, "%s - current MSR status = 0x%x\n",
+		__func__, mxport->msr_state);
+
+	if (rcv_msr_hold & UART_MSR_CTS) {
+		mxport->msr_state |= UART_MSR_CTS;
+		dev_dbg(&port->dev, "%s - CTS high\n", __func__);
+	} else {
+		mxport->msr_state &= ~UART_MSR_CTS;
+		dev_dbg(&port->dev, "%s - CTS low\n", __func__);
+	}
+
+	if (rcv_msr_hold & UART_MSR_DSR) {
+		mxport->msr_state |= UART_MSR_DSR;
+		dev_dbg(&port->dev, "%s - DSR high\n", __func__);
+	} else {
+		mxport->msr_state &= ~UART_MSR_DSR;
+		dev_dbg(&port->dev, "%s - DSR low\n", __func__);
+	}
+
+	if (rcv_msr_hold & UART_MSR_DCD) {
+		mxport->msr_state |= UART_MSR_DCD;
+		dev_dbg(&port->dev, "%s - DCD high\n", __func__);
+	} else {
+		mxport->msr_state &= ~UART_MSR_DCD;
+		dev_dbg(&port->dev, "%s - DCD low\n", __func__);
+	}
+	spin_unlock_irqrestore(&mxport->spinlock, flags);
+
+	if (rcv_msr_event &
+	    (SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD)) {
+
+		if (rcv_msr_event & SERIAL_EV_CTS) {
+			port->icount.cts++;
+			dev_dbg(&port->dev, "%s - CTS change\n", __func__);
+		}
+
+		if (rcv_msr_event & SERIAL_EV_DSR) {
+			port->icount.dsr++;
+			dev_dbg(&port->dev, "%s - DSR change\n", __func__);
+		}
+
+		if (rcv_msr_event & SERIAL_EV_RLSD) {
+			port->icount.dcd++;
+			dev_dbg(&port->dev, "%s - DCD change\n", __func__);
+		}
+		wake_up_interruptible(&port->port.delta_msr_wait);
+	}
+}
+
+static void mxuport_lsr_event(struct usb_serial_port *port, u8 buf[4])
+{
+	u8 lsr_event = buf[2];
+
+	if (lsr_event & UART_LSR_BI) {
+		port->icount.brk++;
+		dev_dbg(&port->dev, "%s - break error\n", __func__);
+	}
+
+	if (lsr_event & UART_LSR_FE) {
+		port->icount.frame++;
+		dev_dbg(&port->dev, "%s - frame error\n", __func__);
+	}
+
+	if (lsr_event & UART_LSR_PE) {
+		port->icount.parity++;
+		dev_dbg(&port->dev, "%s - parity error\n", __func__);
+	}
+
+	if (lsr_event & UART_LSR_OE) {
+		port->icount.overrun++;
+		dev_dbg(&port->dev, "%s - overrun error\n", __func__);
+	}
+}
+
+/*
+ * When something interesting happens, modem control lines XON/XOFF
+ * etc, the device sends an event. Process these events.
+ */
+static void mxuport_process_read_urb_event(struct usb_serial_port *port,
+					   u8 buf[4], u32 event)
+{
+	dev_dbg(&port->dev, "%s - receive event : %04x\n", __func__, event);
+
+	switch (event) {
+	case UPORT_EVENT_SEND_NEXT:
+		/*
+		 * Sent as part of the flow control on device buffers.
+		 * Not currently used.
+		 */
+		break;
+	case UPORT_EVENT_MSR:
+		mxuport_msr_event(port, buf);
+		break;
+	case UPORT_EVENT_LSR:
+		mxuport_lsr_event(port, buf);
+		break;
+	case UPORT_EVENT_MCR:
+		/*
+		 * Event to indicate a change in XON/XOFF from the
+		 * peer.  Currently not used. We just continue
+		 * sending the device data and it will buffer it if
+		 * needed. This event could be used for flow control
+		 * between the host and the device.
+		 */
+		break;
+	default:
+		dev_dbg(&port->dev, "Unexpected event\n");
+		break;
+	}
+}
+
+/*
+ * One URB can contain data for multiple ports. Demultiplex the data,
+ * checking the port exists, is opened and the message is valid.
+ */
+static void mxuport_process_read_urb_demux_data(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct usb_serial *serial = port->serial;
+	u8 *data = urb->transfer_buffer;
+	u8 *end = data + urb->actual_length;
+	struct usb_serial_port *demux_port;
+	u8 *ch;
+	u16 rcv_port;
+	u16 rcv_len;
+
+	while (data < end) {
+		if (data + HEADER_SIZE > end) {
+			dev_warn(&port->dev, "%s - message with short header\n",
+				 __func__);
+			return;
+		}
+
+		rcv_port = get_unaligned_be16(data);
+		if (rcv_port >= serial->num_ports) {
+			dev_warn(&port->dev, "%s - message for invalid port\n",
+				 __func__);
+			return;
+		}
+
+		demux_port = serial->port[rcv_port];
+		rcv_len = get_unaligned_be16(data + 2);
+		if (!rcv_len || data + HEADER_SIZE + rcv_len > end) {
+			dev_warn(&port->dev, "%s - short data\n", __func__);
+			return;
+		}
+
+		if (tty_port_initialized(&demux_port->port)) {
+			ch = data + HEADER_SIZE;
+			mxuport_process_read_urb_data(demux_port, ch, rcv_len);
+		} else {
+			dev_dbg(&demux_port->dev, "%s - data for closed port\n",
+				__func__);
+		}
+		data += HEADER_SIZE + rcv_len;
+	}
+}
+
+/*
+ * One URB can contain events for multiple ports. Demultiplex the event,
+ * checking the port exists, and is opened.
+ */
+static void mxuport_process_read_urb_demux_event(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct usb_serial *serial = port->serial;
+	u8 *data = urb->transfer_buffer;
+	u8 *end = data + urb->actual_length;
+	struct usb_serial_port *demux_port;
+	u8 *ch;
+	u16 rcv_port;
+	u16 rcv_event;
+
+	while (data < end) {
+		if (data + EVENT_LENGTH > end) {
+			dev_warn(&port->dev, "%s - message with short event\n",
+				 __func__);
+			return;
+		}
+
+		rcv_port = get_unaligned_be16(data);
+		if (rcv_port >= serial->num_ports) {
+			dev_warn(&port->dev, "%s - message for invalid port\n",
+				 __func__);
+			return;
+		}
+
+		demux_port = serial->port[rcv_port];
+		if (tty_port_initialized(&demux_port->port)) {
+			ch = data + HEADER_SIZE;
+			rcv_event = get_unaligned_be16(data + 2);
+			mxuport_process_read_urb_event(demux_port, ch,
+						       rcv_event);
+		} else {
+			dev_dbg(&demux_port->dev,
+				"%s - event for closed port\n", __func__);
+		}
+		data += EVENT_LENGTH;
+	}
+}
+
+/*
+ * This is called when we have received data on the bulk in
+ * endpoint. Depending on which port it was received on, it can
+ * contain serial data or events.
+ */
+static void mxuport_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct usb_serial *serial = port->serial;
+
+	if (port == serial->port[0])
+		mxuport_process_read_urb_demux_data(urb);
+
+	if (port == serial->port[1])
+		mxuport_process_read_urb_demux_event(urb);
+}
+
+/*
+ * Ask the device how many bytes it has queued to be sent out. If
+ * there are none, return true.
+ */
+static bool mxuport_tx_empty(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	bool is_empty = true;
+	u32 txlen;
+	u8 *len_buf;
+	int err;
+
+	len_buf = kzalloc(4, GFP_KERNEL);
+	if (!len_buf)
+		goto out;
+
+	err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_OUTQUEUE, 0,
+				    port->port_number, len_buf, 4);
+	if (err < 0)
+		goto out;
+
+	txlen = get_unaligned_be32(len_buf);
+	dev_dbg(&port->dev, "%s - tx len = %u\n", __func__, txlen);
+
+	if (txlen != 0)
+		is_empty = false;
+
+out:
+	kfree(len_buf);
+	return is_empty;
+}
+
+static int mxuport_set_mcr(struct usb_serial_port *port, u8 mcr_state)
+{
+	struct usb_serial *serial = port->serial;
+	int err;
+
+	dev_dbg(&port->dev, "%s - %02x\n", __func__, mcr_state);
+
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_MCR,
+				    mcr_state, port->port_number);
+	if (err)
+		dev_err(&port->dev, "%s - failed to change MCR\n", __func__);
+
+	return err;
+}
+
+static int mxuport_set_dtr(struct usb_serial_port *port, int on)
+{
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	int err;
+
+	mutex_lock(&mxport->mutex);
+
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_DTR,
+				    !!on, port->port_number);
+	if (!err) {
+		if (on)
+			mxport->mcr_state |= UART_MCR_DTR;
+		else
+			mxport->mcr_state &= ~UART_MCR_DTR;
+	}
+
+	mutex_unlock(&mxport->mutex);
+
+	return err;
+}
+
+static int mxuport_set_rts(struct usb_serial_port *port, u8 state)
+{
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	int err;
+	u8 mcr_state;
+
+	mutex_lock(&mxport->mutex);
+	mcr_state = mxport->mcr_state;
+
+	switch (state) {
+	case MX_RTS_DISABLE:
+		mcr_state &= ~UART_MCR_RTS;
+		break;
+	case MX_RTS_ENABLE:
+		mcr_state |= UART_MCR_RTS;
+		break;
+	case MX_RTS_HW:
+		/*
+		 * Do not update mxport->mcr_state when doing hardware
+		 * flow control.
+		 */
+		break;
+	default:
+		/*
+		 * Should not happen, but somebody might try passing
+		 * MX_RTS_NO_CHANGE, which is not valid.
+		 */
+		err = -EINVAL;
+		goto out;
+	}
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RTS,
+				    state, port->port_number);
+	if (!err)
+		mxport->mcr_state = mcr_state;
+
+out:
+	mutex_unlock(&mxport->mutex);
+
+	return err;
+}
+
+static void mxuport_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	u8 mcr_state;
+	int err;
+
+	mutex_lock(&mxport->mutex);
+	mcr_state = mxport->mcr_state;
+
+	if (on)
+		mcr_state |= (UART_MCR_RTS | UART_MCR_DTR);
+	else
+		mcr_state &= ~(UART_MCR_RTS | UART_MCR_DTR);
+
+	err = mxuport_set_mcr(port, mcr_state);
+	if (!err)
+		mxport->mcr_state = mcr_state;
+
+	mutex_unlock(&mxport->mutex);
+}
+
+static int mxuport_tiocmset(struct tty_struct *tty, unsigned int set,
+			    unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	int err;
+	u8 mcr_state;
+
+	mutex_lock(&mxport->mutex);
+	mcr_state = mxport->mcr_state;
+
+	if (set & TIOCM_RTS)
+		mcr_state |= UART_MCR_RTS;
+
+	if (set & TIOCM_DTR)
+		mcr_state |= UART_MCR_DTR;
+
+	if (clear & TIOCM_RTS)
+		mcr_state &= ~UART_MCR_RTS;
+
+	if (clear & TIOCM_DTR)
+		mcr_state &= ~UART_MCR_DTR;
+
+	err = mxuport_set_mcr(port, mcr_state);
+	if (!err)
+		mxport->mcr_state = mcr_state;
+
+	mutex_unlock(&mxport->mutex);
+
+	return err;
+}
+
+static int mxuport_tiocmget(struct tty_struct *tty)
+{
+	struct mxuport_port *mxport;
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int result;
+	unsigned long flags;
+	unsigned int msr;
+	unsigned int mcr;
+
+	mxport = usb_get_serial_port_data(port);
+
+	mutex_lock(&mxport->mutex);
+	spin_lock_irqsave(&mxport->spinlock, flags);
+
+	msr = mxport->msr_state;
+	mcr = mxport->mcr_state;
+
+	spin_unlock_irqrestore(&mxport->spinlock, flags);
+	mutex_unlock(&mxport->mutex);
+
+	result = (((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) |	/* 0x002 */
+		  ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) |	/* 0x004 */
+		  ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) |	/* 0x020 */
+		  ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) |	/* 0x040 */
+		  ((msr & UART_MSR_RI) ? TIOCM_RI : 0) |	/* 0x080 */
+		  ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0));	/* 0x100 */
+
+	dev_dbg(&port->dev, "%s - 0x%04x\n", __func__, result);
+
+	return result;
+}
+
+static int mxuport_set_termios_flow(struct tty_struct *tty,
+				    struct ktermios *old_termios,
+				    struct usb_serial_port *port,
+				    struct usb_serial *serial)
+{
+	u8 xon = START_CHAR(tty);
+	u8 xoff = STOP_CHAR(tty);
+	int enable;
+	int err;
+	u8 *buf;
+	u8 rts;
+
+	buf = kmalloc(2, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* S/W flow control settings */
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		enable = 1;
+		buf[0] = xon;
+		buf[1] = xoff;
+
+		err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_CHARS,
+						 0, port->port_number,
+						 buf, 2);
+		if (err)
+			goto out;
+
+		dev_dbg(&port->dev, "%s - XON = 0x%02x, XOFF = 0x%02x\n",
+			__func__, xon, xoff);
+	} else {
+		enable = 0;
+	}
+
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_XONXOFF,
+				    enable, port->port_number);
+	if (err)
+		goto out;
+
+	rts = MX_RTS_NO_CHANGE;
+
+	/* H/W flow control settings */
+	if (!old_termios ||
+	    C_CRTSCTS(tty) != (old_termios->c_cflag & CRTSCTS)) {
+		if (C_CRTSCTS(tty))
+			rts = MX_RTS_HW;
+		else
+			rts = MX_RTS_ENABLE;
+	}
+
+	if (C_BAUD(tty)) {
+		if (old_termios && (old_termios->c_cflag & CBAUD) == B0) {
+			/* Raise DTR and RTS */
+			if (C_CRTSCTS(tty))
+				rts = MX_RTS_HW;
+			else
+				rts = MX_RTS_ENABLE;
+			mxuport_set_dtr(port, 1);
+		}
+	} else {
+		/* Drop DTR and RTS */
+		rts = MX_RTS_DISABLE;
+		mxuport_set_dtr(port, 0);
+	}
+
+	if (rts != MX_RTS_NO_CHANGE)
+		err = mxuport_set_rts(port, rts);
+
+out:
+	kfree(buf);
+	return err;
+}
+
+static void mxuport_set_termios(struct tty_struct *tty,
+				struct usb_serial_port *port,
+				struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	u8 *buf;
+	u8 data_bits;
+	u8 stop_bits;
+	u8 parity;
+	int baud;
+	int err;
+
+	if (old_termios &&
+	    !tty_termios_hw_change(&tty->termios, old_termios) &&
+	    tty->termios.c_iflag == old_termios->c_iflag) {
+		dev_dbg(&port->dev, "%s - nothing to change\n", __func__);
+		return;
+	}
+
+	buf = kmalloc(4, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	/* Set data bit of termios */
+	switch (C_CSIZE(tty)) {
+	case CS5:
+		data_bits = MX_WORDLENGTH_5;
+		break;
+	case CS6:
+		data_bits = MX_WORDLENGTH_6;
+		break;
+	case CS7:
+		data_bits = MX_WORDLENGTH_7;
+		break;
+	case CS8:
+	default:
+		data_bits = MX_WORDLENGTH_8;
+		break;
+	}
+
+	/* Set parity of termios */
+	if (C_PARENB(tty)) {
+		if (C_CMSPAR(tty)) {
+			if (C_PARODD(tty))
+				parity = MX_PARITY_MARK;
+			else
+				parity = MX_PARITY_SPACE;
+		} else {
+			if (C_PARODD(tty))
+				parity = MX_PARITY_ODD;
+			else
+				parity = MX_PARITY_EVEN;
+		}
+	} else {
+		parity = MX_PARITY_NONE;
+	}
+
+	/* Set stop bit of termios */
+	if (C_CSTOPB(tty))
+		stop_bits = MX_STOP_BITS_2;
+	else
+		stop_bits = MX_STOP_BITS_1;
+
+	buf[0] = data_bits;
+	buf[1] = parity;
+	buf[2] = stop_bits;
+	buf[3] = 0;
+
+	err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_LINE,
+					 0, port->port_number, buf, 4);
+	if (err)
+		goto out;
+
+	err = mxuport_set_termios_flow(tty, old_termios, port, serial);
+	if (err)
+		goto out;
+
+	baud = tty_get_baud_rate(tty);
+	if (!baud)
+		baud = 9600;
+
+	/* Note: Little Endian */
+	put_unaligned_le32(baud, buf);
+
+	err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_BAUD,
+					 0, port->port_number,
+					 buf, 4);
+	if (err)
+		goto out;
+
+	dev_dbg(&port->dev, "baud_rate	: %d\n", baud);
+	dev_dbg(&port->dev, "data_bits	: %d\n", data_bits);
+	dev_dbg(&port->dev, "parity	: %d\n", parity);
+	dev_dbg(&port->dev, "stop_bits	: %d\n", stop_bits);
+
+out:
+	kfree(buf);
+}
+
+/*
+ * Determine how many ports this device has dynamically.  It will be
+ * called after the probe() callback is called, but before attach().
+ */
+static int mxuport_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	unsigned long features = (unsigned long)usb_get_serial_data(serial);
+	int num_ports;
+	int i;
+
+	if (features & MX_UPORT_2_PORT) {
+		num_ports = 2;
+	} else if (features & MX_UPORT_4_PORT) {
+		num_ports = 4;
+	} else if (features & MX_UPORT_8_PORT) {
+		num_ports = 8;
+	} else if (features & MX_UPORT_16_PORT) {
+		num_ports = 16;
+	} else {
+		dev_warn(&serial->interface->dev,
+				"unknown device, assuming two ports\n");
+		num_ports = 2;
+	}
+
+	/*
+	 * Setup bulk-out endpoint multiplexing. All ports share the same
+	 * bulk-out endpoint.
+	 */
+	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16);
+
+	for (i = 1; i < num_ports; ++i)
+		epds->bulk_out[i] = epds->bulk_out[0];
+
+	epds->num_bulk_out = num_ports;
+
+	return num_ports;
+}
+
+/* Get the version of the firmware currently running. */
+static int mxuport_get_fw_version(struct usb_serial *serial, u32 *version)
+{
+	u8 *ver_buf;
+	int err;
+
+	ver_buf = kzalloc(4, GFP_KERNEL);
+	if (!ver_buf)
+		return -ENOMEM;
+
+	/* Get firmware version from SDRAM */
+	err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_VERSION, 0, 0,
+				    ver_buf, 4);
+	if (err != 4) {
+		err = -EIO;
+		goto out;
+	}
+
+	*version = (ver_buf[0] << 16) | (ver_buf[1] << 8) | ver_buf[2];
+	err = 0;
+out:
+	kfree(ver_buf);
+	return err;
+}
+
+/* Given a firmware blob, download it to the device. */
+static int mxuport_download_fw(struct usb_serial *serial,
+			       const struct firmware *fw_p)
+{
+	u8 *fw_buf;
+	size_t txlen;
+	size_t fwidx;
+	int err;
+
+	fw_buf = kmalloc(DOWN_BLOCK_SIZE, GFP_KERNEL);
+	if (!fw_buf)
+		return -ENOMEM;
+
+	dev_dbg(&serial->interface->dev, "Starting firmware download...\n");
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_START_FW_DOWN, 0, 0);
+	if (err)
+		goto out;
+
+	fwidx = 0;
+	do {
+		txlen = min_t(size_t, (fw_p->size - fwidx), DOWN_BLOCK_SIZE);
+
+		memcpy(fw_buf, &fw_p->data[fwidx], txlen);
+		err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_FW_DATA,
+						 0, 0, fw_buf, txlen);
+		if (err) {
+			mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN,
+					      0, 0);
+			goto out;
+		}
+
+		fwidx += txlen;
+		usleep_range(1000, 2000);
+
+	} while (fwidx < fw_p->size);
+
+	msleep(1000);
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN, 0, 0);
+	if (err)
+		goto out;
+
+	msleep(1000);
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_READY, 0, 0);
+
+out:
+	kfree(fw_buf);
+	return err;
+}
+
+static int mxuport_probe(struct usb_serial *serial,
+			 const struct usb_device_id *id)
+{
+	u16 productid = le16_to_cpu(serial->dev->descriptor.idProduct);
+	const struct firmware *fw_p = NULL;
+	u32 version;
+	int local_ver;
+	char buf[32];
+	int err;
+
+	/* Load our firmware */
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_CONFIG, 0, 0);
+	if (err) {
+		mxuport_send_ctrl_urb(serial, RQ_VENDOR_RESET_DEVICE, 0, 0);
+		return err;
+	}
+
+	err = mxuport_get_fw_version(serial, &version);
+	if (err < 0)
+		return err;
+
+	dev_dbg(&serial->interface->dev, "Device firmware version v%x.%x.%x\n",
+		(version & 0xff0000) >> 16,
+		(version & 0xff00) >> 8,
+		(version & 0xff));
+
+	snprintf(buf, sizeof(buf) - 1, "moxa/moxa-%04x.fw", productid);
+
+	err = request_firmware(&fw_p, buf, &serial->interface->dev);
+	if (err) {
+		dev_warn(&serial->interface->dev, "Firmware %s not found\n",
+			 buf);
+
+		/* Use the firmware already in the device */
+		err = 0;
+	} else {
+		local_ver = ((fw_p->data[VER_ADDR_1] << 16) |
+			     (fw_p->data[VER_ADDR_2] << 8) |
+			     fw_p->data[VER_ADDR_3]);
+		dev_dbg(&serial->interface->dev,
+			"Available firmware version v%x.%x.%x\n",
+			fw_p->data[VER_ADDR_1], fw_p->data[VER_ADDR_2],
+			fw_p->data[VER_ADDR_3]);
+		if (local_ver > version) {
+			err = mxuport_download_fw(serial, fw_p);
+			if (err)
+				goto out;
+			err  = mxuport_get_fw_version(serial, &version);
+			if (err < 0)
+				goto out;
+		}
+	}
+
+	dev_info(&serial->interface->dev,
+		 "Using device firmware version v%x.%x.%x\n",
+		 (version & 0xff0000) >> 16,
+		 (version & 0xff00) >> 8,
+		 (version & 0xff));
+
+	/*
+	 * Contains the features of this hardware. Store away for
+	 * later use, eg, number of ports.
+	 */
+	usb_set_serial_data(serial, (void *)id->driver_info);
+out:
+	if (fw_p)
+		release_firmware(fw_p);
+	return err;
+}
+
+
+static int mxuport_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct mxuport_port *mxport;
+	int err;
+
+	mxport = devm_kzalloc(&port->dev, sizeof(struct mxuport_port),
+			      GFP_KERNEL);
+	if (!mxport)
+		return -ENOMEM;
+
+	mutex_init(&mxport->mutex);
+	spin_lock_init(&mxport->spinlock);
+
+	/* Set the port private data */
+	usb_set_serial_port_data(port, mxport);
+
+	/* Set FIFO (Enable) */
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_FIFO_DISABLE,
+				    0, port->port_number);
+	if (err)
+		return err;
+
+	/* Set transmission mode (Hi-Performance) */
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_HIGH_PERFOR,
+				    0, port->port_number);
+	if (err)
+		return err;
+
+	/* Set interface (RS-232) */
+	return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE,
+				     MX_INT_RS232,
+				     port->port_number);
+}
+
+static int mxuport_attach(struct usb_serial *serial)
+{
+	struct usb_serial_port *port0 = serial->port[0];
+	struct usb_serial_port *port1 = serial->port[1];
+	int err;
+
+	/*
+	 * All data from the ports is received on the first bulk in
+	 * endpoint, with a multiplex header. The second bulk in is
+	 * used for events.
+	 *
+	 * Start to read from the device.
+	 */
+	err = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL);
+	if (err)
+		return err;
+
+	err = usb_serial_generic_submit_read_urbs(port1, GFP_KERNEL);
+	if (err) {
+		usb_serial_generic_close(port0);
+		return err;
+	}
+
+	return 0;
+}
+
+static void mxuport_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *port0 = serial->port[0];
+	struct usb_serial_port *port1 = serial->port[1];
+
+	usb_serial_generic_close(port1);
+	usb_serial_generic_close(port0);
+}
+
+static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct mxuport_port *mxport = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	int err;
+
+	/* Set receive host (enable) */
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN,
+				    1, port->port_number);
+	if (err)
+		return err;
+
+	err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN,
+				    1, port->port_number);
+	if (err) {
+		mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN,
+				      0, port->port_number);
+		return err;
+	}
+
+	/* Initial port termios */
+	if (tty)
+		mxuport_set_termios(tty, port, NULL);
+
+	/*
+	 * TODO: use RQ_VENDOR_GET_MSR, once we know what it
+	 * returns.
+	 */
+	mxport->msr_state = 0;
+
+	return err;
+}
+
+static void mxuport_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+
+	mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN, 0,
+			      port->port_number);
+
+	mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 0,
+			      port->port_number);
+}
+
+/* Send a break to the port. */
+static void mxuport_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	int enable;
+
+	if (break_state == -1) {
+		enable = 1;
+		dev_dbg(&port->dev, "%s - sending break\n", __func__);
+	} else {
+		enable = 0;
+		dev_dbg(&port->dev, "%s - clearing break\n", __func__);
+	}
+
+	mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK,
+			      enable, port->port_number);
+}
+
+static int mxuport_resume(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	int c = 0;
+	int i;
+	int r;
+
+	for (i = 0; i < 2; i++) {
+		port = serial->port[i];
+
+		r = usb_serial_generic_submit_read_urbs(port, GFP_NOIO);
+		if (r < 0)
+			c++;
+	}
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		if (!tty_port_initialized(&port->port))
+			continue;
+
+		r = usb_serial_generic_write_start(port, GFP_NOIO);
+		if (r < 0)
+			c++;
+	}
+
+	return c ? -EIO : 0;
+}
+
+static struct usb_serial_driver mxuport_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"mxuport",
+	},
+	.description		= "MOXA UPort",
+	.id_table		= mxuport_idtable,
+	.num_bulk_in		= 2,
+	.num_bulk_out		= 1,
+	.probe			= mxuport_probe,
+	.port_probe		= mxuport_port_probe,
+	.attach			= mxuport_attach,
+	.release		= mxuport_release,
+	.calc_num_ports		= mxuport_calc_num_ports,
+	.open			= mxuport_open,
+	.close			= mxuport_close,
+	.set_termios		= mxuport_set_termios,
+	.break_ctl		= mxuport_break_ctl,
+	.tx_empty		= mxuport_tx_empty,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.throttle		= mxuport_throttle,
+	.unthrottle		= mxuport_unthrottle,
+	.tiocmget		= mxuport_tiocmget,
+	.tiocmset		= mxuport_tiocmset,
+	.dtr_rts		= mxuport_dtr_rts,
+	.process_read_urb	= mxuport_process_read_urb,
+	.prepare_write_buffer	= mxuport_prepare_write_buffer,
+	.resume			= mxuport_resume,
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+	&mxuport_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, mxuport_idtable);
+
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_AUTHOR("<support@moxa.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
new file mode 100644
index 0000000..20277c5
--- /dev/null
+++ b/drivers/usb/serial/navman.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Navman Serial USB driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * TODO:
+ *	Add termios method that uses copy_hw but also kills all echo
+ *	flags as the navman is rx only so cannot echo.
+ */
+
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x0a99, 0x0001) },	/* Talon Technology device */
+	{ USB_DEVICE(0x0df7, 0x0900) },	/* Mobile Action i-gotU */
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static void navman_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	int result;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	if (urb->actual_length) {
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting interrupt urb\n",
+			__func__, result);
+}
+
+static int navman_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result = 0;
+
+	if (port->interrupt_in_urb) {
+		dev_dbg(&port->dev, "%s - adding interrupt input for treo\n",
+			__func__);
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (result)
+			dev_err(&port->dev,
+				"%s - failed submitting interrupt urb, error %d\n",
+				__func__, result);
+	}
+	return result;
+}
+
+static void navman_close(struct usb_serial_port *port)
+{
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int navman_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	/*
+	 * This device can't write any data, only read from the device
+	 */
+	return -EOPNOTSUPP;
+}
+
+static struct usb_serial_driver navman_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"navman",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.open =			navman_open,
+	.close = 		navman_close,
+	.write = 		navman_write,
+	.read_int_callback =	navman_read_int_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&navman_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
new file mode 100644
index 0000000..e51c946
--- /dev/null
+++ b/drivers/usb/serial/omninet.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB ZyXEL omni.net LCD PLUS driver
+ *
+ * Copyright (C) 2013,2017 Johan Hovold <johan@kernel.org>
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * Please report both successes and troubles to the author at omninet@kroah.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_AUTHOR "Alessandro Zummo"
+#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
+
+#define ZYXEL_VENDOR_ID		0x0586
+#define ZYXEL_OMNINET_ID	0x1000
+/* This one seems to be a re-branded ZyXEL device */
+#define BT_IGNITIONPRO_ID	0x2000
+
+/* function prototypes */
+static void omninet_process_read_urb(struct urb *urb);
+static int omninet_prepare_write_buffer(struct usb_serial_port *port,
+				void *buf, size_t count);
+static int omninet_calc_num_ports(struct usb_serial *serial,
+				struct usb_serial_endpoints *epds);
+static int omninet_port_probe(struct usb_serial_port *port);
+static int omninet_port_remove(struct usb_serial_port *port);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
+	{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
+	{ }						/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver zyxel_omninet_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"omninet",
+	},
+	.description =		"ZyXEL - omni.net lcd plus usb",
+	.id_table =		id_table,
+	.num_bulk_out =		2,
+	.calc_num_ports =	omninet_calc_num_ports,
+	.port_probe =		omninet_port_probe,
+	.port_remove =		omninet_port_remove,
+	.process_read_urb =	omninet_process_read_urb,
+	.prepare_write_buffer =	omninet_prepare_write_buffer,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&zyxel_omninet_device, NULL
+};
+
+
+/*
+ * The protocol.
+ *
+ * The omni.net always exchange 64 bytes of data with the host. The first
+ * four bytes are the control header.
+ *
+ * oh_seq is a sequence number. Don't know if/how it's used.
+ * oh_len is the length of the data bytes in the packet.
+ * oh_xxx Bit-mapped, related to handshaking and status info.
+ *	I normally set it to 0x03 in transmitted frames.
+ *	7: Active when the TA is in a CONNECTed state.
+ *	6: unknown
+ *	5: handshaking, unknown
+ *	4: handshaking, unknown
+ *	3: unknown, usually 0
+ *	2: unknown, usually 0
+ *	1: handshaking, unknown, usually set to 1 in transmitted frames
+ *	0: handshaking, unknown, usually set to 1 in transmitted frames
+ * oh_pad Probably a pad byte.
+ *
+ * After the header you will find data bytes if oh_len was greater than zero.
+ */
+struct omninet_header {
+	__u8	oh_seq;
+	__u8	oh_len;
+	__u8	oh_xxx;
+	__u8	oh_pad;
+};
+
+struct omninet_data {
+	__u8	od_outseq;	/* Sequence number for bulk_out URBs */
+};
+
+static int omninet_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	/* We need only the second bulk-out for our single-port device. */
+	epds->bulk_out[0] = epds->bulk_out[1];
+	epds->num_bulk_out = 1;
+
+	return 1;
+}
+
+static int omninet_port_probe(struct usb_serial_port *port)
+{
+	struct omninet_data *od;
+
+	od = kzalloc(sizeof(*od), GFP_KERNEL);
+	if (!od)
+		return -ENOMEM;
+
+	usb_set_serial_port_data(port, od);
+
+	return 0;
+}
+
+static int omninet_port_remove(struct usb_serial_port *port)
+{
+	struct omninet_data *od;
+
+	od = usb_get_serial_port_data(port);
+	kfree(od);
+
+	return 0;
+}
+
+#define OMNINET_HEADERLEN	4
+#define OMNINET_BULKOUTSIZE	64
+#define OMNINET_PAYLOADSIZE	(OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
+
+static void omninet_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	const struct omninet_header *hdr = urb->transfer_buffer;
+	const unsigned char *data;
+	size_t data_len;
+
+	if (urb->actual_length <= OMNINET_HEADERLEN || !hdr->oh_len)
+		return;
+
+	data = (char *)urb->transfer_buffer + OMNINET_HEADERLEN;
+	data_len = min_t(size_t, urb->actual_length - OMNINET_HEADERLEN,
+								hdr->oh_len);
+	tty_insert_flip_string(&port->port, data, data_len);
+	tty_flip_buffer_push(&port->port);
+}
+
+static int omninet_prepare_write_buffer(struct usb_serial_port *port,
+					void *buf, size_t count)
+{
+	struct omninet_data *od = usb_get_serial_port_data(port);
+	struct omninet_header *header = buf;
+
+	count = min_t(size_t, count, OMNINET_PAYLOADSIZE);
+
+	count = kfifo_out_locked(&port->write_fifo, buf + OMNINET_HEADERLEN,
+			count, &port->lock);
+
+	header->oh_seq = od->od_outseq++;
+	header->oh_len = count;
+	header->oh_xxx = 0x03;
+	header->oh_pad = 0x00;
+
+	/* always 64 bytes */
+	return OMNINET_BULKOUTSIZE;
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
new file mode 100644
index 0000000..caa0746
--- /dev/null
+++ b/drivers/usb/serial/opticon.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Opticon USB barcode to serial driver
+ *
+ * Copyright (C) 2011 - 2012 Johan Hovold <jhovold@gmail.com>
+ * Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com>
+ * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2008 - 2009 Novell Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define CONTROL_RTS			0x02
+#define RESEND_CTS_STATE	0x03
+
+/* max number of write urbs in flight */
+#define URB_UPPER_LIMIT	8
+
+/* This driver works for the Opticon 1D barcode reader
+ * an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */
+#define DRIVER_DESC	"Opticon USB barcode to serial driver (1D)"
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x065a, 0x0009) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* This structure holds all of the individual device information */
+struct opticon_private {
+	spinlock_t lock;	/* protects the following flags */
+	bool rts;
+	bool cts;
+	int outstanding_urbs;
+};
+
+
+static void opticon_process_data_packet(struct usb_serial_port *port,
+					const unsigned char *buf, size_t len)
+{
+	tty_insert_flip_string(&port->port, buf, len);
+	tty_flip_buffer_push(&port->port);
+}
+
+static void opticon_process_status_packet(struct usb_serial_port *port,
+					const unsigned char *buf, size_t len)
+{
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (buf[0] == 0x00)
+		priv->cts = false;
+	else
+		priv->cts = true;
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void opticon_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	const unsigned char *hdr = urb->transfer_buffer;
+	const unsigned char *data = hdr + 2;
+	size_t data_len = urb->actual_length - 2;
+
+	if (urb->actual_length <= 2) {
+		dev_dbg(&port->dev, "malformed packet received: %d bytes\n",
+							urb->actual_length);
+		return;
+	}
+	/*
+	 * Data from the device comes with a 2 byte header:
+	 *
+	 * <0x00><0x00>data...
+	 *      This is real data to be sent to the tty layer
+	 * <0x00><0x01>level
+	 *      This is a CTS level change, the third byte is the CTS
+	 *      value (0 for low, 1 for high).
+	 */
+	if ((hdr[0] == 0x00) && (hdr[1] == 0x00)) {
+		opticon_process_data_packet(port, data, data_len);
+	} else if ((hdr[0] == 0x00) && (hdr[1] == 0x01)) {
+		opticon_process_status_packet(port, data, data_len);
+	} else {
+		dev_dbg(&port->dev, "unknown packet received: %02x %02x\n",
+							hdr[0], hdr[1]);
+	}
+}
+
+static int send_control_msg(struct usb_serial_port *port, u8 requesttype,
+				u8 val)
+{
+	struct usb_serial *serial = port->serial;
+	int retval;
+	u8 *buffer;
+
+	buffer = kzalloc(1, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = val;
+	/* Send the message to the vendor control endpoint
+	 * of the connected device */
+	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				requesttype,
+				USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+				0, 0, buffer, 1, 0);
+	kfree(buffer);
+
+	if (retval < 0)
+		return retval;
+
+	return 0;
+}
+
+static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int res;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->rts = false;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Clear RTS line */
+	send_control_msg(port, CONTROL_RTS, 0);
+
+	/* clear the halt status of the endpoint */
+	usb_clear_halt(port->serial->dev, port->read_urb->pipe);
+
+	res = usb_serial_generic_open(tty, port);
+	if (res)
+		return res;
+
+	/* Request CTS line state, sometimes during opening the current
+	 * CTS state can be missed. */
+	send_control_msg(port, RESEND_CTS_STATE, 1);
+
+	return res;
+}
+
+static void opticon_write_control_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
+	unsigned long flags;
+
+	/* free up the transfer buffer, as usb_free_urb() does not do this */
+	kfree(urb->transfer_buffer);
+
+	/* setup packet may be set if we're using it for writing */
+	kfree(urb->setup_packet);
+
+	if (status)
+		dev_dbg(&port->dev,
+			"%s - non-zero urb status received: %d\n",
+			__func__, status);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	--priv->outstanding_urbs;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	usb_serial_port_softint(port);
+}
+
+static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
+			 const unsigned char *buf, int count)
+{
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	struct urb *urb;
+	unsigned char *buffer;
+	unsigned long flags;
+	int status;
+	struct usb_ctrlrequest *dr;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
+		return 0;
+	}
+	priv->outstanding_urbs++;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	buffer = kmalloc(count, GFP_ATOMIC);
+	if (!buffer) {
+		count = -ENOMEM;
+		goto error_no_buffer;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		count = -ENOMEM;
+		goto error_no_urb;
+	}
+
+	memcpy(buffer, buf, count);
+
+	usb_serial_debug_data(&port->dev, __func__, count, buffer);
+
+	/* The connected devices do not have a bulk write endpoint,
+	 * to transmit data to de barcode device the control endpoint is used */
+	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+	if (!dr) {
+		count = -ENOMEM;
+		goto error_no_dr;
+	}
+
+	dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
+	dr->bRequest = 0x01;
+	dr->wValue = 0;
+	dr->wIndex = 0;
+	dr->wLength = cpu_to_le16(count);
+
+	usb_fill_control_urb(urb, serial->dev,
+		usb_sndctrlpipe(serial->dev, 0),
+		(unsigned char *)dr, buffer, count,
+		opticon_write_control_callback, port);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&port->dev,
+		"%s - usb_submit_urb(write endpoint) failed status = %d\n",
+							__func__, status);
+		count = status;
+		goto error;
+	}
+
+	/* we are done with this urb, so let the host driver
+	 * really free it when it is finished with it */
+	usb_free_urb(urb);
+
+	return count;
+error:
+	kfree(dr);
+error_no_dr:
+	usb_free_urb(urb);
+error_no_urb:
+	kfree(buffer);
+error_no_buffer:
+	spin_lock_irqsave(&priv->lock, flags);
+	--priv->outstanding_urbs;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return count;
+}
+
+static int opticon_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	/*
+	 * We really can take almost anything the user throws at us
+	 * but let's pick a nice big number to tell the tty
+	 * layer that we have lots of free space, unless we don't.
+	 */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
+		return 0;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 2048;
+}
+
+static int opticon_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int result = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->rts)
+		result |= TIOCM_RTS;
+	if (priv->cts)
+		result |= TIOCM_CTS;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(&port->dev, "%s - %x\n", __func__, result);
+	return result;
+}
+
+static int opticon_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	bool rts;
+	bool changed = false;
+	int ret;
+
+	/* We only support RTS so we only handle that */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	rts = priv->rts;
+	if (set & TIOCM_RTS)
+		priv->rts = true;
+	if (clear & TIOCM_RTS)
+		priv->rts = false;
+	changed = rts ^ priv->rts;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (!changed)
+		return 0;
+
+	ret = send_control_msg(port, CONTROL_RTS, !rts);
+	if (ret)
+		return usb_translate_errors(ret);
+
+	return 0;
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *serial)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0x00, sizeof(tmp));
+
+	/* fake emulate a 16550 uart to make userspace code happy */
+	tmp.type		= PORT_16550A;
+	tmp.line		= port->minor;
+	tmp.port		= 0;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= 1024;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(serial, &tmp, sizeof(*serial)))
+		return -EFAULT;
+	return 0;
+}
+
+static int opticon_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *)arg);
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int opticon_port_probe(struct usb_serial_port *port)
+{
+	struct opticon_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int opticon_port_remove(struct usb_serial_port *port)
+{
+	struct opticon_private *priv = usb_get_serial_port_data(port);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct usb_serial_driver opticon_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"opticon",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_bulk_in =		1,
+	.bulk_in_size =		256,
+	.port_probe =		opticon_port_probe,
+	.port_remove =		opticon_port_remove,
+	.open =			opticon_open,
+	.write =		opticon_write,
+	.write_room = 		opticon_write_room,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.ioctl =		opticon_ioctl,
+	.tiocmget =		opticon_tiocmget,
+	.tiocmset =		opticon_tiocmset,
+	.process_read_urb =	opticon_process_read_urb,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&opticon_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
new file mode 100644
index 0000000..17787dc
--- /dev/null
+++ b/drivers/usb/serial/option.c
@@ -0,0 +1,2122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+  USB Driver for GSM modems
+
+  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
+
+  Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
+
+  History: see the git log.
+
+  Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
+
+  This driver exists because the "normal" serial driver doesn't work too well
+  with GSM modems. Issues:
+  - data loss -- one single Receive URB is not nearly enough
+  - nonstandard flow (Option devices) control
+  - controlling the baud rate doesn't make sense
+
+  This driver is named "option" because the most common device it's
+  used for is a PC-Card (with an internal OHCI-USB interface, behind
+  which the GSM interface sits), made by Option Inc.
+
+  Some of the "one port" devices actually exhibit multiple USB instances
+  on the USB bus. This is not a bug, these ports are used for different
+  device features.
+*/
+
+#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
+#define DRIVER_DESC "USB Driver for GSM modems"
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "usb-wwan.h"
+
+/* Function prototypes */
+static int  option_probe(struct usb_serial *serial,
+			const struct usb_device_id *id);
+static int option_attach(struct usb_serial *serial);
+static void option_release(struct usb_serial *serial);
+static void option_instat_callback(struct urb *urb);
+
+/* Vendor and product IDs */
+#define OPTION_VENDOR_ID			0x0AF0
+#define OPTION_PRODUCT_COLT			0x5000
+#define OPTION_PRODUCT_RICOLA			0x6000
+#define OPTION_PRODUCT_RICOLA_LIGHT		0x6100
+#define OPTION_PRODUCT_RICOLA_QUAD		0x6200
+#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT	0x6300
+#define OPTION_PRODUCT_RICOLA_NDIS		0x6050
+#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT	0x6150
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD		0x6250
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT	0x6350
+#define OPTION_PRODUCT_COBRA			0x6500
+#define OPTION_PRODUCT_COBRA_BUS		0x6501
+#define OPTION_PRODUCT_VIPER			0x6600
+#define OPTION_PRODUCT_VIPER_BUS		0x6601
+#define OPTION_PRODUCT_GT_MAX_READY		0x6701
+#define OPTION_PRODUCT_FUJI_MODEM_LIGHT		0x6721
+#define OPTION_PRODUCT_FUJI_MODEM_GT		0x6741
+#define OPTION_PRODUCT_FUJI_MODEM_EX		0x6761
+#define OPTION_PRODUCT_KOI_MODEM		0x6800
+#define OPTION_PRODUCT_SCORPION_MODEM		0x6901
+#define OPTION_PRODUCT_ETNA_MODEM		0x7001
+#define OPTION_PRODUCT_ETNA_MODEM_LITE		0x7021
+#define OPTION_PRODUCT_ETNA_MODEM_GT		0x7041
+#define OPTION_PRODUCT_ETNA_MODEM_EX		0x7061
+#define OPTION_PRODUCT_ETNA_KOI_MODEM		0x7100
+#define OPTION_PRODUCT_GTM380_MODEM		0x7201
+
+#define HUAWEI_VENDOR_ID			0x12D1
+#define HUAWEI_PRODUCT_E173			0x140C
+#define HUAWEI_PRODUCT_E1750			0x1406
+#define HUAWEI_PRODUCT_K4505			0x1464
+#define HUAWEI_PRODUCT_K3765			0x1465
+#define HUAWEI_PRODUCT_K4605			0x14C6
+#define HUAWEI_PRODUCT_E173S6			0x1C07
+
+#define QUANTA_VENDOR_ID			0x0408
+#define QUANTA_PRODUCT_Q101			0xEA02
+#define QUANTA_PRODUCT_Q111			0xEA03
+#define QUANTA_PRODUCT_GLX			0xEA04
+#define QUANTA_PRODUCT_GKE			0xEA05
+#define QUANTA_PRODUCT_GLE			0xEA06
+
+#define NOVATELWIRELESS_VENDOR_ID		0x1410
+
+/* YISO PRODUCTS */
+
+#define YISO_VENDOR_ID				0x0EAB
+#define YISO_PRODUCT_U893			0xC893
+
+/*
+ * NOVATEL WIRELESS PRODUCTS
+ *
+ * Note from Novatel Wireless:
+ * If your Novatel modem does not work on linux, don't
+ * change the option module, but check our website. If
+ * that does not help, contact ddeschepper@nvtl.com
+*/
+/* MERLIN EVDO PRODUCTS */
+#define NOVATELWIRELESS_PRODUCT_V640		0x1100
+#define NOVATELWIRELESS_PRODUCT_V620		0x1110
+#define NOVATELWIRELESS_PRODUCT_V740		0x1120
+#define NOVATELWIRELESS_PRODUCT_V720		0x1130
+
+/* MERLIN HSDPA/HSPA PRODUCTS */
+#define NOVATELWIRELESS_PRODUCT_U730		0x1400
+#define NOVATELWIRELESS_PRODUCT_U740		0x1410
+#define NOVATELWIRELESS_PRODUCT_U870		0x1420
+#define NOVATELWIRELESS_PRODUCT_XU870		0x1430
+#define NOVATELWIRELESS_PRODUCT_X950D		0x1450
+
+/* EXPEDITE PRODUCTS */
+#define NOVATELWIRELESS_PRODUCT_EV620		0x2100
+#define NOVATELWIRELESS_PRODUCT_ES720		0x2110
+#define NOVATELWIRELESS_PRODUCT_E725		0x2120
+#define NOVATELWIRELESS_PRODUCT_ES620		0x2130
+#define NOVATELWIRELESS_PRODUCT_EU730		0x2400
+#define NOVATELWIRELESS_PRODUCT_EU740		0x2410
+#define NOVATELWIRELESS_PRODUCT_EU870D		0x2420
+/* OVATION PRODUCTS */
+#define NOVATELWIRELESS_PRODUCT_MC727		0x4100
+#define NOVATELWIRELESS_PRODUCT_MC950D		0x4400
+/*
+ * Note from Novatel Wireless:
+ * All PID in the 5xxx range are currently reserved for
+ * auto-install CDROMs, and should not be added to this
+ * module.
+ *
+ * #define NOVATELWIRELESS_PRODUCT_U727		0x5010
+ * #define NOVATELWIRELESS_PRODUCT_MC727_NEW	0x5100
+*/
+#define NOVATELWIRELESS_PRODUCT_OVMC760		0x6002
+#define NOVATELWIRELESS_PRODUCT_MC780		0x6010
+#define NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED	0x6000
+#define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED	0x6001
+#define NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED	0x7000
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED	0x7001
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED3	0x7003
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED4	0x7004
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED5	0x7005
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED6	0x7006
+#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED7	0x7007
+#define NOVATELWIRELESS_PRODUCT_MC996D		0x7030
+#define NOVATELWIRELESS_PRODUCT_MF3470		0x7041
+#define NOVATELWIRELESS_PRODUCT_MC547		0x7042
+#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED	0x8000
+#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED	0x8001
+#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED	0x9000
+#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED	0x9001
+#define NOVATELWIRELESS_PRODUCT_E362		0x9010
+#define NOVATELWIRELESS_PRODUCT_E371		0x9011
+#define NOVATELWIRELESS_PRODUCT_U620L		0x9022
+#define NOVATELWIRELESS_PRODUCT_G2		0xA010
+#define NOVATELWIRELESS_PRODUCT_MC551		0xB001
+
+/* AMOI PRODUCTS */
+#define AMOI_VENDOR_ID				0x1614
+#define AMOI_PRODUCT_H01			0x0800
+#define AMOI_PRODUCT_H01A			0x7002
+#define AMOI_PRODUCT_H02			0x0802
+#define AMOI_PRODUCT_SKYPEPHONE_S2		0x0407
+
+#define DELL_VENDOR_ID				0x413C
+
+/* Dell modems */
+#define DELL_PRODUCT_5700_MINICARD		0x8114
+#define DELL_PRODUCT_5500_MINICARD		0x8115
+#define DELL_PRODUCT_5505_MINICARD		0x8116
+#define DELL_PRODUCT_5700_EXPRESSCARD		0x8117
+#define DELL_PRODUCT_5510_EXPRESSCARD		0x8118
+
+#define DELL_PRODUCT_5700_MINICARD_SPRINT	0x8128
+#define DELL_PRODUCT_5700_MINICARD_TELUS	0x8129
+
+#define DELL_PRODUCT_5720_MINICARD_VZW		0x8133
+#define DELL_PRODUCT_5720_MINICARD_SPRINT	0x8134
+#define DELL_PRODUCT_5720_MINICARD_TELUS	0x8135
+#define DELL_PRODUCT_5520_MINICARD_CINGULAR	0x8136
+#define DELL_PRODUCT_5520_MINICARD_GENERIC_L	0x8137
+#define DELL_PRODUCT_5520_MINICARD_GENERIC_I	0x8138
+
+#define DELL_PRODUCT_5730_MINICARD_SPRINT	0x8180
+#define DELL_PRODUCT_5730_MINICARD_TELUS	0x8181
+#define DELL_PRODUCT_5730_MINICARD_VZW		0x8182
+
+#define DELL_PRODUCT_5800_MINICARD_VZW		0x8195  /* Novatel E362 */
+#define DELL_PRODUCT_5800_V2_MINICARD_VZW	0x8196  /* Novatel E362 */
+#define DELL_PRODUCT_5804_MINICARD_ATT		0x819b  /* Novatel E371 */
+
+#define DELL_PRODUCT_5821E			0x81d7
+
+#define KYOCERA_VENDOR_ID			0x0c88
+#define KYOCERA_PRODUCT_KPC650			0x17da
+#define KYOCERA_PRODUCT_KPC680			0x180a
+
+#define ANYDATA_VENDOR_ID			0x16d5
+#define ANYDATA_PRODUCT_ADU_620UW		0x6202
+#define ANYDATA_PRODUCT_ADU_E100A		0x6501
+#define ANYDATA_PRODUCT_ADU_500A		0x6502
+
+#define AXESSTEL_VENDOR_ID			0x1726
+#define AXESSTEL_PRODUCT_MV110H			0x1000
+
+#define BANDRICH_VENDOR_ID			0x1A8D
+#define BANDRICH_PRODUCT_C100_1			0x1002
+#define BANDRICH_PRODUCT_C100_2			0x1003
+#define BANDRICH_PRODUCT_1004			0x1004
+#define BANDRICH_PRODUCT_1005			0x1005
+#define BANDRICH_PRODUCT_1006			0x1006
+#define BANDRICH_PRODUCT_1007			0x1007
+#define BANDRICH_PRODUCT_1008			0x1008
+#define BANDRICH_PRODUCT_1009			0x1009
+#define BANDRICH_PRODUCT_100A			0x100a
+
+#define BANDRICH_PRODUCT_100B			0x100b
+#define BANDRICH_PRODUCT_100C			0x100c
+#define BANDRICH_PRODUCT_100D			0x100d
+#define BANDRICH_PRODUCT_100E			0x100e
+
+#define BANDRICH_PRODUCT_100F			0x100f
+#define BANDRICH_PRODUCT_1010			0x1010
+#define BANDRICH_PRODUCT_1011			0x1011
+#define BANDRICH_PRODUCT_1012			0x1012
+
+#define QUALCOMM_VENDOR_ID			0x05C6
+/* These Quectel products use Qualcomm's vendor ID */
+#define QUECTEL_PRODUCT_UC20			0x9003
+#define QUECTEL_PRODUCT_UC15			0x9090
+/* These u-blox products use Qualcomm's vendor ID */
+#define UBLOX_PRODUCT_R410M			0x90b2
+/* These Yuga products use Qualcomm's vendor ID */
+#define YUGA_PRODUCT_CLM920_NC5			0x9625
+
+#define QUECTEL_VENDOR_ID			0x2c7c
+/* These Quectel products use Quectel's vendor ID */
+#define QUECTEL_PRODUCT_EC21			0x0121
+#define QUECTEL_PRODUCT_EC25			0x0125
+#define QUECTEL_PRODUCT_BG96			0x0296
+#define QUECTEL_PRODUCT_EP06			0x0306
+
+#define CMOTECH_VENDOR_ID			0x16d8
+#define CMOTECH_PRODUCT_6001			0x6001
+#define CMOTECH_PRODUCT_CMU_300			0x6002
+#define CMOTECH_PRODUCT_6003			0x6003
+#define CMOTECH_PRODUCT_6004			0x6004
+#define CMOTECH_PRODUCT_6005			0x6005
+#define CMOTECH_PRODUCT_CGU_628A		0x6006
+#define CMOTECH_PRODUCT_CHE_628S		0x6007
+#define CMOTECH_PRODUCT_CMU_301			0x6008
+#define CMOTECH_PRODUCT_CHU_628			0x6280
+#define CMOTECH_PRODUCT_CHU_628S		0x6281
+#define CMOTECH_PRODUCT_CDU_680			0x6803
+#define CMOTECH_PRODUCT_CDU_685A		0x6804
+#define CMOTECH_PRODUCT_CHU_720S		0x7001
+#define CMOTECH_PRODUCT_7002			0x7002
+#define CMOTECH_PRODUCT_CHU_629K		0x7003
+#define CMOTECH_PRODUCT_7004			0x7004
+#define CMOTECH_PRODUCT_7005			0x7005
+#define CMOTECH_PRODUCT_CGU_629			0x7006
+#define CMOTECH_PRODUCT_CHU_629S		0x700a
+#define CMOTECH_PRODUCT_CHU_720I		0x7211
+#define CMOTECH_PRODUCT_7212			0x7212
+#define CMOTECH_PRODUCT_7213			0x7213
+#define CMOTECH_PRODUCT_7251			0x7251
+#define CMOTECH_PRODUCT_7252			0x7252
+#define CMOTECH_PRODUCT_7253			0x7253
+
+#define TELIT_VENDOR_ID				0x1bc7
+#define TELIT_PRODUCT_UC864E			0x1003
+#define TELIT_PRODUCT_UC864G			0x1004
+#define TELIT_PRODUCT_CC864_DUAL		0x1005
+#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
+#define TELIT_PRODUCT_ME910			0x1100
+#define TELIT_PRODUCT_ME910_DUAL_MODEM		0x1101
+#define TELIT_PRODUCT_LE920			0x1200
+#define TELIT_PRODUCT_LE910			0x1201
+#define TELIT_PRODUCT_LE910_USBCFG4		0x1206
+#define TELIT_PRODUCT_LE920A4_1207		0x1207
+#define TELIT_PRODUCT_LE920A4_1208		0x1208
+#define TELIT_PRODUCT_LE920A4_1211		0x1211
+#define TELIT_PRODUCT_LE920A4_1212		0x1212
+#define TELIT_PRODUCT_LE920A4_1213		0x1213
+#define TELIT_PRODUCT_LE920A4_1214		0x1214
+
+/* ZTE PRODUCTS */
+#define ZTE_VENDOR_ID				0x19d2
+#define ZTE_PRODUCT_MF622			0x0001
+#define ZTE_PRODUCT_MF628			0x0015
+#define ZTE_PRODUCT_MF626			0x0031
+#define ZTE_PRODUCT_ZM8620_X			0x0396
+#define ZTE_PRODUCT_ME3620_MBIM			0x0426
+#define ZTE_PRODUCT_ME3620_X			0x1432
+#define ZTE_PRODUCT_ME3620_L			0x1433
+#define ZTE_PRODUCT_AC2726			0xfff1
+#define ZTE_PRODUCT_MG880			0xfffd
+#define ZTE_PRODUCT_CDMA_TECH			0xfffe
+#define ZTE_PRODUCT_AC8710T			0xffff
+#define ZTE_PRODUCT_MC2718			0xffe8
+#define ZTE_PRODUCT_AD3812			0xffeb
+#define ZTE_PRODUCT_MC2716			0xffed
+
+#define BENQ_VENDOR_ID				0x04a5
+#define BENQ_PRODUCT_H10			0x4068
+
+#define DLINK_VENDOR_ID				0x1186
+#define DLINK_PRODUCT_DWM_652			0x3e04
+#define DLINK_PRODUCT_DWM_652_U5		0xce16
+#define DLINK_PRODUCT_DWM_652_U5A		0xce1e
+
+#define QISDA_VENDOR_ID				0x1da5
+#define QISDA_PRODUCT_H21_4512			0x4512
+#define QISDA_PRODUCT_H21_4523			0x4523
+#define QISDA_PRODUCT_H20_4515			0x4515
+#define QISDA_PRODUCT_H20_4518			0x4518
+#define QISDA_PRODUCT_H20_4519			0x4519
+
+/* TLAYTECH PRODUCTS */
+#define TLAYTECH_VENDOR_ID			0x20B9
+#define TLAYTECH_PRODUCT_TEU800			0x1682
+
+/* TOSHIBA PRODUCTS */
+#define TOSHIBA_VENDOR_ID			0x0930
+#define TOSHIBA_PRODUCT_HSDPA_MINICARD		0x1302
+#define TOSHIBA_PRODUCT_G450			0x0d45
+
+#define ALINK_VENDOR_ID				0x1e0e
+#define SIMCOM_PRODUCT_SIM7100E			0x9001 /* Yes, ALINK_VENDOR_ID */
+#define ALINK_PRODUCT_PH300			0x9100
+#define ALINK_PRODUCT_3GU			0x9200
+
+/* ALCATEL PRODUCTS */
+#define ALCATEL_VENDOR_ID			0x1bbb
+#define ALCATEL_PRODUCT_X060S_X200		0x0000
+#define ALCATEL_PRODUCT_X220_X500D		0x0017
+#define ALCATEL_PRODUCT_L100V			0x011e
+#define ALCATEL_PRODUCT_L800MA			0x0203
+
+#define PIRELLI_VENDOR_ID			0x1266
+#define PIRELLI_PRODUCT_C100_1			0x1002
+#define PIRELLI_PRODUCT_C100_2			0x1003
+#define PIRELLI_PRODUCT_1004			0x1004
+#define PIRELLI_PRODUCT_1005			0x1005
+#define PIRELLI_PRODUCT_1006			0x1006
+#define PIRELLI_PRODUCT_1007			0x1007
+#define PIRELLI_PRODUCT_1008			0x1008
+#define PIRELLI_PRODUCT_1009			0x1009
+#define PIRELLI_PRODUCT_100A			0x100a
+#define PIRELLI_PRODUCT_100B			0x100b
+#define PIRELLI_PRODUCT_100C			0x100c
+#define PIRELLI_PRODUCT_100D			0x100d
+#define PIRELLI_PRODUCT_100E			0x100e
+#define PIRELLI_PRODUCT_100F			0x100f
+#define PIRELLI_PRODUCT_1011			0x1011
+#define PIRELLI_PRODUCT_1012			0x1012
+
+/* Airplus products */
+#define AIRPLUS_VENDOR_ID			0x1011
+#define AIRPLUS_PRODUCT_MCD650			0x3198
+
+/* Longcheer/Longsung vendor ID; makes whitelabel devices that
+ * many other vendors like 4G Systems, Alcatel, ChinaBird,
+ * Mobidata, etc sell under their own brand names.
+ */
+#define LONGCHEER_VENDOR_ID			0x1c9e
+
+/* 4G Systems products */
+/* This is the 4G XS Stick W14 a.k.a. Mobilcom Debitel Surf-Stick *
+ * It seems to contain a Qualcomm QSC6240/6290 chipset            */
+#define FOUR_G_SYSTEMS_PRODUCT_W14		0x9603
+#define FOUR_G_SYSTEMS_PRODUCT_W100		0x9b01
+
+/* Fujisoft products */
+#define FUJISOFT_PRODUCT_FS040U			0x9b02
+
+/* iBall 3.5G connect wireless modem */
+#define IBALL_3_5G_CONNECT			0x9605
+
+/* Zoom */
+#define ZOOM_PRODUCT_4597			0x9607
+
+/* SpeedUp SU9800 usb 3g modem */
+#define SPEEDUP_PRODUCT_SU9800			0x9800
+
+/* Haier products */
+#define HAIER_VENDOR_ID				0x201e
+#define HAIER_PRODUCT_CE81B			0x10f8
+#define HAIER_PRODUCT_CE100			0x2009
+
+/* Gemalto's Cinterion products (formerly Siemens) */
+#define SIEMENS_VENDOR_ID			0x0681
+#define CINTERION_VENDOR_ID			0x1e2d
+#define CINTERION_PRODUCT_HC25_MDMNET		0x0040
+#define CINTERION_PRODUCT_HC25_MDM		0x0047
+#define CINTERION_PRODUCT_HC28_MDMNET		0x004A /* same for HC28J */
+#define CINTERION_PRODUCT_HC28_MDM		0x004C
+#define CINTERION_PRODUCT_EU3_E			0x0051
+#define CINTERION_PRODUCT_EU3_P			0x0052
+#define CINTERION_PRODUCT_PH8			0x0053
+#define CINTERION_PRODUCT_AHXX			0x0055
+#define CINTERION_PRODUCT_PLXX			0x0060
+#define CINTERION_PRODUCT_PH8_2RMNET		0x0082
+#define CINTERION_PRODUCT_PH8_AUDIO		0x0083
+#define CINTERION_PRODUCT_AHXX_2RMNET		0x0084
+#define CINTERION_PRODUCT_AHXX_AUDIO		0x0085
+
+/* Olivetti products */
+#define OLIVETTI_VENDOR_ID			0x0b3c
+#define OLIVETTI_PRODUCT_OLICARD100		0xc000
+#define OLIVETTI_PRODUCT_OLICARD120		0xc001
+#define OLIVETTI_PRODUCT_OLICARD140		0xc002
+#define OLIVETTI_PRODUCT_OLICARD145		0xc003
+#define OLIVETTI_PRODUCT_OLICARD155		0xc004
+#define OLIVETTI_PRODUCT_OLICARD200		0xc005
+#define OLIVETTI_PRODUCT_OLICARD160		0xc00a
+#define OLIVETTI_PRODUCT_OLICARD500		0xc00b
+
+/* Celot products */
+#define CELOT_VENDOR_ID				0x211f
+#define CELOT_PRODUCT_CT680M			0x6801
+
+/* Samsung products */
+#define SAMSUNG_VENDOR_ID                       0x04e8
+#define SAMSUNG_PRODUCT_GT_B3730                0x6889
+
+/* YUGA products  www.yuga-info.com gavin.kx@qq.com */
+#define YUGA_VENDOR_ID				0x257A
+#define YUGA_PRODUCT_CEM600			0x1601
+#define YUGA_PRODUCT_CEM610			0x1602
+#define YUGA_PRODUCT_CEM500			0x1603
+#define YUGA_PRODUCT_CEM510			0x1604
+#define YUGA_PRODUCT_CEM800			0x1605
+#define YUGA_PRODUCT_CEM900			0x1606
+
+#define YUGA_PRODUCT_CEU818			0x1607
+#define YUGA_PRODUCT_CEU816			0x1608
+#define YUGA_PRODUCT_CEU828			0x1609
+#define YUGA_PRODUCT_CEU826			0x160A
+#define YUGA_PRODUCT_CEU518			0x160B
+#define YUGA_PRODUCT_CEU516			0x160C
+#define YUGA_PRODUCT_CEU528			0x160D
+#define YUGA_PRODUCT_CEU526			0x160F
+#define YUGA_PRODUCT_CEU881			0x161F
+#define YUGA_PRODUCT_CEU882			0x162F
+
+#define YUGA_PRODUCT_CWM600			0x2601
+#define YUGA_PRODUCT_CWM610			0x2602
+#define YUGA_PRODUCT_CWM500			0x2603
+#define YUGA_PRODUCT_CWM510			0x2604
+#define YUGA_PRODUCT_CWM800			0x2605
+#define YUGA_PRODUCT_CWM900			0x2606
+
+#define YUGA_PRODUCT_CWU718			0x2607
+#define YUGA_PRODUCT_CWU716			0x2608
+#define YUGA_PRODUCT_CWU728			0x2609
+#define YUGA_PRODUCT_CWU726			0x260A
+#define YUGA_PRODUCT_CWU518			0x260B
+#define YUGA_PRODUCT_CWU516			0x260C
+#define YUGA_PRODUCT_CWU528			0x260D
+#define YUGA_PRODUCT_CWU581			0x260E
+#define YUGA_PRODUCT_CWU526			0x260F
+#define YUGA_PRODUCT_CWU582			0x261F
+#define YUGA_PRODUCT_CWU583			0x262F
+
+#define YUGA_PRODUCT_CLM600			0x3601
+#define YUGA_PRODUCT_CLM610			0x3602
+#define YUGA_PRODUCT_CLM500			0x3603
+#define YUGA_PRODUCT_CLM510			0x3604
+#define YUGA_PRODUCT_CLM800			0x3605
+#define YUGA_PRODUCT_CLM900			0x3606
+
+#define YUGA_PRODUCT_CLU718			0x3607
+#define YUGA_PRODUCT_CLU716			0x3608
+#define YUGA_PRODUCT_CLU728			0x3609
+#define YUGA_PRODUCT_CLU726			0x360A
+#define YUGA_PRODUCT_CLU518			0x360B
+#define YUGA_PRODUCT_CLU516			0x360C
+#define YUGA_PRODUCT_CLU528			0x360D
+#define YUGA_PRODUCT_CLU526			0x360F
+
+/* Viettel products */
+#define VIETTEL_VENDOR_ID			0x2262
+#define VIETTEL_PRODUCT_VT1000			0x0002
+
+/* ZD Incorporated */
+#define ZD_VENDOR_ID				0x0685
+#define ZD_PRODUCT_7000				0x7000
+
+/* LG products */
+#define LG_VENDOR_ID				0x1004
+#define LG_PRODUCT_L02C				0x618f
+
+/* MediaTek products */
+#define MEDIATEK_VENDOR_ID			0x0e8d
+#define MEDIATEK_PRODUCT_DC_1COM		0x00a0
+#define MEDIATEK_PRODUCT_DC_4COM		0x00a5
+#define MEDIATEK_PRODUCT_DC_4COM2		0x00a7
+#define MEDIATEK_PRODUCT_DC_5COM		0x00a4
+#define MEDIATEK_PRODUCT_7208_1COM		0x7101
+#define MEDIATEK_PRODUCT_7208_2COM		0x7102
+#define MEDIATEK_PRODUCT_7103_2COM		0x7103
+#define MEDIATEK_PRODUCT_7106_2COM		0x7106
+#define MEDIATEK_PRODUCT_FP_1COM		0x0003
+#define MEDIATEK_PRODUCT_FP_2COM		0x0023
+#define MEDIATEK_PRODUCT_FPDC_1COM		0x0043
+#define MEDIATEK_PRODUCT_FPDC_2COM		0x0033
+
+/* Cellient products */
+#define CELLIENT_VENDOR_ID			0x2692
+#define CELLIENT_PRODUCT_MEN200			0x9005
+
+/* Hyundai Petatel Inc. products */
+#define PETATEL_VENDOR_ID			0x1ff4
+#define PETATEL_PRODUCT_NP10T_600A		0x600a
+#define PETATEL_PRODUCT_NP10T_600E		0x600e
+
+/* TP-LINK Incorporated products */
+#define TPLINK_VENDOR_ID			0x2357
+#define TPLINK_PRODUCT_LTE			0x000D
+#define TPLINK_PRODUCT_MA180			0x0201
+
+/* Changhong products */
+#define CHANGHONG_VENDOR_ID			0x2077
+#define CHANGHONG_PRODUCT_CH690			0x7001
+
+/* Inovia */
+#define INOVIA_VENDOR_ID			0x20a6
+#define INOVIA_SEW858				0x1105
+
+/* VIA Telecom */
+#define VIATELECOM_VENDOR_ID			0x15eb
+#define VIATELECOM_PRODUCT_CDS7			0x0001
+
+/* WeTelecom products */
+#define WETELECOM_VENDOR_ID			0x22de
+#define WETELECOM_PRODUCT_WMD200		0x6801
+#define WETELECOM_PRODUCT_6802			0x6802
+#define WETELECOM_PRODUCT_WMD300		0x6803
+
+
+/* Device flags */
+
+/* Interface does not support modem-control requests */
+#define NCTRL(ifnum)	((BIT(ifnum) & 0xff) << 8)
+
+/* Interface is reserved */
+#define RSVD(ifnum)	((BIT(ifnum) & 0xff) << 0)
+
+/* Interface must have two endpoints */
+#define NUMEP2		BIT(16)
+
+
+static const struct usb_device_id option_ids[] = {
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
+	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTM380_MODEM) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
+	{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S6, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) | RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) | RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x14ac, 0xff, 0xff, 0xff),	/* Huawei E1820 */
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) | RSVD(2) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x02) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x03) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x04) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x06) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x10) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x12) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x13) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x14) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x15) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x17) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x18) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x19) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x31) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x32) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x33) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x34) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x35) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x36) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x48) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x49) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4C) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x61) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x62) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x63) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x64) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x65) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x66) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6D) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6E) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6F) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x72) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x73) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x74) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x75) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x78) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x79) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7A) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7B) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7C) },
+
+
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V720) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U730) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U740) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U870) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_XU870) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_X950D) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EV620) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_ES720) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E725) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_ES620) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU730) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU740) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU870D) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC780) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED3) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED4) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED5) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED6) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED7) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC996D) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MF3470) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC547) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED) },
+	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G2) },
+	/* Novatel Ovation MC551 a.k.a. Verizon USB551L */
+	{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E362, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E371, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U620L, 0xff, 0x00, 0x00) },
+
+	{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
+	{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
+	{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H02) },
+	{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_SKYPEPHONE_S2) },
+
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD) },		/* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5500_MINICARD) },		/* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5505_MINICARD) },		/* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_EXPRESSCARD) },		/* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5510_EXPRESSCARD) },		/* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_SPRINT) },	/* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_TELUS) },	/* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_VZW) }, 	/* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_SPRINT) }, 	/* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_TELUS) }, 	/* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_CINGULAR) },	/* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_L) },	/* Dell Wireless HSDPA 5520 */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_I) },	/* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_SPRINT) },	/* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_TELUS) },	/* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_VZW) }, 	/* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
+	{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_MINICARD_VZW, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_V2_MINICARD_VZW, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5804_MINICARD_ATT, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E),
+	  .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
+	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) },	/* ADU-E100, ADU-310 */
+	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
+	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },
+	{ USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) },
+	{ USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1005, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1006, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1007, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1008, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1009, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100A, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100B, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100C, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100D, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100E, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100F, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1010, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1011, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012, 0xff) },
+	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) },
+	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, 0x6001, 0xff, 0xff, 0xff), /* 4G LTE usb-modem U901 */
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
+	/* Quectel products using Qualcomm vendor ID */
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
+	  .driver_info = RSVD(4) },
+	/* Yuga products use Qualcomm vendor ID */
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5),
+	  .driver_info = RSVD(1) | RSVD(4) },
+	/* u-blox products using Qualcomm vendor ID */
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, UBLOX_PRODUCT_R410M),
+	  .driver_info = RSVD(1) | RSVD(3) },
+	/* Quectel products using Quectel vendor ID */
+	{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6004) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6005) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_628A) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHE_628S),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_301),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628S) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_680) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_685A) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720S),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7002),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629K),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7004),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7005) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_629),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629S),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720I),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7212),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7213),
+	  .driver_info = RSVD(0) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7251),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7252),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7253),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
+	  .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
+	  .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
+	  .driver_info = NCTRL(0) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(5) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1207) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1208),
+	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1211),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1212),
+	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
+	  .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+	{ USB_DEVICE(TELIT_VENDOR_ID, 0x1900),				/* Telit LN940 (QMI) */
+	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff),	/* Telit LN940 (MBIM) */
+	  .driver_info = NCTRL(0) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0006, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0008, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0009, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff),
+	  .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff),
+	  .driver_info = NCTRL(0) | NCTRL(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0067, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0069, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0076, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0077, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0078, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0079, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0082, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0088, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0089, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0090, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0091, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0092, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0093, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0135, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0136, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0137, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0139, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0142, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0144, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0145, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0148, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0197, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0200, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0201, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0330, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0395, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0412, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x0602, 0xff) },	/* GosunCn ZTE WeLink ME3630 (MBIM mode) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1060, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1061, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1062, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1063, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1064, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1065, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1066, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1067, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1068, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1069, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1070, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1071, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1072, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1073, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1074, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1075, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1076, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1077, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1078, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1079, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1080, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1081, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1082, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1083, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1084, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1085, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1086, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1087, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1088, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1089, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1090, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1091, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1092, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1093, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1094, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1095, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1096, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1097, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1098, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1099, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1100, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1101, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1102, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1103, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1104, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1105, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1106, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1107, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1108, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1109, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1110, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1111, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1112, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1113, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1114, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1115, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1116, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1117, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1118, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1119, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1120, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1121, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1122, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1123, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1124, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1125, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1126, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1127, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1128, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1129, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1130, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1131, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1132, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1133, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1134, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1135, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1136, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1137, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1138, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1139, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1140, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1141, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1142, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1143, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1144, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1145, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1146, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1147, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1148, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1149, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1150, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1151, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1152, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1153, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1154, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1155, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1156, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1157, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1158, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1159, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1160, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1161, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1162, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1163, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1164, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1165, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1166, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1167, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1168, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1169, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) | RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1260, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1261, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1262, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1263, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1264, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1265, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1266, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1267, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1274, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1275, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1276, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1277, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1278, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1279, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1280, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1281, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1282, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1283, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1284, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1285, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1286, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1287, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1288, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1289, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1290, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1291, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1292, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1293, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1294, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1295, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1296, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1297, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1301, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1302, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1303, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1333, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff),  /* ZTE MF91 */
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff),  /* Telewell TW-LTE 4G v2 */
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x1476, 0xff) },	/* GosunCn ZTE WeLink ME3630 (ECM/NCM mode) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1545, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1546, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1547, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1565, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1566, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1567, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1589, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1590, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1591, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1592, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1594, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff),
+	  .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
+
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(1) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff45, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff46, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff47, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff48, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff49, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff50, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff51, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff52, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff53, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff54, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff55, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff56, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff57, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff58, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff59, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff60, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff61, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff62, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff63, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff64, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff65, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff66, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff67, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff68, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff69, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff70, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff71, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff72, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff73, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff74, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff75, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff76, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff77, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff78, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff79, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff80, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff81, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff82, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff83, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff84, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff85, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff86, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff87, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff88, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff89, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8a, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8e, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff90, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff91, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff9f, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa0, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa1, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa2, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa3, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa4, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa5, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa6, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa7, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa8, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa9, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaa, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffab, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffac, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffae, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaf, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb0, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb1, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb2, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb3, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb4, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb5, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb6, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb7, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb8, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb9, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffba, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbb, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbc, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbd, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbe, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbf, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc0, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc1, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc2, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc3, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc4, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc5, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc6, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc7, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc8, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc9, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffca, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcb, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcc, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcd, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffce, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcf, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd0, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd1, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd2, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd3, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd4, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd5, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff7, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff8, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff9, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfffb, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfffc, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MG880, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff),
+	 .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) | NCTRL(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff),
+	 .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
+	 .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) },
+	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L),
+	 .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
+	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM),
+	 .driver_info = RSVD(2) | RSVD(3) | RSVD(4) },
+	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X),
+	 .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
+	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X),
+	 .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
+
+	{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
+	{ USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
+	{ USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */
+	{ USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5A) },
+	{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) },
+	{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) },
+	{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) },
+	{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4518) },
+	{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) },
+	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) },
+	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */
+	{ USB_DEVICE(ALINK_VENDOR_ID, 0x9000) },
+	{ USB_DEVICE(ALINK_VENDOR_ID, ALINK_PRODUCT_PH300) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E),
+	  .driver_info = RSVD(5) | RSVD(6) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) },	/* Simcom SIM7500/SIM7600 MBIM mode */
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
+	  .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x0052),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b6),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b7),
+	  .driver_info = RSVD(5) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L800MA),
+	  .driver_info = RSVD(2) },
+	{ USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
+	{ USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) },
+	{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
+	  .driver_info = NCTRL(0) | NCTRL(1) },
+	{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
+	  .driver_info = NCTRL(1) | NCTRL(2) | RSVD(3) },
+	{USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U),
+	 .driver_info = RSVD(3)},
+	{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff),
+	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
+	{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
+	{ USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HAIER_VENDOR_ID, HAIER_PRODUCT_CE81B, 0xff, 0xff, 0xff) },
+	/* Pirelli  */
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_C100_1, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_C100_2, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1004, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1005, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1006, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1007, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1008, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1009, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100A, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100B, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100C, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100D, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100E, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100F, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1011, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1012, 0xff) },
+	/* Cinterion */
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_E) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_P) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
+	  .driver_info = RSVD(4) | RSVD(5) },
+	{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDMNET) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, /* HC28 enumerates with Siemens or Cinterion VID depending on FW revision */
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD140),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD155),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD160),
+	  .driver_info = RSVD(6) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD500),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
+	{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM610) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM500) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM510) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM800) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM900) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU818) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU816) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU828) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU826) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU518) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU516) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU528) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU526) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM600) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM610) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM500) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM510) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM800) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWM900) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU718) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU716) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU728) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU726) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU518) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU516) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU528) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU526) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM600) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM610) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM500) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM510) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM800) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLM900) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU718) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU716) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU728) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU726) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU518) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU516) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU528) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU881) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU882) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU581) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU582) },
+	{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU583) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE(LG_VENDOR_ID, LG_PRODUCT_L02C) }, /* docomo L-02C modem */
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a1, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a1, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x02, 0x01) },        /* MediaTek MT6276M modem & app port */
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_1COM, 0x0a, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_1COM, 0x02, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_2COM, 0x02, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_1COM, 0x0a, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7103_2COM, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7106_2COM, 0x02, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
+	{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) },
+	{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) },	/* TP-Link LTE Module */
+	{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000),					/* TP-Link MA260 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d01, 0xff) },			/* D-Link DWM-156 (variant) */
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d02, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d03, 0xff) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff),			/* D-Link DWM-158 */
+	 .driver_info = RSVD(4) | RSVD(5) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) },			/* D-Link DWM-157 C1 */
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff),			/* D-Link DWM-221 B1 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff),			/* D-Link DWM-222 */
+	  .driver_info = RSVD(4) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
+	{ USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) },                /* OLICARD300 - MT6225 */
+	{ USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
+	{ USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) },
+	{ 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) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x10) },	/* HP lt4132 (Huawei ME906s-158) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x12) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x13) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x14) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) },
+	{ USB_DEVICE(0x1508, 0x1001),						/* Fibocom NL668 */
+	  .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
+	{ } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, option_ids);
+
+/* The card has three separate interfaces, which the serial driver
+ * recognizes separately, thus num_port=1.
+ */
+
+static struct usb_serial_driver option_1port_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"option1",
+	},
+	.description       = "GSM modem (1-port)",
+	.id_table          = option_ids,
+	.num_ports         = 1,
+	.probe             = option_probe,
+	.open              = usb_wwan_open,
+	.close             = usb_wwan_close,
+	.dtr_rts	   = usb_wwan_dtr_rts,
+	.write             = usb_wwan_write,
+	.write_room        = usb_wwan_write_room,
+	.chars_in_buffer   = usb_wwan_chars_in_buffer,
+	.tiocmget          = usb_wwan_tiocmget,
+	.tiocmset          = usb_wwan_tiocmset,
+	.ioctl             = usb_wwan_ioctl,
+	.attach            = option_attach,
+	.release           = option_release,
+	.port_probe        = usb_wwan_port_probe,
+	.port_remove	   = usb_wwan_port_remove,
+	.read_int_callback = option_instat_callback,
+#ifdef CONFIG_PM
+	.suspend           = usb_wwan_suspend,
+	.resume            = usb_wwan_resume,
+#endif
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&option_1port_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, option_ids);
+
+static int option_probe(struct usb_serial *serial,
+			const struct usb_device_id *id)
+{
+	struct usb_interface_descriptor *iface_desc =
+				&serial->interface->cur_altsetting->desc;
+	unsigned long device_flags = id->driver_info;
+
+	/* Never bind to the CD-Rom emulation interface	*/
+	if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
+		return -ENODEV;
+
+	/*
+	 * Don't bind reserved interfaces (like network ones) which often have
+	 * 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))
+		return -ENODEV;
+
+	/*
+	 * Allow matching on bNumEndpoints for devices whose interface numbers
+	 * can change (e.g. Quectel EP06).
+	 */
+	if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
+		return -ENODEV;
+
+	/* Store the device flags so we can use them during attach. */
+	usb_set_serial_data(serial, (void *)device_flags);
+
+	return 0;
+}
+
+static int option_attach(struct usb_serial *serial)
+{
+	struct usb_interface_descriptor *iface_desc;
+	struct usb_wwan_intf_private *data;
+	unsigned long device_flags;
+
+	data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* Retrieve device flags stored at probe. */
+	device_flags = (unsigned long)usb_get_serial_data(serial);
+
+	iface_desc = &serial->interface->cur_altsetting->desc;
+
+	if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber)))
+		data->use_send_setup = 1;
+
+	spin_lock_init(&data->susp_lock);
+
+	usb_set_serial_data(serial, data);
+
+	return 0;
+}
+
+static void option_release(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+
+	kfree(intfdata);
+}
+
+static void option_instat_callback(struct urb *urb)
+{
+	int err;
+	int status = urb->status;
+	struct usb_serial_port *port = urb->context;
+	struct device *dev = &port->dev;
+	struct usb_wwan_port_private *portdata =
+					usb_get_serial_port_data(port);
+
+	dev_dbg(dev, "%s: urb %p port %p has data %p\n", __func__, urb, port, portdata);
+
+	if (status == 0) {
+		struct usb_ctrlrequest *req_pkt =
+				(struct usb_ctrlrequest *)urb->transfer_buffer;
+
+		if (!req_pkt) {
+			dev_dbg(dev, "%s: NULL req_pkt\n", __func__);
+			return;
+		}
+		if ((req_pkt->bRequestType == 0xA1) &&
+				(req_pkt->bRequest == 0x20)) {
+			int old_dcd_state;
+			unsigned char signals = *((unsigned char *)
+					urb->transfer_buffer +
+					sizeof(struct usb_ctrlrequest));
+
+			dev_dbg(dev, "%s: signal x%x\n", __func__, signals);
+
+			old_dcd_state = portdata->dcd_state;
+			portdata->cts_state = 1;
+			portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
+			portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
+			portdata->ri_state = ((signals & 0x08) ? 1 : 0);
+
+			if (old_dcd_state && !portdata->dcd_state)
+				tty_port_tty_hangup(&port->port, true);
+		} else {
+			dev_dbg(dev, "%s: type %x req %x\n", __func__,
+				req_pkt->bRequestType, req_pkt->bRequest);
+		}
+	} else if (status == -ENOENT || status == -ESHUTDOWN) {
+		dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status);
+	} else
+		dev_dbg(dev, "%s: error %d\n", __func__, status);
+
+	/* Resubmit urb so we continue receiving IRQ data */
+	if (status != -ESHUTDOWN && status != -ENOENT) {
+		usb_mark_last_busy(port->serial->dev);
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err)
+			dev_dbg(dev, "%s: resubmit intr urb failed. (%d)\n",
+				__func__, err);
+	}
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
new file mode 100644
index 0000000..ae9cb15
--- /dev/null
+++ b/drivers/usb/serial/oti6858.c
@@ -0,0 +1,847 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
+ *
+ * Copyleft  (C) 2007 Kees Lemmens (adapted for kernel 2.6.20)
+ * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail)
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
+ *
+ * Many thanks to the authors of pl2303 driver: all functions in this file
+ * are heavily based on pl2303 code, buffering code is a 1-to-1 copy.
+ *
+ * Warning! You use this driver on your own risk! The only official
+ * description of this device I have is datasheet from manufacturer,
+ * and it doesn't contain almost any information needed to write a driver.
+ * Almost all knowlegde used while writing this driver was gathered by:
+ *  - analyzing traffic between device and the M$ Windows 2000 driver,
+ *  - trying different bit combinations and checking pin states
+ *    with a voltmeter,
+ *  - receiving malformed frames and producing buffer overflows
+ *    to learn how errors are reported,
+ * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE
+ * CONNECTED TO IT!
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * TODO:
+ *  - implement correct flushing for ioctls and oti6858_close()
+ *  - check how errors (rx overflow, parity error, framing error) are reported
+ *  - implement oti6858_break_ctl()
+ *  - implement more ioctls
+ *  - test/implement flow control
+ *  - allow setting custom baud rates
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+#include "oti6858.h"
+
+#define OTI6858_DESCRIPTION \
+	"Ours Technology Inc. OTi-6858 USB to serial adapter driver"
+#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* requests */
+#define	OTI6858_REQ_GET_STATUS		(USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
+#define	OTI6858_REQ_T_GET_STATUS	0x01
+
+#define	OTI6858_REQ_SET_LINE		(USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)
+#define	OTI6858_REQ_T_SET_LINE		0x00
+
+#define	OTI6858_REQ_CHECK_TXBUFF	(USB_DIR_IN | USB_TYPE_VENDOR | 0x01)
+#define	OTI6858_REQ_T_CHECK_TXBUFF	0x00
+
+/* format of the control packet */
+struct oti6858_control_pkt {
+	__le16	divisor;	/* baud rate = 96000000 / (16 * divisor), LE */
+#define OTI6858_MAX_BAUD_RATE	3000000
+	u8	frame_fmt;
+#define FMT_STOP_BITS_MASK	0xc0
+#define FMT_STOP_BITS_1		0x00
+#define FMT_STOP_BITS_2		0x40	/* 1.5 stop bits if FMT_DATA_BITS_5 */
+#define FMT_PARITY_MASK		0x38
+#define FMT_PARITY_NONE		0x00
+#define FMT_PARITY_ODD		0x08
+#define FMT_PARITY_EVEN		0x18
+#define FMT_PARITY_MARK		0x28
+#define FMT_PARITY_SPACE	0x38
+#define FMT_DATA_BITS_MASK	0x03
+#define FMT_DATA_BITS_5		0x00
+#define FMT_DATA_BITS_6		0x01
+#define FMT_DATA_BITS_7		0x02
+#define FMT_DATA_BITS_8		0x03
+	u8	something;	/* always equals 0x43 */
+	u8	control;	/* settings of flow control lines */
+#define CONTROL_MASK		0x0c
+#define CONTROL_DTR_HIGH	0x08
+#define CONTROL_RTS_HIGH	0x04
+	u8	tx_status;
+#define	TX_BUFFER_EMPTIED	0x09
+	u8	pin_state;
+#define PIN_MASK		0x3f
+#define PIN_MSR_MASK		0x1b
+#define PIN_RTS			0x20	/* output pin */
+#define PIN_CTS			0x10	/* input pin, active low */
+#define PIN_DSR			0x08	/* input pin, active low */
+#define PIN_DTR			0x04	/* output pin */
+#define PIN_RI			0x02	/* input pin, active low */
+#define PIN_DCD			0x01	/* input pin, active low */
+	u8	rx_bytes_avail;		/* number of bytes in rx buffer */;
+};
+
+#define OTI6858_CTRL_PKT_SIZE	sizeof(struct oti6858_control_pkt)
+#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \
+	(((a)->divisor == (priv)->pending_setup.divisor) \
+	  && ((a)->control == (priv)->pending_setup.control) \
+	  && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt))
+
+/* function prototypes */
+static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void oti6858_close(struct usb_serial_port *port);
+static void oti6858_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static void oti6858_init_termios(struct tty_struct *tty);
+static void oti6858_read_int_callback(struct urb *urb);
+static void oti6858_read_bulk_callback(struct urb *urb);
+static void oti6858_write_bulk_callback(struct urb *urb);
+static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count);
+static int oti6858_write_room(struct tty_struct *tty);
+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_port_probe(struct usb_serial_port *port);
+static int oti6858_port_remove(struct usb_serial_port *port);
+
+/* device info */
+static struct usb_serial_driver oti6858_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"oti6858",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_bulk_in =		1,
+	.num_bulk_out =		1,
+	.num_interrupt_in =	1,
+	.open =			oti6858_open,
+	.close =		oti6858_close,
+	.write =		oti6858_write,
+	.set_termios =		oti6858_set_termios,
+	.init_termios = 	oti6858_init_termios,
+	.tiocmget =		oti6858_tiocmget,
+	.tiocmset =		oti6858_tiocmset,
+	.tiocmiwait =		usb_serial_generic_tiocmiwait,
+	.read_bulk_callback =	oti6858_read_bulk_callback,
+	.read_int_callback =	oti6858_read_int_callback,
+	.write_bulk_callback =	oti6858_write_bulk_callback,
+	.write_room =		oti6858_write_room,
+	.chars_in_buffer =	oti6858_chars_in_buffer,
+	.port_probe =		oti6858_port_probe,
+	.port_remove =		oti6858_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&oti6858_device, NULL
+};
+
+struct oti6858_private {
+	spinlock_t lock;
+
+	struct oti6858_control_pkt status;
+
+	struct {
+		u8 read_urb_in_use;
+		u8 write_urb_in_use;
+	} flags;
+	struct delayed_work delayed_write_work;
+
+	struct {
+		__le16 divisor;
+		u8 frame_fmt;
+		u8 control;
+	} pending_setup;
+	u8 transient;
+	u8 setup_done;
+	struct delayed_work delayed_setup_work;
+
+	struct usb_serial_port *port;   /* USB port with which associated */
+};
+
+static void setup_line(struct work_struct *work)
+{
+	struct oti6858_private *priv = container_of(work,
+			struct oti6858_private, delayed_setup_work.work);
+	struct usb_serial_port *port = priv->port;
+	struct oti6858_control_pkt *new_setup;
+	unsigned long flags;
+	int result;
+
+	new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL);
+	if (!new_setup) {
+		/* we will try again */
+		schedule_delayed_work(&priv->delayed_setup_work,
+						msecs_to_jiffies(2));
+		return;
+	}
+
+	result = usb_control_msg(port->serial->dev,
+				usb_rcvctrlpipe(port->serial->dev, 0),
+				OTI6858_REQ_T_GET_STATUS,
+				OTI6858_REQ_GET_STATUS,
+				0, 0,
+				new_setup, OTI6858_CTRL_PKT_SIZE,
+				100);
+
+	if (result != OTI6858_CTRL_PKT_SIZE) {
+		dev_err(&port->dev, "%s(): error reading status\n", __func__);
+		kfree(new_setup);
+		/* we will try again */
+		schedule_delayed_work(&priv->delayed_setup_work,
+							msecs_to_jiffies(2));
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) {
+		new_setup->divisor = priv->pending_setup.divisor;
+		new_setup->control = priv->pending_setup.control;
+		new_setup->frame_fmt = priv->pending_setup.frame_fmt;
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+		result = usb_control_msg(port->serial->dev,
+					usb_sndctrlpipe(port->serial->dev, 0),
+					OTI6858_REQ_T_SET_LINE,
+					OTI6858_REQ_SET_LINE,
+					0, 0,
+					new_setup, OTI6858_CTRL_PKT_SIZE,
+					100);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		result = 0;
+	}
+	kfree(new_setup);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (result != OTI6858_CTRL_PKT_SIZE)
+		priv->transient = 0;
+	priv->setup_done = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+			__func__, result);
+	}
+}
+
+static void send_data(struct work_struct *work)
+{
+	struct oti6858_private *priv = container_of(work,
+			struct oti6858_private, delayed_write_work.work);
+	struct usb_serial_port *port = priv->port;
+	int count = 0, result;
+	unsigned long flags;
+	u8 *allow;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->flags.write_urb_in_use) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		schedule_delayed_work(&priv->delayed_write_work,
+						msecs_to_jiffies(2));
+		return;
+	}
+	priv->flags.write_urb_in_use = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	spin_lock_irqsave(&port->lock, flags);
+	count = kfifo_len(&port->write_fifo);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (count > port->bulk_out_size)
+		count = port->bulk_out_size;
+
+	if (count != 0) {
+		allow = kmalloc(1, GFP_KERNEL);
+		if (!allow)
+			return;
+
+		result = usb_control_msg(port->serial->dev,
+				usb_rcvctrlpipe(port->serial->dev, 0),
+				OTI6858_REQ_T_CHECK_TXBUFF,
+				OTI6858_REQ_CHECK_TXBUFF,
+				count, 0, allow, 1, 100);
+		if (result != 1 || *allow != 0)
+			count = 0;
+		kfree(allow);
+	}
+
+	if (count == 0) {
+		priv->flags.write_urb_in_use = 0;
+
+		dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+		if (result != 0) {
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+				__func__, result);
+		}
+		return;
+	}
+
+	count = kfifo_out_locked(&port->write_fifo,
+					port->write_urb->transfer_buffer,
+					count, &port->lock);
+	port->write_urb->transfer_buffer_length = count;
+	result = usb_submit_urb(port->write_urb, GFP_NOIO);
+	if (result != 0) {
+		dev_err_console(port, "%s(): usb_submit_urb() failed with error %d\n",
+				__func__, result);
+		priv->flags.write_urb_in_use = 0;
+	}
+
+	usb_serial_port_softint(port);
+}
+
+static int oti6858_port_probe(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->port = port;
+	INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line);
+	INIT_DELAYED_WORK(&priv->delayed_write_work, send_data);
+
+	usb_set_serial_port_data(port, priv);
+
+	port->port.drain_delay = 256;	/* FIXME: check the FIFO length */
+
+	return 0;
+}
+
+static int oti6858_port_remove(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	if (!count)
+		return count;
+
+	count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
+
+	return count;
+}
+
+static int oti6858_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int room = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	room = kfifo_avail(&port->write_fifo);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return room;
+}
+
+static int oti6858_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int chars = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	chars = kfifo_len(&port->write_fifo);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return chars;
+}
+
+static void oti6858_init_termios(struct tty_struct *tty)
+{
+	tty->termios = tty_std_termios;
+	tty->termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty->termios.c_ispeed = 38400;
+	tty->termios.c_ospeed = 38400;
+}
+
+static void oti6858_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int cflag;
+	u8 frame_fmt, control;
+	__le16 divisor;
+	int br;
+
+	cflag = tty->termios.c_cflag;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	divisor = priv->pending_setup.divisor;
+	frame_fmt = priv->pending_setup.frame_fmt;
+	control = priv->pending_setup.control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	frame_fmt &= ~FMT_DATA_BITS_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		frame_fmt |= FMT_DATA_BITS_5;
+		break;
+	case CS6:
+		frame_fmt |= FMT_DATA_BITS_6;
+		break;
+	case CS7:
+		frame_fmt |= FMT_DATA_BITS_7;
+		break;
+	default:
+	case CS8:
+		frame_fmt |= FMT_DATA_BITS_8;
+		break;
+	}
+
+	/* manufacturer claims that this device can work with baud rates
+	 * up to 3 Mbps; I've tested it only on 115200 bps, so I can't
+	 * guarantee that any other baud rate will work (especially
+	 * the higher ones)
+	 */
+	br = tty_get_baud_rate(tty);
+	if (br == 0) {
+		divisor = 0;
+	} else {
+		int real_br;
+		int new_divisor;
+		br = min(br, OTI6858_MAX_BAUD_RATE);
+
+		new_divisor = (96000000 + 8 * br) / (16 * br);
+		real_br = 96000000 / (16 * new_divisor);
+		divisor = cpu_to_le16(new_divisor);
+		tty_encode_baud_rate(tty, real_br, real_br);
+	}
+
+	frame_fmt &= ~FMT_STOP_BITS_MASK;
+	if ((cflag & CSTOPB) != 0)
+		frame_fmt |= FMT_STOP_BITS_2;
+	else
+		frame_fmt |= FMT_STOP_BITS_1;
+
+	frame_fmt &= ~FMT_PARITY_MASK;
+	if ((cflag & PARENB) != 0) {
+		if ((cflag & PARODD) != 0)
+			frame_fmt |= FMT_PARITY_ODD;
+		else
+			frame_fmt |= FMT_PARITY_EVEN;
+	} else {
+		frame_fmt |= FMT_PARITY_NONE;
+	}
+
+	control &= ~CONTROL_MASK;
+	if ((cflag & CRTSCTS) != 0)
+		control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH);
+
+	/* change control lines if we are switching to or from B0 */
+	/* FIXME:
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	if ((cflag & CBAUD) == B0)
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	else
+		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+	if (control != priv->line_control) {
+		control = priv->line_control;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		set_control_lines(serial->dev, control);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+	*/
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (divisor != priv->pending_setup.divisor
+			|| control != priv->pending_setup.control
+			|| frame_fmt != priv->pending_setup.frame_fmt) {
+		priv->pending_setup.divisor = divisor;
+		priv->pending_setup.control = control;
+		priv->pending_setup.frame_fmt = frame_fmt;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	struct oti6858_control_pkt *buf;
+	unsigned long flags;
+	int result;
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				OTI6858_REQ_T_GET_STATUS,
+				OTI6858_REQ_GET_STATUS,
+				0, 0,
+				buf, OTI6858_CTRL_PKT_SIZE,
+				100);
+	if (result != OTI6858_CTRL_PKT_SIZE) {
+		/* assume default (after power-on reset) values */
+		buf->divisor = cpu_to_le16(0x009c);	/* 38400 bps */
+		buf->frame_fmt = 0x03;	/* 8N1 */
+		buf->something = 0x43;
+		buf->control = 0x4c;	/* DTR, RTS */
+		buf->tx_status = 0x00;
+		buf->pin_state = 0x5b;	/* RTS, CTS, DSR, DTR, RI, DCD */
+		buf->rx_bytes_avail = 0x00;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE);
+	priv->pending_setup.divisor = buf->divisor;
+	priv->pending_setup.frame_fmt = buf->frame_fmt;
+	priv->pending_setup.control = buf->control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	kfree(buf);
+
+	dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+			__func__, result);
+		oti6858_close(port);
+		return result;
+	}
+
+	/* setup termios */
+	if (tty)
+		oti6858_set_termios(tty, port, NULL);
+
+	return 0;
+}
+
+static void oti6858_close(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	/* clear out any remaining data in the buffer */
+	kfifo_reset_out(&port->write_fifo);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	dev_dbg(&port->dev, "%s(): after buf_clear()\n", __func__);
+
+	/* cancel scheduled setup */
+	cancel_delayed_work_sync(&priv->delayed_setup_work);
+	cancel_delayed_work_sync(&priv->delayed_write_work);
+
+	/* shutdown our urbs */
+	dev_dbg(&port->dev, "%s(): shutting down urbs\n", __func__);
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int oti6858_tiocmset(struct tty_struct *tty,
+				unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	dev_dbg(&port->dev, "%s(set = 0x%08x, clear = 0x%08x)\n",
+		__func__, set, clear);
+
+	/* FIXME: check if this is correct (active high/low) */
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->pending_setup.control;
+	if ((set & TIOCM_RTS) != 0)
+		control |= CONTROL_RTS_HIGH;
+	if ((set & TIOCM_DTR) != 0)
+		control |= CONTROL_DTR_HIGH;
+	if ((clear & TIOCM_RTS) != 0)
+		control &= ~CONTROL_RTS_HIGH;
+	if ((clear & TIOCM_DTR) != 0)
+		control &= ~CONTROL_DTR_HIGH;
+
+	if (control != priv->pending_setup.control)
+		priv->pending_setup.control = control;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return 0;
+}
+
+static int oti6858_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned pin_state;
+	unsigned result = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	pin_state = priv->status.pin_state & PIN_MASK;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* FIXME: check if this is correct (active high/low) */
+	if ((pin_state & PIN_RTS) != 0)
+		result |= TIOCM_RTS;
+	if ((pin_state & PIN_CTS) != 0)
+		result |= TIOCM_CTS;
+	if ((pin_state & PIN_DSR) != 0)
+		result |= TIOCM_DSR;
+	if ((pin_state & PIN_DTR) != 0)
+		result |= TIOCM_DTR;
+	if ((pin_state & PIN_RI) != 0)
+		result |= TIOCM_RI;
+	if ((pin_state & PIN_DCD) != 0)
+		result |= TIOCM_CD;
+
+	dev_dbg(&port->dev, "%s() = 0x%08x\n", __func__, result);
+
+	return result;
+}
+
+static void oti6858_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port =  urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int transient = 0, can_recv = 0, resubmit = 1;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&urb->dev->dev, "%s(): nonzero urb status received: %d\n",
+			__func__, status);
+		break;
+	}
+
+	if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) {
+		struct oti6858_control_pkt *xs = urb->transfer_buffer;
+		unsigned long flags;
+
+		spin_lock_irqsave(&priv->lock, flags);
+
+		if (!priv->transient) {
+			if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
+				if (xs->rx_bytes_avail == 0) {
+					priv->transient = 4;
+					priv->setup_done = 0;
+					resubmit = 0;
+					dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
+					schedule_delayed_work(&priv->delayed_setup_work, 0);
+				}
+			}
+		} else {
+			if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
+				priv->transient = 0;
+			} else if (!priv->setup_done) {
+				resubmit = 0;
+			} else if (--priv->transient == 0) {
+				if (xs->rx_bytes_avail == 0) {
+					priv->transient = 4;
+					priv->setup_done = 0;
+					resubmit = 0;
+					dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
+					schedule_delayed_work(&priv->delayed_setup_work, 0);
+				}
+			}
+		}
+
+		if (!priv->transient) {
+			u8 delta = xs->pin_state ^ priv->status.pin_state;
+
+			if (delta & PIN_MSR_MASK) {
+				if (delta & PIN_CTS)
+					port->icount.cts++;
+				if (delta & PIN_DSR)
+					port->icount.dsr++;
+				if (delta & PIN_RI)
+					port->icount.rng++;
+				if (delta & PIN_DCD)
+					port->icount.dcd++;
+
+				wake_up_interruptible(&port->port.delta_msr_wait);
+			}
+
+			memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE);
+		}
+
+		if (!priv->transient && xs->rx_bytes_avail != 0) {
+			can_recv = xs->rx_bytes_avail;
+			priv->flags.read_urb_in_use = 1;
+		}
+
+		transient = priv->transient;
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	if (can_recv) {
+		int result;
+
+		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (result != 0) {
+			priv->flags.read_urb_in_use = 0;
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
+					" error %d\n", __func__, result);
+		} else {
+			resubmit = 0;
+		}
+	} else if (!transient) {
+		unsigned long flags;
+		int count;
+
+		spin_lock_irqsave(&port->lock, flags);
+		count = kfifo_len(&port->write_fifo);
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		spin_lock_irqsave(&priv->lock, flags);
+		if (priv->flags.write_urb_in_use == 0 && count != 0) {
+			schedule_delayed_work(&priv->delayed_write_work, 0);
+			resubmit = 0;
+		}
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	if (resubmit) {
+		int result;
+
+/*		dev_dbg(&urb->dev->dev, "%s(): submitting interrupt urb\n", __func__); */
+		result = usb_submit_urb(urb, GFP_ATOMIC);
+		if (result != 0) {
+			dev_err(&urb->dev->dev,
+					"%s(): usb_submit_urb() failed with"
+					" error %d\n", __func__, result);
+		}
+	}
+}
+
+static void oti6858_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port =  urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	int status = urb->status;
+	int result;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->flags.read_urb_in_use = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (status != 0) {
+		dev_dbg(&urb->dev->dev, "%s(): unable to handle the error, exiting\n", __func__);
+		return;
+	}
+
+	if (urb->actual_length > 0) {
+		tty_insert_flip_string(&port->port, data, urb->actual_length);
+		tty_flip_buffer_push(&port->port);
+	}
+
+	/* schedule the interrupt urb */
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result != 0 && result != -EPERM) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
+				" error %d\n", __func__, result);
+	}
+}
+
+static void oti6858_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port =  urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
+	int result;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", __func__, status);
+		priv->flags.write_urb_in_use = 0;
+		return;
+	default:
+		/* error in the urb, so we have to resubmit it */
+		dev_dbg(&urb->dev->dev, "%s(): nonzero write bulk status received: %d\n", __func__, status);
+		dev_dbg(&urb->dev->dev, "%s(): overflow in write\n", __func__);
+
+		port->write_urb->transfer_buffer_length = 1;
+		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (result) {
+			dev_err_console(port, "%s(): usb_submit_urb() failed,"
+					" error %d\n", __func__, result);
+		} else {
+			return;
+		}
+	}
+
+	priv->flags.write_urb_in_use = 0;
+
+	/* schedule the interrupt urb if we are still open */
+	dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): failed submitting int urb,"
+					" error %d\n", __func__, result);
+	}
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
+MODULE_AUTHOR(OTI6858_AUTHOR);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h
new file mode 100644
index 0000000..1226bf2
--- /dev/null
+++ b/drivers/usb/serial/oti6858.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
+ */
+#ifndef __LINUX_USB_SERIAL_OTI6858_H
+#define __LINUX_USB_SERIAL_OTI6858_H
+
+#define OTI6858_VENDOR_ID	0x0ea0
+#define OTI6858_PRODUCT_ID	0x6858
+
+#endif
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
new file mode 100644
index 0000000..e41f725
--- /dev/null
+++ b/drivers/usb/serial/pl2303.c
@@ -0,0 +1,1042 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Prolific PL2303 USB to serial adaptor driver
+ *
+ * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
+ *
+ * Original driver for 2.2.x by anonymous
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/unaligned.h>
+#include "pl2303.h"
+
+
+#define PL2303_QUIRK_UART_STATE_IDX0		BIT(0)
+#define PL2303_QUIRK_LEGACY			BIT(1)
+#define PL2303_QUIRK_ENDPOINT_HACK		BIT(2)
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_CHILITAG) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
+	{ 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),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC232B),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ 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) },
+	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
+	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
+	{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
+	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
+	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
+	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
+	{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
+	{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1),
+		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65),
+		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75),
+		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */
+	{ USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
+	{ USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) },
+	{ USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
+	{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
+	{ USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
+	{ USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
+	{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
+	{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
+	{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID),
+		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+	{ USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
+	{ USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
+	{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
+	{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_LD960_PRODUCT_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM220_PRODUCT_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM960_PRODUCT_ID) },
+	{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
+	{ USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
+	{ USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
+	{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
+	{ USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+#define SET_LINE_REQUEST_TYPE		0x21
+#define SET_LINE_REQUEST		0x20
+
+#define SET_CONTROL_REQUEST_TYPE	0x21
+#define SET_CONTROL_REQUEST		0x22
+#define CONTROL_DTR			0x01
+#define CONTROL_RTS			0x02
+
+#define BREAK_REQUEST_TYPE		0x21
+#define BREAK_REQUEST			0x23
+#define BREAK_ON			0xffff
+#define BREAK_OFF			0x0000
+
+#define GET_LINE_REQUEST_TYPE		0xa1
+#define GET_LINE_REQUEST		0x21
+
+#define VENDOR_WRITE_REQUEST_TYPE	0x40
+#define VENDOR_WRITE_REQUEST		0x01
+
+#define VENDOR_READ_REQUEST_TYPE	0xc0
+#define VENDOR_READ_REQUEST		0x01
+
+#define UART_STATE_INDEX		8
+#define UART_STATE_MSR_MASK		0x8b
+#define UART_STATE_TRANSIENT_MASK	0x74
+#define UART_DCD			0x01
+#define UART_DSR			0x02
+#define UART_BREAK_ERROR		0x04
+#define UART_RING			0x08
+#define UART_FRAME_ERROR		0x10
+#define UART_PARITY_ERROR		0x20
+#define UART_OVERRUN_ERROR		0x40
+#define UART_CTS			0x80
+
+static void pl2303_set_break(struct usb_serial_port *port, bool enable);
+
+enum pl2303_type {
+	TYPE_01,	/* Type 0 and 1 (difference unknown) */
+	TYPE_HX,	/* HX version of the pl2303 chip */
+	TYPE_COUNT
+};
+
+struct pl2303_type_data {
+	speed_t max_baud_rate;
+	unsigned long quirks;
+};
+
+struct pl2303_serial_private {
+	const struct pl2303_type_data *type;
+	unsigned long quirks;
+};
+
+struct pl2303_private {
+	spinlock_t lock;
+	u8 line_control;
+	u8 line_status;
+
+	u8 line_settings[7];
+};
+
+static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
+	[TYPE_01] = {
+		.max_baud_rate =	1228800,
+		.quirks =		PL2303_QUIRK_LEGACY,
+	},
+	[TYPE_HX] = {
+		.max_baud_rate =	12000000,
+	},
+};
+
+static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
+							unsigned char buf[1])
+{
+	struct device *dev = &serial->interface->dev;
+	int res;
+
+	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+			value, 0, buf, 1, 100);
+	if (res != 1) {
+		dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
+								value, res);
+		if (res >= 0)
+			res = -EIO;
+
+		return res;
+	}
+
+	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, buf[0]);
+
+	return 0;
+}
+
+static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
+{
+	struct device *dev = &serial->interface->dev;
+	int res;
+
+	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
+
+	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+			value, index, NULL, 0, 100);
+	if (res) {
+		dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
+								value, res);
+		return res;
+	}
+
+	return 0;
+}
+
+static int pl2303_probe(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	usb_set_serial_data(serial, (void *)id->driver_info);
+
+	return 0;
+}
+
+/*
+ * Use interrupt endpoint from first interface if available.
+ *
+ * This is needed due to the looney way its endpoints are set up.
+ */
+static int pl2303_endpoint_hack(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	struct usb_interface *interface = serial->interface;
+	struct usb_device *dev = serial->dev;
+	struct device *ddev = &interface->dev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	unsigned int i;
+
+	if (interface == dev->actconfig->interface[0])
+		return 0;
+
+	/* check out the endpoints of the other interface */
+	iface_desc = dev->actconfig->interface[0]->cur_altsetting;
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!usb_endpoint_is_int_in(endpoint))
+			continue;
+
+		dev_dbg(ddev, "found interrupt in on separate interface\n");
+		if (epds->num_interrupt_in < ARRAY_SIZE(epds->interrupt_in))
+			epds->interrupt_in[epds->num_interrupt_in++] = endpoint;
+	}
+
+	return 0;
+}
+
+static int pl2303_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	unsigned long quirks = (unsigned long)usb_get_serial_data(serial);
+	struct device *dev = &serial->interface->dev;
+	int ret;
+
+	if (quirks & PL2303_QUIRK_ENDPOINT_HACK) {
+		ret = pl2303_endpoint_hack(serial, epds);
+		if (ret)
+			return ret;
+	}
+
+	if (epds->num_interrupt_in < 1) {
+		dev_err(dev, "required interrupt-in endpoint missing\n");
+		return -ENODEV;
+	}
+
+	return 1;
+}
+
+static int pl2303_startup(struct usb_serial *serial)
+{
+	struct pl2303_serial_private *spriv;
+	enum pl2303_type type = TYPE_01;
+	unsigned char *buf;
+
+	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
+	if (!spriv)
+		return -ENOMEM;
+
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf) {
+		kfree(spriv);
+		return -ENOMEM;
+	}
+
+	if (serial->dev->descriptor.bDeviceClass == 0x02)
+		type = TYPE_01;		/* type 0 */
+	else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
+		type = TYPE_HX;
+	else if (serial->dev->descriptor.bDeviceClass == 0x00)
+		type = TYPE_01;		/* type 1 */
+	else if (serial->dev->descriptor.bDeviceClass == 0xFF)
+		type = TYPE_01;		/* type 1 */
+	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
+
+	spriv->type = &pl2303_type_data[type];
+	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
+	spriv->quirks |= spriv->type->quirks;
+
+	usb_set_serial_data(serial, spriv);
+
+	pl2303_vendor_read(serial, 0x8484, buf);
+	pl2303_vendor_write(serial, 0x0404, 0);
+	pl2303_vendor_read(serial, 0x8484, buf);
+	pl2303_vendor_read(serial, 0x8383, buf);
+	pl2303_vendor_read(serial, 0x8484, buf);
+	pl2303_vendor_write(serial, 0x0404, 1);
+	pl2303_vendor_read(serial, 0x8484, buf);
+	pl2303_vendor_read(serial, 0x8383, buf);
+	pl2303_vendor_write(serial, 0, 1);
+	pl2303_vendor_write(serial, 1, 0);
+	if (spriv->quirks & PL2303_QUIRK_LEGACY)
+		pl2303_vendor_write(serial, 2, 0x24);
+	else
+		pl2303_vendor_write(serial, 2, 0x44);
+
+	kfree(buf);
+
+	return 0;
+}
+
+static void pl2303_release(struct usb_serial *serial)
+{
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+
+	kfree(spriv);
+}
+
+static int pl2303_port_probe(struct usb_serial_port *port)
+{
+	struct pl2303_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	port->port.drain_delay = 256;
+
+	return 0;
+}
+
+static int pl2303_port_remove(struct usb_serial_port *port)
+{
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
+{
+	struct usb_device *dev = port->serial->dev;
+	int retval;
+
+	dev_dbg(&port->dev, "%s - %02x\n", __func__, value);
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+				 value, 0, NULL, 0, 100);
+	if (retval)
+		dev_err(&port->dev, "%s - failed: %d\n", __func__, retval);
+
+	return retval;
+}
+
+/*
+ * Returns the nearest supported baud rate that can be set directly without
+ * using divisors.
+ */
+static speed_t pl2303_get_supported_baud_rate(speed_t baud)
+{
+	static const speed_t baud_sup[] = {
+		75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
+		14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
+		614400, 921600, 1228800, 2457600, 3000000, 6000000
+	};
+
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
+		if (baud_sup[i] > baud)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(baud_sup))
+		baud = baud_sup[i - 1];
+	else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
+		baud = baud_sup[i - 1];
+	else
+		baud = baud_sup[i];
+
+	return baud;
+}
+
+/*
+ * NOTE: If unsupported baud rates are set directly, the PL2303 seems to
+ *       use 9600 baud.
+ */
+static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
+								speed_t baud)
+{
+	put_unaligned_le32(baud, buf);
+
+	return baud;
+}
+
+static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
+								speed_t baud)
+{
+	unsigned int baseline, mantissa, exponent;
+
+	/*
+	 * Apparently the formula is:
+	 *   baudrate = 12M * 32 / (mantissa * 4^exponent)
+	 * where
+	 *   mantissa = buf[8:0]
+	 *   exponent = buf[11:9]
+	 */
+	baseline = 12000000 * 32;
+	mantissa = baseline / baud;
+	if (mantissa == 0)
+		mantissa = 1;	/* Avoid dividing by zero if baud > 32*12M. */
+	exponent = 0;
+	while (mantissa >= 512) {
+		if (exponent < 7) {
+			mantissa >>= 2;	/* divide by 4 */
+			exponent++;
+		} else {
+			/* Exponent is maxed. Trim mantissa and leave. */
+			mantissa = 511;
+			break;
+		}
+	}
+
+	buf[3] = 0x80;
+	buf[2] = 0;
+	buf[1] = exponent << 1 | mantissa >> 8;
+	buf[0] = mantissa & 0xff;
+
+	/* Calculate and return the exact baud rate. */
+	baud = (baseline / mantissa) >> (exponent << 1);
+
+	return baud;
+}
+
+static void pl2303_encode_baud_rate(struct tty_struct *tty,
+					struct usb_serial_port *port,
+					u8 buf[4])
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+	speed_t	baud_sup;
+	speed_t baud;
+
+	baud = tty_get_baud_rate(tty);
+	dev_dbg(&port->dev, "baud requested = %u\n", baud);
+	if (!baud)
+		return;
+
+	if (spriv->type->max_baud_rate)
+		baud = min_t(speed_t, baud, spriv->type->max_baud_rate);
+	/*
+	 * Use direct method for supported baud rates, otherwise use divisors.
+	 */
+	baud_sup = pl2303_get_supported_baud_rate(baud);
+
+	if (baud == baud_sup)
+		baud = pl2303_encode_baud_rate_direct(buf, baud);
+	else
+		baud = pl2303_encode_baud_rate_divisor(buf, baud);
+
+	/* Save resulting baud rate */
+	tty_encode_baud_rate(tty, baud, baud);
+	dev_dbg(&port->dev, "baud set = %u\n", baud);
+}
+
+static int pl2303_get_line_request(struct usb_serial_port *port,
+							unsigned char buf[7])
+{
+	struct usb_device *udev = port->serial->dev;
+	int ret;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+				0, 0, buf, 7, 100);
+	if (ret != 7) {
+		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
+
+		if (ret >= 0)
+			ret = -EIO;
+
+		return ret;
+	}
+
+	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
+
+	return 0;
+}
+
+static int pl2303_set_line_request(struct usb_serial_port *port,
+							unsigned char buf[7])
+{
+	struct usb_device *udev = port->serial->dev;
+	int ret;
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
+				0, 0, buf, 7, 100);
+	if (ret < 0) {
+		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
+
+	return 0;
+}
+
+static bool pl2303_termios_change(const struct ktermios *a, const struct ktermios *b)
+{
+	bool ixon_change;
+
+	ixon_change = ((a->c_iflag ^ b->c_iflag) & (IXON | IXANY)) ||
+			a->c_cc[VSTART] != b->c_cc[VSTART] ||
+			a->c_cc[VSTOP] != b->c_cc[VSTOP];
+
+	return tty_termios_hw_change(a, b) || ixon_change;
+}
+
+static void pl2303_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned char *buf;
+	int ret;
+	u8 control;
+
+	if (old_termios && !pl2303_termios_change(&tty->termios, old_termios))
+		return;
+
+	buf = kzalloc(7, GFP_KERNEL);
+	if (!buf) {
+		/* Report back no change occurred */
+		if (old_termios)
+			tty->termios = *old_termios;
+		return;
+	}
+
+	pl2303_get_line_request(port, buf);
+
+	switch (C_CSIZE(tty)) {
+	case CS5:
+		buf[6] = 5;
+		break;
+	case CS6:
+		buf[6] = 6;
+		break;
+	case CS7:
+		buf[6] = 7;
+		break;
+	default:
+	case CS8:
+		buf[6] = 8;
+	}
+	dev_dbg(&port->dev, "data bits = %d\n", buf[6]);
+
+	/* For reference buf[0]:buf[3] baud rate value */
+	pl2303_encode_baud_rate(tty, port, &buf[0]);
+
+	/* For reference buf[4]=0 is 1 stop bits */
+	/* For reference buf[4]=1 is 1.5 stop bits */
+	/* For reference buf[4]=2 is 2 stop bits */
+	if (C_CSTOPB(tty)) {
+		/*
+		 * NOTE: Comply with "real" UARTs / RS232:
+		 *       use 1.5 instead of 2 stop bits with 5 data bits
+		 */
+		if (C_CSIZE(tty) == CS5) {
+			buf[4] = 1;
+			dev_dbg(&port->dev, "stop bits = 1.5\n");
+		} else {
+			buf[4] = 2;
+			dev_dbg(&port->dev, "stop bits = 2\n");
+		}
+	} else {
+		buf[4] = 0;
+		dev_dbg(&port->dev, "stop bits = 1\n");
+	}
+
+	if (C_PARENB(tty)) {
+		/* For reference buf[5]=0 is none parity */
+		/* For reference buf[5]=1 is odd parity */
+		/* For reference buf[5]=2 is even parity */
+		/* For reference buf[5]=3 is mark parity */
+		/* For reference buf[5]=4 is space parity */
+		if (C_PARODD(tty)) {
+			if (C_CMSPAR(tty)) {
+				buf[5] = 3;
+				dev_dbg(&port->dev, "parity = mark\n");
+			} else {
+				buf[5] = 1;
+				dev_dbg(&port->dev, "parity = odd\n");
+			}
+		} else {
+			if (C_CMSPAR(tty)) {
+				buf[5] = 4;
+				dev_dbg(&port->dev, "parity = space\n");
+			} else {
+				buf[5] = 2;
+				dev_dbg(&port->dev, "parity = even\n");
+			}
+		}
+	} else {
+		buf[5] = 0;
+		dev_dbg(&port->dev, "parity = none\n");
+	}
+
+	/*
+	 * Some PL2303 are known to lose bytes if you change serial settings
+	 * even to the same values as before. Thus we actually need to filter
+	 * in this specific case.
+	 *
+	 * Note that the tty_termios_hw_change check above is not sufficient
+	 * as a previously requested baud rate may differ from the one
+	 * actually used (and stored in old_termios).
+	 *
+	 * NOTE: No additional locking needed for line_settings as it is
+	 *       only used in set_termios, which is serialised against itself.
+	 */
+	if (!old_termios || memcmp(buf, priv->line_settings, 7)) {
+		ret = pl2303_set_line_request(port, buf);
+		if (!ret)
+			memcpy(priv->line_settings, buf, 7);
+	}
+
+	/* change control lines if we are switching to or from B0 */
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	if (C_BAUD(tty) == B0)
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+	if (control != priv->line_control) {
+		control = priv->line_control;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		pl2303_set_control_lines(port, control);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	if (C_CRTSCTS(tty)) {
+		if (spriv->quirks & PL2303_QUIRK_LEGACY)
+			pl2303_vendor_write(serial, 0x0, 0x41);
+		else
+			pl2303_vendor_write(serial, 0x0, 0x61);
+	} else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 &&
+			STOP_CHAR(tty) == 0x13) {
+		pl2303_vendor_write(serial, 0x0, 0xc0);
+	} else {
+		pl2303_vendor_write(serial, 0x0, 0x0);
+	}
+
+	kfree(buf);
+}
+
+static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (on)
+		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+	else
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	control = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	pl2303_set_control_lines(port, control);
+}
+
+static void pl2303_close(struct usb_serial_port *port)
+{
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+	pl2303_set_break(port, false);
+}
+
+static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+	int result;
+
+	if (spriv->quirks & PL2303_QUIRK_LEGACY) {
+		usb_clear_halt(serial->dev, port->write_urb->pipe);
+		usb_clear_halt(serial->dev, port->read_urb->pipe);
+	} else {
+		/* reset upstream data pipes */
+		pl2303_vendor_write(serial, 8, 0);
+		pl2303_vendor_write(serial, 9, 0);
+	}
+
+	/* Setup termios */
+	if (tty)
+		pl2303_set_termios(tty, port, NULL);
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result) {
+		dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+			result);
+		return result;
+	}
+
+	result = usb_serial_generic_open(tty, port);
+	if (result) {
+		usb_kill_urb(port->interrupt_in_urb);
+		return result;
+	}
+
+	return 0;
+}
+
+static int pl2303_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+	int ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (set & TIOCM_RTS)
+		priv->line_control |= CONTROL_RTS;
+	if (set & TIOCM_DTR)
+		priv->line_control |= CONTROL_DTR;
+	if (clear & TIOCM_RTS)
+		priv->line_control &= ~CONTROL_RTS;
+	if (clear & TIOCM_DTR)
+		priv->line_control &= ~CONTROL_DTR;
+	control = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	ret = pl2303_set_control_lines(port, control);
+	if (ret)
+		return usb_translate_errors(ret);
+
+	return 0;
+}
+
+static int pl2303_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int mcr;
+	unsigned int status;
+	unsigned int result;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	mcr = priv->line_control;
+	status = priv->line_status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	result = ((mcr & CONTROL_DTR)		? TIOCM_DTR : 0)
+		  | ((mcr & CONTROL_RTS)	? TIOCM_RTS : 0)
+		  | ((status & UART_CTS)	? TIOCM_CTS : 0)
+		  | ((status & UART_DSR)	? TIOCM_DSR : 0)
+		  | ((status & UART_RING)	? TIOCM_RI  : 0)
+		  | ((status & UART_DCD)	? TIOCM_CD  : 0);
+
+	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
+
+	return result;
+}
+
+static int pl2303_carrier_raised(struct usb_serial_port *port)
+{
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+
+	if (priv->line_status & UART_DCD)
+		return 1;
+
+	return 0;
+}
+
+static int pl2303_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	struct serial_struct ser;
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		memset(&ser, 0, sizeof ser);
+		ser.type = PORT_16654;
+		ser.line = port->minor;
+		ser.port = port->port_number;
+		ser.baud_base = 460800;
+
+		if (copy_to_user((void __user *)arg, &ser, sizeof ser))
+			return -EFAULT;
+
+		return 0;
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static void pl2303_set_break(struct usb_serial_port *port, bool enable)
+{
+	struct usb_serial *serial = port->serial;
+	u16 state;
+	int result;
+
+	if (enable)
+		state = BREAK_ON;
+	else
+		state = BREAK_OFF;
+
+	dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
+			state == BREAK_OFF ? "off" : "on");
+
+	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				 BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
+				 0, NULL, 0, 100);
+	if (result)
+		dev_err(&port->dev, "error sending break = %d\n", result);
+}
+
+static void pl2303_break_ctl(struct tty_struct *tty, int state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	pl2303_set_break(port, state);
+}
+
+static void pl2303_update_line_status(struct usb_serial_port *port,
+				      unsigned char *data,
+				      unsigned int actual_length)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	unsigned long flags;
+	unsigned int status_idx = UART_STATE_INDEX;
+	u8 status;
+	u8 delta;
+
+	if (spriv->quirks & PL2303_QUIRK_UART_STATE_IDX0)
+		status_idx = 0;
+
+	if (actual_length < status_idx + 1)
+		return;
+
+	status = data[status_idx];
+
+	/* Save off the uart status for others to look at */
+	spin_lock_irqsave(&priv->lock, flags);
+	delta = priv->line_status ^ status;
+	priv->line_status = status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (status & UART_BREAK_ERROR)
+		usb_serial_handle_break(port);
+
+	if (delta & UART_STATE_MSR_MASK) {
+		if (delta & UART_CTS)
+			port->icount.cts++;
+		if (delta & UART_DSR)
+			port->icount.dsr++;
+		if (delta & UART_RING)
+			port->icount.rng++;
+		if (delta & UART_DCD) {
+			port->icount.dcd++;
+			tty = tty_port_tty_get(&port->port);
+			if (tty) {
+				usb_serial_handle_dcd_change(port, tty,
+							status & UART_DCD);
+				tty_kref_put(tty);
+			}
+		}
+
+		wake_up_interruptible(&port->port.delta_msr_wait);
+	}
+}
+
+static void pl2303_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port =  urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int actual_length = urb->actual_length;
+	int status = urb->status;
+	int retval;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__,
+			      urb->actual_length, urb->transfer_buffer);
+
+	pl2303_update_line_status(port, data, actual_length);
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval) {
+		dev_err(&port->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+	}
+}
+
+static void pl2303_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	char tty_flag = TTY_NORMAL;
+	unsigned long flags;
+	u8 line_status;
+	int i;
+
+	/* update line status */
+	spin_lock_irqsave(&priv->lock, flags);
+	line_status = priv->line_status;
+	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (!urb->actual_length)
+		return;
+
+	/*
+	 * Break takes precedence over parity, which takes precedence over
+	 * framing errors.
+	 */
+	if (line_status & UART_BREAK_ERROR)
+		tty_flag = TTY_BREAK;
+	else if (line_status & UART_PARITY_ERROR)
+		tty_flag = TTY_PARITY;
+	else if (line_status & UART_FRAME_ERROR)
+		tty_flag = TTY_FRAME;
+
+	if (tty_flag != TTY_NORMAL)
+		dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__,
+								tty_flag);
+	/* overrun is special, not associated with a char */
+	if (line_status & UART_OVERRUN_ERROR)
+		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+
+	if (port->port.console && port->sysrq) {
+		for (i = 0; i < urb->actual_length; ++i)
+			if (!usb_serial_handle_sysrq_char(port, data[i]))
+				tty_insert_flip_char(&port->port, data[i],
+						tty_flag);
+	} else {
+		tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
+							urb->actual_length);
+	}
+
+	tty_flip_buffer_push(&port->port);
+}
+
+static struct usb_serial_driver pl2303_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"pl2303",
+	},
+	.id_table =		id_table,
+	.num_bulk_in =		1,
+	.num_bulk_out =		1,
+	.num_interrupt_in =	0,	/* see pl2303_calc_num_ports */
+	.bulk_in_size =		256,
+	.bulk_out_size =	256,
+	.open =			pl2303_open,
+	.close =		pl2303_close,
+	.dtr_rts =		pl2303_dtr_rts,
+	.carrier_raised =	pl2303_carrier_raised,
+	.ioctl =		pl2303_ioctl,
+	.break_ctl =		pl2303_break_ctl,
+	.set_termios =		pl2303_set_termios,
+	.tiocmget =		pl2303_tiocmget,
+	.tiocmset =		pl2303_tiocmset,
+	.tiocmiwait =		usb_serial_generic_tiocmiwait,
+	.process_read_urb =	pl2303_process_read_urb,
+	.read_int_callback =	pl2303_read_int_callback,
+	.probe =		pl2303_probe,
+	.calc_num_ports =	pl2303_calc_num_ports,
+	.attach =		pl2303_startup,
+	.release =		pl2303_release,
+	.port_probe =		pl2303_port_probe,
+	.port_remove =		pl2303_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&pl2303_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION("Prolific PL2303 USB to serial adaptor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
new file mode 100644
index 0000000..26965cc
--- /dev/null
+++ b/drivers/usb/serial/pl2303.h
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Prolific PL2303 USB to serial adaptor driver header file
+ */
+
+#define BENQ_VENDOR_ID			0x04a5
+#define BENQ_PRODUCT_ID_S81		0x4027
+
+#define PL2303_VENDOR_ID	0x067b
+#define PL2303_PRODUCT_ID	0x2303
+#define PL2303_PRODUCT_ID_RSAQ2		0x04bb
+#define PL2303_PRODUCT_ID_DCU11		0x1234
+#define PL2303_PRODUCT_ID_PHAROS	0xaaa0
+#define PL2303_PRODUCT_ID_RSAQ3		0xaaa2
+#define PL2303_PRODUCT_ID_CHILITAG	0xaaa8
+#define PL2303_PRODUCT_ID_ALDIGA	0x0611
+#define PL2303_PRODUCT_ID_MMX		0x0612
+#define PL2303_PRODUCT_ID_GPRS		0x0609
+#define PL2303_PRODUCT_ID_HCR331	0x331a
+#define PL2303_PRODUCT_ID_MOTOROLA	0x0307
+#define PL2303_PRODUCT_ID_ZTEK		0xe1f1
+
+#define ATEN_VENDOR_ID		0x0557
+#define ATEN_VENDOR_ID2		0x0547
+#define ATEN_PRODUCT_ID		0x2008
+#define ATEN_PRODUCT_UC485	0x2021
+#define ATEN_PRODUCT_UC232B	0x2022
+#define ATEN_PRODUCT_ID2	0x2118
+
+#define IODATA_VENDOR_ID	0x04bb
+#define IODATA_PRODUCT_ID	0x0a03
+#define IODATA_PRODUCT_ID_RSAQ5	0x0a0e
+
+#define ELCOM_VENDOR_ID		0x056e
+#define ELCOM_PRODUCT_ID	0x5003
+#define ELCOM_PRODUCT_ID_UCSGT	0x5004
+
+#define ITEGNO_VENDOR_ID	0x0eba
+#define ITEGNO_PRODUCT_ID	0x1080
+#define ITEGNO_PRODUCT_ID_2080	0x2080
+
+#define MA620_VENDOR_ID		0x0df7
+#define MA620_PRODUCT_ID	0x0620
+
+#define RATOC_VENDOR_ID		0x0584
+#define RATOC_PRODUCT_ID	0xb000
+
+#define TRIPP_VENDOR_ID		0x2478
+#define TRIPP_PRODUCT_ID	0x2008
+
+#define RADIOSHACK_VENDOR_ID	0x1453
+#define RADIOSHACK_PRODUCT_ID	0x4026
+
+#define DCU10_VENDOR_ID		0x0731
+#define DCU10_PRODUCT_ID	0x0528
+
+#define SITECOM_VENDOR_ID	0x6189
+#define SITECOM_PRODUCT_ID	0x2068
+
+/* Alcatel OT535/735 USB cable */
+#define ALCATEL_VENDOR_ID	0x11f7
+#define ALCATEL_PRODUCT_ID	0x02df
+
+#define SIEMENS_VENDOR_ID	0x11f5
+#define SIEMENS_PRODUCT_ID_SX1	0x0001
+#define SIEMENS_PRODUCT_ID_X65	0x0003
+#define SIEMENS_PRODUCT_ID_X75	0x0004
+#define SIEMENS_PRODUCT_ID_EF81	0x0005
+
+#define SYNTECH_VENDOR_ID	0x0745
+#define SYNTECH_PRODUCT_ID	0x0001
+
+/* Nokia CA-42 Cable */
+#define NOKIA_CA42_VENDOR_ID	0x078b
+#define NOKIA_CA42_PRODUCT_ID	0x1234
+
+/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
+#define CA_42_CA42_VENDOR_ID	0x10b5
+#define CA_42_CA42_PRODUCT_ID	0xac70
+
+#define SAGEM_VENDOR_ID		0x079b
+#define SAGEM_PRODUCT_ID	0x0027
+
+/* Leadtek GPS 9531 (ID 0413:2101) */
+#define LEADTEK_VENDOR_ID	0x0413
+#define LEADTEK_9531_PRODUCT_ID	0x2101
+
+/* USB GSM cable from Speed Dragon Multimedia, Ltd */
+#define SPEEDDRAGON_VENDOR_ID	0x0e55
+#define SPEEDDRAGON_PRODUCT_ID	0x110b
+
+/* DATAPILOT Universal-2 Phone Cable */
+#define DATAPILOT_U2_VENDOR_ID	0x0731
+#define DATAPILOT_U2_PRODUCT_ID	0x2003
+
+/* Belkin "F5U257" Serial Adapter */
+#define BELKIN_VENDOR_ID	0x050d
+#define BELKIN_PRODUCT_ID	0x0257
+
+/* Alcor Micro Corp. USB 2.0 TO RS-232 */
+#define ALCOR_VENDOR_ID		0x058F
+#define ALCOR_PRODUCT_ID	0x9720
+
+/* Willcom WS002IN Data Driver (by NetIndex Inc.) */
+#define WS002IN_VENDOR_ID	0x11f6
+#define WS002IN_PRODUCT_ID	0x2001
+
+/* Corega CG-USBRS232R Serial Adapter */
+#define COREGA_VENDOR_ID	0x07aa
+#define COREGA_PRODUCT_ID	0x002a
+
+/* Y.C. Cable U.S.A., Inc - USB to RS-232 */
+#define YCCABLE_VENDOR_ID	0x05ad
+#define YCCABLE_PRODUCT_ID	0x0fba
+
+/* "Superial" USB - Serial */
+#define SUPERIAL_VENDOR_ID	0x5372
+#define SUPERIAL_PRODUCT_ID	0x2303
+
+/* Hewlett-Packard POS Pole Displays */
+#define HP_VENDOR_ID		0x03f0
+#define HP_LD960_PRODUCT_ID	0x0b39
+#define HP_LCM220_PRODUCT_ID	0x3139
+#define HP_LCM960_PRODUCT_ID	0x3239
+#define HP_LD220_PRODUCT_ID	0x3524
+
+/* Cressi Edy (diving computer) PC interface */
+#define CRESSI_VENDOR_ID	0x04b8
+#define CRESSI_EDY_PRODUCT_ID	0x0521
+
+/* Zeagle dive computer interface */
+#define ZEAGLE_VENDOR_ID	0x04b8
+#define ZEAGLE_N2ITION3_PRODUCT_ID	0x0522
+
+/* Sony, USB data cable for CMD-Jxx mobile phones */
+#define SONY_VENDOR_ID		0x054c
+#define SONY_QN3USB_PRODUCT_ID	0x0437
+
+/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */
+#define SANWA_VENDOR_ID		0x11ad
+#define SANWA_PRODUCT_ID	0x0001
+
+/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */
+#define ADLINK_VENDOR_ID		0x0b63
+#define ADLINK_ND6530_PRODUCT_ID	0x6530
+
+/* SMART USB Serial Adapter */
+#define SMART_VENDOR_ID	0x0b8c
+#define SMART_PRODUCT_ID	0x2303
+
diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c
new file mode 100644
index 0000000..929ffba
--- /dev/null
+++ b/drivers/usb/serial/qcaux.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm USB Auxiliary Serial Port driver
+ *
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ *
+ * Devices listed here usually provide a CDC ACM port on which normal modem
+ * AT commands and PPP can be used.  But when that port is in-use by PPP it
+ * cannot be used simultaneously for status or signal strength.  Instead, the
+ * ports here can be queried for that information using the Qualcomm DM
+ * protocol.
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* NOTE: for now, only use this driver for devices that provide a CDC-ACM port
+ * for normal AT commands, but also provide secondary USB interfaces for the
+ * QCDM-capable ports.  Devices that do not provide a CDC-ACM port should
+ * probably be driven by option.ko.
+ */
+
+/* UTStarcom/Pantech/Curitel devices */
+#define UTSTARCOM_VENDOR_ID			0x106c
+#define UTSTARCOM_PRODUCT_PC5740		0x3701
+#define UTSTARCOM_PRODUCT_PC5750		0x3702 /* aka Pantech PX-500 */
+#define UTSTARCOM_PRODUCT_UM150			0x3711
+#define UTSTARCOM_PRODUCT_UM175_V1		0x3712
+#define UTSTARCOM_PRODUCT_UM175_V2		0x3714
+#define UTSTARCOM_PRODUCT_UM175_ALLTEL		0x3715
+
+/* CMOTECH devices */
+#define CMOTECH_VENDOR_ID			0x16d8
+#define CMOTECH_PRODUCT_CDU550			0x5553
+#define CMOTECH_PRODUCT_CDX650			0x6512
+
+/* LG devices */
+#define LG_VENDOR_ID				0x1004
+#define LG_PRODUCT_VX4400_6000			0x6000 /* VX4400/VX6000/Rumor */
+
+/* Sanyo devices */
+#define SANYO_VENDOR_ID				0x0474
+#define SANYO_PRODUCT_KATANA_LX			0x0754 /* SCP-3800 (Katana LX) */
+
+/* Samsung devices */
+#define SAMSUNG_VENDOR_ID			0x04e8
+#define SAMSUNG_PRODUCT_U520			0x6640 /* SCH-U520 */
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5740, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5750, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM150, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_V1, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_V2, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_ALLTEL, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU550, 0xff, 0xff, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDX650, 0xff, 0xff, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfd, 0xff) },  /* NMEA */
+	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfe, 0xff) },  /* WMC */
+	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xff, 0xff) },  /* DIAG */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1fac, 0x0151, 0xff, 0xff, 0xff) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver qcaux_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"qcaux",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&qcaux_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
new file mode 100644
index 0000000..613f91a
--- /dev/null
+++ b/drivers/usb/serial/qcserial.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm Serial USB driver
+ *
+ *	Copyright (c) 2008 QUALCOMM Incorporated.
+ *	Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ *	Copyright (c) 2009 Novell Inc.
+ */
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/slab.h>
+#include "usb-wwan.h"
+
+#define DRIVER_AUTHOR "Qualcomm Inc"
+#define DRIVER_DESC "Qualcomm USB Serial driver"
+
+#define QUECTEL_EC20_PID	0x9215
+
+/* standard device layouts supported by this driver */
+enum qcserial_layouts {
+	QCSERIAL_G2K = 0,	/* Gobi 2000 */
+	QCSERIAL_G1K = 1,	/* Gobi 1000 */
+	QCSERIAL_SWI = 2,	/* Sierra Wireless */
+	QCSERIAL_HWI = 3,	/* Huawei */
+};
+
+#define DEVICE_G1K(v, p) \
+	USB_DEVICE(v, p), .driver_info = QCSERIAL_G1K
+#define DEVICE_SWI(v, p) \
+	USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
+#define DEVICE_HWI(v, p) \
+	USB_DEVICE(v, p), .driver_info = QCSERIAL_HWI
+
+static const struct usb_device_id id_table[] = {
+	/* Gobi 1000 devices */
+	{DEVICE_G1K(0x05c6, 0x9211)},	/* Acer Gobi QDL device */
+	{DEVICE_G1K(0x05c6, 0x9212)},	/* Acer Gobi Modem Device */
+	{DEVICE_G1K(0x03f0, 0x1f1d)},	/* HP un2400 Gobi Modem Device */
+	{DEVICE_G1K(0x03f0, 0x201d)},	/* HP un2400 Gobi QDL Device */
+	{DEVICE_G1K(0x04da, 0x250d)},	/* Panasonic Gobi Modem device */
+	{DEVICE_G1K(0x04da, 0x250c)},	/* Panasonic Gobi QDL device */
+	{DEVICE_G1K(0x413c, 0x8172)},	/* Dell Gobi Modem device */
+	{DEVICE_G1K(0x413c, 0x8171)},	/* Dell Gobi QDL device */
+	{DEVICE_G1K(0x1410, 0xa001)},	/* Novatel/Verizon USB-1000 */
+	{DEVICE_G1K(0x1410, 0xa002)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa003)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa004)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa005)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa006)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa007)},	/* Novatel Gobi Modem device */
+	{DEVICE_G1K(0x1410, 0xa008)},	/* Novatel Gobi QDL device */
+	{DEVICE_G1K(0x0b05, 0x1776)},	/* Asus Gobi Modem device */
+	{DEVICE_G1K(0x0b05, 0x1774)},	/* Asus Gobi QDL device */
+	{DEVICE_G1K(0x19d2, 0xfff3)},	/* ONDA Gobi Modem device */
+	{DEVICE_G1K(0x19d2, 0xfff2)},	/* ONDA Gobi QDL device */
+	{DEVICE_G1K(0x1557, 0x0a80)},	/* OQO Gobi QDL device */
+	{DEVICE_G1K(0x05c6, 0x9001)},   /* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9002)},	/* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9202)},	/* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9203)},	/* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9222)},	/* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9008)},	/* Generic Gobi QDL device */
+	{DEVICE_G1K(0x05c6, 0x9009)},	/* Generic Gobi Modem device */
+	{DEVICE_G1K(0x05c6, 0x9201)},	/* Generic Gobi QDL device */
+	{DEVICE_G1K(0x05c6, 0x9221)},	/* Generic Gobi QDL device */
+	{DEVICE_G1K(0x05c6, 0x9231)},	/* Generic Gobi QDL device */
+	{DEVICE_G1K(0x1f45, 0x0001)},	/* Unknown Gobi QDL device */
+	{DEVICE_G1K(0x1bc7, 0x900e)},	/* Telit Gobi QDL device */
+
+	/* Gobi 2000 devices */
+	{USB_DEVICE(0x1410, 0xa010)},	/* Novatel Gobi 2000 QDL device */
+	{USB_DEVICE(0x1410, 0xa011)},	/* Novatel Gobi 2000 QDL device */
+	{USB_DEVICE(0x1410, 0xa012)},	/* Novatel Gobi 2000 QDL device */
+	{USB_DEVICE(0x1410, 0xa013)},	/* Novatel Gobi 2000 QDL device */
+	{USB_DEVICE(0x1410, 0xa014)},	/* Novatel Gobi 2000 QDL device */
+	{USB_DEVICE(0x413c, 0x8185)},	/* Dell Gobi 2000 QDL device (N0218, VU936) */
+	{USB_DEVICE(0x413c, 0x8186)},	/* Dell Gobi 2000 Modem device (N0218, VU936) */
+	{USB_DEVICE(0x05c6, 0x9208)},	/* Generic Gobi 2000 QDL device */
+	{USB_DEVICE(0x05c6, 0x920b)},	/* Generic Gobi 2000 Modem device */
+	{USB_DEVICE(0x05c6, 0x9224)},	/* Sony Gobi 2000 QDL device (N0279, VU730) */
+	{USB_DEVICE(0x05c6, 0x9225)},	/* Sony Gobi 2000 Modem device (N0279, VU730) */
+	{USB_DEVICE(0x05c6, 0x9244)},	/* Samsung Gobi 2000 QDL device (VL176) */
+	{USB_DEVICE(0x05c6, 0x9245)},	/* Samsung Gobi 2000 Modem device (VL176) */
+	{USB_DEVICE(0x03f0, 0x241d)},	/* HP Gobi 2000 QDL device (VP412) */
+	{USB_DEVICE(0x03f0, 0x251d)},	/* HP Gobi 2000 Modem device (VP412) */
+	{USB_DEVICE(0x05c6, 0x9214)},	/* Acer Gobi 2000 QDL device (VP413) */
+	{USB_DEVICE(0x05c6, 0x9215)},	/* Acer Gobi 2000 Modem device (VP413) */
+	{USB_DEVICE(0x05c6, 0x9264)},	/* Asus Gobi 2000 QDL device (VR305) */
+	{USB_DEVICE(0x05c6, 0x9265)},	/* Asus Gobi 2000 Modem device (VR305) */
+	{USB_DEVICE(0x05c6, 0x9234)},	/* Top Global Gobi 2000 QDL device (VR306) */
+	{USB_DEVICE(0x05c6, 0x9235)},	/* Top Global Gobi 2000 Modem device (VR306) */
+	{USB_DEVICE(0x05c6, 0x9274)},	/* iRex Technologies Gobi 2000 QDL device (VR307) */
+	{USB_DEVICE(0x05c6, 0x9275)},	/* iRex Technologies Gobi 2000 Modem device (VR307) */
+	{USB_DEVICE(0x1199, 0x9000)},	/* Sierra Wireless Gobi 2000 QDL device (VT773) */
+	{USB_DEVICE(0x1199, 0x9001)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9002)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9003)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9004)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9005)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9006)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9007)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9008)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9009)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x900a)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+	{USB_DEVICE(0x1199, 0x9011)},   /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
+	{USB_DEVICE(0x16d8, 0x8001)},	/* CMDTech Gobi 2000 QDL device (VU922) */
+	{USB_DEVICE(0x16d8, 0x8002)},	/* CMDTech Gobi 2000 Modem device (VU922) */
+	{USB_DEVICE(0x05c6, 0x9204)},	/* Gobi 2000 QDL device */
+	{USB_DEVICE(0x05c6, 0x9205)},	/* Gobi 2000 Modem device */
+
+	/* Gobi 3000 devices */
+	{USB_DEVICE(0x03f0, 0x371d)},	/* HP un2430 Gobi 3000 QDL */
+	{USB_DEVICE(0x05c6, 0x920c)},	/* Gobi 3000 QDL */
+	{USB_DEVICE(0x05c6, 0x920d)},	/* Gobi 3000 Composite */
+	{USB_DEVICE(0x1410, 0xa020)},   /* Novatel Gobi 3000 QDL */
+	{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 */
+	{USB_DEVICE(0x1199, 0x68a9)},	/* Sierra Wireless Modem */
+	{USB_DEVICE(0x1199, 0x9010)},	/* Sierra Wireless Gobi 3000 QDL */
+	{USB_DEVICE(0x1199, 0x9012)},	/* Sierra Wireless Gobi 3000 QDL */
+	{USB_DEVICE(0x1199, 0x9013)},	/* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+	{USB_DEVICE(0x1199, 0x9014)},	/* Sierra Wireless Gobi 3000 QDL */
+	{USB_DEVICE(0x1199, 0x9015)},	/* Sierra Wireless Gobi 3000 Modem device */
+	{USB_DEVICE(0x1199, 0x9018)},	/* Sierra Wireless Gobi 3000 QDL */
+	{USB_DEVICE(0x1199, 0x9019)},	/* Sierra Wireless Gobi 3000 Modem device */
+	{USB_DEVICE(0x1199, 0x901b)},	/* Sierra Wireless MC7770 */
+	{USB_DEVICE(0x12D1, 0x14F0)},	/* Sony Gobi 3000 QDL */
+	{USB_DEVICE(0x12D1, 0x14F1)},	/* Sony Gobi 3000 Composite */
+	{USB_DEVICE(0x0AF0, 0x8120)},	/* Option GTM681W */
+
+	/* non-Gobi Sierra Wireless devices */
+	{DEVICE_SWI(0x03f0, 0x4e1d)},	/* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+	{DEVICE_SWI(0x0f3d, 0x68a2)},	/* Sierra Wireless MC7700 */
+	{DEVICE_SWI(0x114f, 0x68a2)},	/* Sierra Wireless MC7750 */
+	{DEVICE_SWI(0x1199, 0x68a2)},	/* Sierra Wireless MC7710 */
+	{DEVICE_SWI(0x1199, 0x68c0)},	/* Sierra Wireless MC7304/MC7354 */
+	{DEVICE_SWI(0x1199, 0x901c)},	/* Sierra Wireless EM7700 */
+	{DEVICE_SWI(0x1199, 0x901e)},	/* Sierra Wireless EM7355 QDL */
+	{DEVICE_SWI(0x1199, 0x901f)},	/* Sierra Wireless EM7355 */
+	{DEVICE_SWI(0x1199, 0x9040)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9041)},	/* Sierra Wireless MC7305/MC7355 */
+	{DEVICE_SWI(0x1199, 0x9051)},	/* Netgear AirCard 340U */
+	{DEVICE_SWI(0x1199, 0x9053)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9054)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9055)},	/* Netgear AirCard 341U */
+	{DEVICE_SWI(0x1199, 0x9056)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9060)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9061)},	/* Sierra Wireless Modem */
+	{DEVICE_SWI(0x1199, 0x9063)},	/* Sierra Wireless EM7305 */
+	{DEVICE_SWI(0x1199, 0x9070)},	/* Sierra Wireless MC74xx */
+	{DEVICE_SWI(0x1199, 0x9071)},	/* Sierra Wireless MC74xx */
+	{DEVICE_SWI(0x1199, 0x9078)},	/* Sierra Wireless EM74xx */
+	{DEVICE_SWI(0x1199, 0x9079)},	/* Sierra Wireless EM74xx */
+	{DEVICE_SWI(0x1199, 0x907a)},	/* Sierra Wireless EM74xx QDL */
+	{DEVICE_SWI(0x1199, 0x907b)},	/* Sierra Wireless EM74xx */
+	{DEVICE_SWI(0x1199, 0x9090)},	/* Sierra Wireless EM7565 QDL */
+	{DEVICE_SWI(0x1199, 0x9091)},	/* Sierra Wireless EM7565 */
+	{DEVICE_SWI(0x413c, 0x81a2)},	/* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81a3)},	/* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81a4)},	/* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81a8)},	/* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81a9)},	/* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81b1)},	/* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
+	{DEVICE_SWI(0x413c, 0x81b3)},	/* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
+	{DEVICE_SWI(0x413c, 0x81b5)},	/* Dell Wireless 5811e QDL */
+	{DEVICE_SWI(0x413c, 0x81b6)},	/* Dell Wireless 5811e QDL */
+	{DEVICE_SWI(0x413c, 0x81cf)},   /* Dell Wireless 5819 */
+	{DEVICE_SWI(0x413c, 0x81d0)},   /* Dell Wireless 5819 */
+	{DEVICE_SWI(0x413c, 0x81d1)},   /* Dell Wireless 5818 */
+	{DEVICE_SWI(0x413c, 0x81d2)},   /* Dell Wireless 5818 */
+
+	/* Huawei devices */
+	{DEVICE_HWI(0x03f0, 0x581d)},	/* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
+
+	{ }				/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int handle_quectel_ec20(struct device *dev, int ifnum)
+{
+	int altsetting = 0;
+
+	/*
+	 * Quectel EC20 Mini PCIe LTE module layout:
+	 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+	 * 1: NMEA
+	 * 2: AT-capable modem port
+	 * 3: Modem interface
+	 * 4: NDIS
+	 */
+	switch (ifnum) {
+	case 0:
+		dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
+		break;
+	case 1:
+		dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
+		break;
+	case 2:
+	case 3:
+		dev_dbg(dev, "Quectel EC20 Modem port found\n");
+		break;
+	case 4:
+		/* Don't claim the QMI/net interface */
+		altsetting = -1;
+		break;
+	}
+
+	return altsetting;
+}
+
+static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+	struct usb_host_interface *intf = serial->interface->cur_altsetting;
+	struct device *dev = &serial->dev->dev;
+	int retval = -ENODEV;
+	__u8 nintf;
+	__u8 ifnum;
+	int altsetting = -1;
+	bool sendsetup = false;
+
+	/* we only support vendor specific functions */
+	if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+		goto done;
+
+	nintf = serial->dev->actconfig->desc.bNumInterfaces;
+	dev_dbg(dev, "Num Interfaces = %d\n", nintf);
+	ifnum = intf->desc.bInterfaceNumber;
+	dev_dbg(dev, "This Interface = %d\n", ifnum);
+
+	if (nintf == 1) {
+		/* QDL mode */
+		/* Gobi 2000 has a single altsetting, older ones have two */
+		if (serial->interface->num_altsetting == 2)
+			intf = &serial->interface->altsetting[1];
+		else if (serial->interface->num_altsetting > 2)
+			goto done;
+
+		if (intf->desc.bNumEndpoints == 2 &&
+		    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
+		    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
+			dev_dbg(dev, "QDL port found\n");
+
+			if (serial->interface->num_altsetting == 1)
+				retval = 0; /* Success */
+			else
+				altsetting = 1;
+		}
+		goto done;
+
+	}
+
+	/* default to enabling interface */
+	altsetting = 0;
+
+	/*
+	 * Composite mode; don't bind to the QMI/net interface as that
+	 * gets handled by other drivers.
+	 */
+
+	switch (id->driver_info) {
+	case QCSERIAL_G1K:
+		/*
+		 * Gobi 1K USB layout:
+		 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+		 * 1: serial port (doesn't respond)
+		 * 2: AT-capable modem port
+		 * 3: QMI/net
+		 */
+		if (nintf < 3 || nintf > 4) {
+			dev_err(dev, "unknown number of interfaces: %d\n", nintf);
+			altsetting = -1;
+			goto done;
+		}
+
+		if (ifnum == 0) {
+			dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
+			altsetting = 1;
+		} else if (ifnum == 2)
+			dev_dbg(dev, "Modem port found\n");
+		else
+			altsetting = -1;
+		break;
+	case QCSERIAL_G2K:
+		/* handle non-standard layouts */
+		if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
+			altsetting = handle_quectel_ec20(dev, ifnum);
+			goto done;
+		}
+
+		/*
+		 * Gobi 2K+ USB layout:
+		 * 0: QMI/net
+		 * 1: DM/DIAG (use libqcdm from ModemManager for communication)
+		 * 2: AT-capable modem port
+		 * 3: NMEA
+		 */
+		if (nintf < 3 || nintf > 4) {
+			dev_err(dev, "unknown number of interfaces: %d\n", nintf);
+			altsetting = -1;
+			goto done;
+		}
+
+		switch (ifnum) {
+		case 0:
+			/* Don't claim the QMI/net interface */
+			altsetting = -1;
+			break;
+		case 1:
+			dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
+			break;
+		case 2:
+			dev_dbg(dev, "Modem port found\n");
+			break;
+		case 3:
+			/*
+			 * NMEA (serial line 9600 8N1)
+			 * # echo "\$GPS_START" > /dev/ttyUSBx
+			 * # echo "\$GPS_STOP"  > /dev/ttyUSBx
+			 */
+			dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
+			break;
+		}
+		break;
+	case QCSERIAL_SWI:
+		/*
+		 * Sierra Wireless layout:
+		 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+		 * 2: NMEA
+		 * 3: AT-capable modem port
+		 * 8: QMI/net
+		 */
+		switch (ifnum) {
+		case 0:
+			dev_dbg(dev, "DM/DIAG interface found\n");
+			break;
+		case 2:
+			dev_dbg(dev, "NMEA GPS interface found\n");
+			sendsetup = true;
+			break;
+		case 3:
+			dev_dbg(dev, "Modem port found\n");
+			sendsetup = true;
+			break;
+		default:
+			/* don't claim any unsupported interface */
+			altsetting = -1;
+			break;
+		}
+		break;
+	case QCSERIAL_HWI:
+		/*
+		 * Huawei devices map functions by subclass + protocol
+		 * instead of interface numbers. The protocol identify
+		 * a specific function, while the subclass indicate a
+		 * specific firmware source
+		 *
+		 * This is a blacklist of functions known to be
+		 * non-serial.  The rest are assumed to be serial and
+		 * will be handled by this driver
+		 */
+		switch (intf->desc.bInterfaceProtocol) {
+			/* QMI combined (qmi_wwan) */
+		case 0x07:
+		case 0x37:
+		case 0x67:
+			/* QMI data (qmi_wwan) */
+		case 0x08:
+		case 0x38:
+		case 0x68:
+			/* QMI control (qmi_wwan) */
+		case 0x09:
+		case 0x39:
+		case 0x69:
+			/* NCM like (huawei_cdc_ncm) */
+		case 0x16:
+		case 0x46:
+		case 0x76:
+			altsetting = -1;
+			break;
+		default:
+			dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
+				intf->desc.bInterfaceClass,
+				intf->desc.bInterfaceSubClass,
+				intf->desc.bInterfaceProtocol);
+		}
+		break;
+	default:
+		dev_err(dev, "unsupported device layout type: %lu\n",
+			id->driver_info);
+		break;
+	}
+
+done:
+	if (altsetting >= 0) {
+		retval = usb_set_interface(serial->dev, ifnum, altsetting);
+		if (retval < 0) {
+			dev_err(dev,
+				"Could not set interface, error %d\n",
+				retval);
+			retval = -ENODEV;
+		}
+	}
+
+	if (!retval)
+		usb_set_serial_data(serial, (void *)(unsigned long)sendsetup);
+
+	return retval;
+}
+
+static int qc_attach(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *data;
+	bool sendsetup;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	sendsetup = !!(unsigned long)(usb_get_serial_data(serial));
+	if (sendsetup)
+		data->use_send_setup = 1;
+
+	spin_lock_init(&data->susp_lock);
+
+	usb_set_serial_data(serial, data);
+
+	return 0;
+}
+
+static void qc_release(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+	usb_set_serial_data(serial, NULL);
+	kfree(priv);
+}
+
+static struct usb_serial_driver qcdevice = {
+	.driver = {
+		.owner     = THIS_MODULE,
+		.name      = "qcserial",
+	},
+	.description         = "Qualcomm USB modem",
+	.id_table            = id_table,
+	.num_ports           = 1,
+	.probe               = qcprobe,
+	.open		     = usb_wwan_open,
+	.close		     = usb_wwan_close,
+	.dtr_rts	     = usb_wwan_dtr_rts,
+	.write		     = usb_wwan_write,
+	.write_room	     = usb_wwan_write_room,
+	.chars_in_buffer     = usb_wwan_chars_in_buffer,
+	.tiocmget            = usb_wwan_tiocmget,
+	.tiocmset            = usb_wwan_tiocmset,
+	.attach              = qc_attach,
+	.release	     = qc_release,
+	.port_probe          = usb_wwan_port_probe,
+	.port_remove	     = usb_wwan_port_remove,
+#ifdef CONFIG_PM
+	.suspend	     = usb_wwan_suspend,
+	.resume		     = usb_wwan_resume,
+#endif
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&qcdevice, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
new file mode 100644
index 0000000..b61c2a9
--- /dev/null
+++ b/drivers/usb/serial/quatech2.c
@@ -0,0 +1,1027 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * usb-serial driver for Quatech USB 2 devices
+ *
+ * Copyright (C) 2012 Bill Pemberton (wfp5p@virginia.edu)
+ *
+ *  These devices all have only 1 bulk in and 1 bulk out that is shared
+ *  for all serial ports.
+ *
+ */
+
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/uaccess.h>
+
+/* default urb timeout for usb operations */
+#define QT2_USB_TIMEOUT USB_CTRL_SET_TIMEOUT
+
+#define QT_OPEN_CLOSE_CHANNEL       0xca
+#define QT_SET_GET_DEVICE           0xc2
+#define QT_SET_GET_REGISTER         0xc0
+#define QT_GET_SET_PREBUF_TRIG_LVL  0xcc
+#define QT_SET_ATF                  0xcd
+#define QT_TRANSFER_IN              0xc0
+#define QT_HW_FLOW_CONTROL_MASK     0xc5
+#define QT_SW_FLOW_CONTROL_MASK     0xc6
+#define QT2_BREAK_CONTROL	    0xc8
+#define QT2_GET_SET_UART            0xc1
+#define QT2_FLUSH_DEVICE	    0xc4
+#define QT2_GET_SET_QMCR            0xe1
+#define QT2_QMCR_RS232              0x40
+#define QT2_QMCR_RS422              0x10
+
+#define  SERIAL_CRTSCTS ((UART_MCR_RTS << 8) | UART_MSR_CTS)
+
+#define  SERIAL_EVEN_PARITY         (UART_LCR_PARITY | UART_LCR_EPAR)
+
+/* status bytes for the device */
+#define QT2_CONTROL_BYTE    0x1b
+#define QT2_LINE_STATUS     0x00  /* following 1 byte is line status */
+#define QT2_MODEM_STATUS    0x01  /* following 1 byte is modem status */
+#define QT2_XMIT_HOLD       0x02  /* following 2 bytes are ?? */
+#define QT2_CHANGE_PORT     0x03  /* following 1 byte is port to change to */
+#define QT2_REC_FLUSH       0x04  /* no following info */
+#define QT2_XMIT_FLUSH      0x05  /* no following info */
+#define QT2_CONTROL_ESCAPE  0xff  /* pass through previous 2 control bytes */
+
+#define  MAX_BAUD_RATE              921600
+#define  DEFAULT_BAUD_RATE          9600
+
+#define QT2_READ_BUFFER_SIZE    512  /* size of read buffer */
+#define QT2_WRITE_BUFFER_SIZE   512  /* size of write buffer */
+#define QT2_WRITE_CONTROL_SIZE  5    /* control bytes used for a write */
+
+#define DRIVER_DESC "Quatech 2nd gen USB to Serial Driver"
+
+#define	USB_VENDOR_ID_QUATECH	0x061d
+#define QUATECH_SSU2_100	0xC120	/* RS232 single port */
+#define QUATECH_DSU2_100	0xC140	/* RS232 dual port */
+#define QUATECH_DSU2_400	0xC150	/* RS232/422/485 dual port */
+#define QUATECH_QSU2_100	0xC160	/* RS232 four port */
+#define QUATECH_QSU2_400	0xC170	/* RS232/422/485 four port */
+#define QUATECH_ESU2_100	0xC1A0	/* RS232 eight port */
+#define QUATECH_ESU2_400	0xC180	/* RS232/422/485 eight port */
+
+struct qt2_device_detail {
+	int product_id;
+	int num_ports;
+};
+
+#define QT_DETAILS(prod, ports)	\
+	.product_id = (prod),   \
+	.num_ports = (ports)
+
+static const struct qt2_device_detail qt2_device_details[] = {
+	{QT_DETAILS(QUATECH_SSU2_100, 1)},
+	{QT_DETAILS(QUATECH_DSU2_400, 2)},
+	{QT_DETAILS(QUATECH_DSU2_100, 2)},
+	{QT_DETAILS(QUATECH_QSU2_400, 4)},
+	{QT_DETAILS(QUATECH_QSU2_100, 4)},
+	{QT_DETAILS(QUATECH_ESU2_400, 8)},
+	{QT_DETAILS(QUATECH_ESU2_100, 8)},
+	{QT_DETAILS(0, 0)}	/* Terminating entry */
+};
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU2_100)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_DSU2_100)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_DSU2_400)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_QSU2_100)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_QSU2_400)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU2_100)},
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU2_400)},
+	{}			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct qt2_serial_private {
+	unsigned char current_port;  /* current port for incoming data */
+
+	struct urb	*read_urb;   /* shared among all ports */
+	char		*read_buffer;
+};
+
+struct qt2_port_private {
+	u8   device_port;
+
+	spinlock_t urb_lock;
+	bool       urb_in_use;
+	struct urb *write_urb;
+	char       *write_buffer;
+
+	spinlock_t  lock;
+	u8          shadowLSR;
+	u8          shadowMSR;
+
+	struct usb_serial_port *port;
+};
+
+static void qt2_update_lsr(struct usb_serial_port *port, unsigned char *ch);
+static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch);
+static void qt2_write_bulk_callback(struct urb *urb);
+static void qt2_read_bulk_callback(struct urb *urb);
+
+static void qt2_release(struct usb_serial *serial)
+{
+	struct qt2_serial_private *serial_priv;
+
+	serial_priv = usb_get_serial_data(serial);
+
+	usb_kill_urb(serial_priv->read_urb);
+	usb_free_urb(serial_priv->read_urb);
+	kfree(serial_priv->read_buffer);
+	kfree(serial_priv);
+}
+
+static inline int calc_baud_divisor(int baudrate)
+{
+	int divisor, rem;
+
+	divisor = MAX_BAUD_RATE / baudrate;
+	rem = MAX_BAUD_RATE % baudrate;
+	/* Round to nearest divisor */
+	if (((rem * 2) >= baudrate) && (baudrate != 110))
+		divisor++;
+
+	return divisor;
+}
+
+static inline int qt2_set_port_config(struct usb_device *dev,
+				      unsigned char port_number,
+				      u16 baudrate, u16 lcr)
+{
+	int divisor = calc_baud_divisor(baudrate);
+	u16 index = ((u16) (lcr << 8) | (u16) (port_number));
+
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       QT2_GET_SET_UART, 0x40,
+			       divisor, index, NULL, 0, QT2_USB_TIMEOUT);
+}
+
+static inline int qt2_control_msg(struct usb_device *dev,
+				  u8 request, u16 data, u16 index)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       request, 0x40, data, index,
+			       NULL, 0, QT2_USB_TIMEOUT);
+}
+
+static inline int qt2_setdevice(struct usb_device *dev, u8 *data)
+{
+	u16 x = ((u16) (data[1] << 8) | (u16) (data[0]));
+
+	return qt2_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
+}
+
+
+static inline int qt2_getregister(struct usb_device *dev,
+				  u8 uart,
+				  u8 reg,
+				  u8 *data)
+{
+	int ret;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			      QT_SET_GET_REGISTER, 0xc0, reg,
+			      uart, data, sizeof(*data), QT2_USB_TIMEOUT);
+	if (ret < (int)sizeof(*data)) {
+		if (ret >= 0)
+			ret = -EIO;
+	}
+
+	return ret;
+}
+
+static inline int qt2_setregister(struct usb_device *dev,
+				  u8 uart, u8 reg, u16 data)
+{
+	u16 value = (data << 8) | reg;
+
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       QT_SET_GET_REGISTER, 0x40, value, uart,
+			       NULL, 0, QT2_USB_TIMEOUT);
+}
+
+static inline int update_mctrl(struct qt2_port_private *port_priv,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = port_priv->port;
+	struct usb_device *dev = port->serial->dev;
+	unsigned urb_value;
+	int status;
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+		dev_dbg(&port->dev,
+			"update_mctrl - DTR|RTS not being set|cleared\n");
+		return 0;	/* no change */
+	}
+
+	clear &= ~set;	/* 'set' takes precedence over 'clear' */
+	urb_value = 0;
+	if (set & TIOCM_DTR)
+		urb_value |= UART_MCR_DTR;
+	if (set & TIOCM_RTS)
+		urb_value |= UART_MCR_RTS;
+
+	status = qt2_setregister(dev, port_priv->device_port, UART_MCR,
+				 urb_value);
+	if (status < 0)
+		dev_err(&port->dev,
+			"update_mctrl - Error from MODEM_CTRL urb: %i\n",
+			status);
+	return status;
+}
+
+static int qt2_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	struct qt2_device_detail d;
+	int i;
+
+	for (i = 0; d = qt2_device_details[i], d.product_id != 0; i++) {
+		if (d.product_id == le16_to_cpu(serial->dev->descriptor.idProduct))
+			return d.num_ports;
+	}
+
+	/* we didn't recognize the device */
+	dev_err(&serial->dev->dev,
+		 "don't know the number of ports, assuming 1\n");
+
+	return 1;
+}
+
+static void qt2_set_termios(struct tty_struct *tty,
+			    struct usb_serial_port *port,
+			    struct ktermios *old_termios)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct qt2_port_private *port_priv;
+	struct ktermios *termios = &tty->termios;
+	u16 baud;
+	unsigned int cflag = termios->c_cflag;
+	u16 new_lcr = 0;
+	int status;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			new_lcr |= UART_LCR_PARITY;
+		else
+			new_lcr |= SERIAL_EVEN_PARITY;
+	}
+
+	switch (cflag & CSIZE) {
+	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)
+		baud = 9600;
+
+	status = qt2_set_port_config(dev, port_priv->device_port, baud,
+				     new_lcr);
+	if (status < 0)
+		dev_err(&port->dev, "%s - qt2_set_port_config failed: %i\n",
+			__func__, status);
+
+	if (cflag & CRTSCTS)
+		status = qt2_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+					 SERIAL_CRTSCTS,
+					 port_priv->device_port);
+	else
+		status = qt2_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+					 0, port_priv->device_port);
+	if (status < 0)
+		dev_err(&port->dev, "%s - set HW flow control failed: %i\n",
+			__func__, status);
+
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		u16 x = ((u16) (START_CHAR(tty) << 8) | (u16) (STOP_CHAR(tty)));
+
+		status = qt2_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+					 x, port_priv->device_port);
+	} else
+		status = qt2_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+					 0, port_priv->device_port);
+
+	if (status < 0)
+		dev_err(&port->dev, "%s - set SW flow control failed: %i\n",
+			__func__, status);
+
+}
+
+static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct qt2_port_private *port_priv;
+	u8 *data;
+	u16 device_port;
+	int status;
+	unsigned long flags;
+
+	device_port = port->port_number;
+
+	serial = port->serial;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	/* set the port to RS232 mode */
+	status = qt2_control_msg(serial->dev, QT2_GET_SET_QMCR,
+				 QT2_QMCR_RS232, device_port);
+	if (status < 0) {
+		dev_err(&port->dev,
+			"%s failed to set RS232 mode for port %i error %i\n",
+			__func__, device_port, status);
+		return status;
+	}
+
+	data = kzalloc(2, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* open the port */
+	status = usb_control_msg(serial->dev,
+				 usb_rcvctrlpipe(serial->dev, 0),
+				 QT_OPEN_CLOSE_CHANNEL,
+				 0xc0, 0,
+				 device_port, data, 2, QT2_USB_TIMEOUT);
+
+	if (status < 2) {
+		dev_err(&port->dev, "%s - open port failed %i\n", __func__,
+			status);
+		if (status >= 0)
+			status = -EIO;
+		kfree(data);
+		return status;
+	}
+
+	spin_lock_irqsave(&port_priv->lock, flags);
+	port_priv->shadowLSR = data[0];
+	port_priv->shadowMSR = data[1];
+	spin_unlock_irqrestore(&port_priv->lock, flags);
+
+	kfree(data);
+
+	/* set to default speed and 8bit word size */
+	status = qt2_set_port_config(serial->dev, device_port,
+				     DEFAULT_BAUD_RATE, UART_LCR_WLEN8);
+	if (status < 0) {
+		dev_err(&port->dev, "%s - initial setup failed (%i)\n",
+			__func__, device_port);
+		return status;
+	}
+
+	port_priv->device_port = (u8) device_port;
+
+	if (tty)
+		qt2_set_termios(tty, port, &tty->termios);
+
+	return 0;
+
+}
+
+static void qt2_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct qt2_port_private *port_priv;
+	int i;
+
+	serial = port->serial;
+	port_priv = usb_get_serial_port_data(port);
+
+	usb_kill_urb(port_priv->write_urb);
+
+	/* flush the port transmit buffer */
+	i = usb_control_msg(serial->dev,
+			    usb_rcvctrlpipe(serial->dev, 0),
+			    QT2_FLUSH_DEVICE, 0x40, 1,
+			    port_priv->device_port, NULL, 0, QT2_USB_TIMEOUT);
+
+	if (i < 0)
+		dev_err(&port->dev, "%s - transmit buffer flush failed: %i\n",
+			__func__, i);
+
+	/* flush the port receive buffer */
+	i = usb_control_msg(serial->dev,
+			    usb_rcvctrlpipe(serial->dev, 0),
+			    QT2_FLUSH_DEVICE, 0x40, 0,
+			    port_priv->device_port, NULL, 0, QT2_USB_TIMEOUT);
+
+	if (i < 0)
+		dev_err(&port->dev, "%s - receive buffer flush failed: %i\n",
+			__func__, i);
+
+	/* close the port */
+	i = usb_control_msg(serial->dev,
+			    usb_sndctrlpipe(serial->dev, 0),
+			    QT_OPEN_CLOSE_CHANNEL,
+			    0x40, 0,
+			    port_priv->device_port, NULL, 0, QT2_USB_TIMEOUT);
+
+	if (i < 0)
+		dev_err(&port->dev, "%s - close port failed %i\n",
+			__func__, i);
+}
+
+static void qt2_disconnect(struct usb_serial *serial)
+{
+	struct qt2_serial_private *serial_priv = usb_get_serial_data(serial);
+
+	usb_kill_urb(serial_priv->read_urb);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line		= port->minor;
+	tmp.port		= 0;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= port->bulk_out_size;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int qt2_ioctl(struct tty_struct *tty,
+		     unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *)arg);
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static void qt2_process_status(struct usb_serial_port *port, unsigned char *ch)
+{
+	switch (*ch) {
+	case QT2_LINE_STATUS:
+		qt2_update_lsr(port, ch + 1);
+		break;
+	case QT2_MODEM_STATUS:
+		qt2_update_msr(port, ch + 1);
+		break;
+	}
+}
+
+/* not needed, kept to document functionality */
+static void qt2_process_xmit_empty(struct usb_serial_port *port,
+				   unsigned char *ch)
+{
+	int bytes_written;
+
+	bytes_written = (int)(*ch) + (int)(*(ch + 1) << 4);
+}
+
+/* not needed, kept to document functionality */
+static void qt2_process_flush(struct usb_serial_port *port, unsigned char *ch)
+{
+	return;
+}
+
+static void qt2_process_read_urb(struct urb *urb)
+{
+	struct usb_serial *serial;
+	struct qt2_serial_private *serial_priv;
+	struct usb_serial_port *port;
+	struct qt2_port_private *port_priv;
+	bool escapeflag;
+	unsigned char *ch;
+	int i;
+	unsigned char newport;
+	int len = urb->actual_length;
+
+	if (!len)
+		return;
+
+	ch = urb->transfer_buffer;
+	serial = urb->context;
+	serial_priv = usb_get_serial_data(serial);
+	port = serial->port[serial_priv->current_port];
+	port_priv = usb_get_serial_port_data(port);
+
+	for (i = 0; i < urb->actual_length; i++) {
+		ch = (unsigned char *)urb->transfer_buffer + i;
+		if ((i <= (len - 3)) &&
+		    (*ch == QT2_CONTROL_BYTE) &&
+		    (*(ch + 1) == QT2_CONTROL_BYTE)) {
+			escapeflag = false;
+			switch (*(ch + 2)) {
+			case QT2_LINE_STATUS:
+			case QT2_MODEM_STATUS:
+				if (i > (len - 4)) {
+					dev_warn(&port->dev,
+						 "%s - status message too short\n",
+						__func__);
+					break;
+				}
+				qt2_process_status(port, ch + 2);
+				i += 3;
+				escapeflag = true;
+				break;
+			case QT2_XMIT_HOLD:
+				if (i > (len - 5)) {
+					dev_warn(&port->dev,
+						 "%s - xmit_empty message too short\n",
+						 __func__);
+					break;
+				}
+				qt2_process_xmit_empty(port, ch + 3);
+				i += 4;
+				escapeflag = true;
+				break;
+			case QT2_CHANGE_PORT:
+				if (i > (len - 4)) {
+					dev_warn(&port->dev,
+						 "%s - change_port message too short\n",
+						 __func__);
+					break;
+				}
+				tty_flip_buffer_push(&port->port);
+
+				newport = *(ch + 3);
+
+				if (newport > serial->num_ports) {
+					dev_err(&port->dev,
+						"%s - port change to invalid port: %i\n",
+						__func__, newport);
+					break;
+				}
+
+				serial_priv->current_port = newport;
+				port = serial->port[serial_priv->current_port];
+				port_priv = usb_get_serial_port_data(port);
+				i += 3;
+				escapeflag = true;
+				break;
+			case QT2_REC_FLUSH:
+			case QT2_XMIT_FLUSH:
+				qt2_process_flush(port, ch + 2);
+				i += 2;
+				escapeflag = true;
+				break;
+			case QT2_CONTROL_ESCAPE:
+				tty_insert_flip_string(&port->port, ch, 2);
+				i += 2;
+				escapeflag = true;
+				break;
+			default:
+				dev_warn(&port->dev,
+					 "%s - unsupported command %i\n",
+					 __func__, *(ch + 2));
+				break;
+			}
+			if (escapeflag)
+				continue;
+		}
+
+		tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
+	}
+
+	tty_flip_buffer_push(&port->port);
+}
+
+static void qt2_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct qt2_port_private *port_priv;
+	unsigned long flags;
+
+	port = urb->context;
+	port_priv = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&port_priv->urb_lock, flags);
+
+	port_priv->urb_in_use = false;
+	usb_serial_port_softint(port);
+
+	spin_unlock_irqrestore(&port_priv->urb_lock, flags);
+
+}
+
+static void qt2_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial *serial = urb->context;
+	int status;
+
+	if (urb->status) {
+		dev_warn(&serial->dev->dev,
+			 "%s - non-zero urb status: %i\n", __func__,
+			 urb->status);
+		return;
+	}
+
+	qt2_process_read_urb(urb);
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status != 0)
+		dev_err(&serial->dev->dev,
+			"%s - resubmit read urb failed: %i\n",
+			__func__, status);
+}
+
+static int qt2_setup_urbs(struct usb_serial *serial)
+{
+	struct usb_serial_port *port0;
+	struct qt2_serial_private *serial_priv;
+	int status;
+
+	port0 = serial->port[0];
+
+	serial_priv = usb_get_serial_data(serial);
+	serial_priv->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!serial_priv->read_urb)
+		return -ENOMEM;
+
+	usb_fill_bulk_urb(serial_priv->read_urb, serial->dev,
+			  usb_rcvbulkpipe(serial->dev,
+					  port0->bulk_in_endpointAddress),
+			  serial_priv->read_buffer,
+			  QT2_READ_BUFFER_SIZE,
+			  qt2_read_bulk_callback, serial);
+
+	status = usb_submit_urb(serial_priv->read_urb, GFP_KERNEL);
+	if (status != 0) {
+		dev_err(&serial->dev->dev,
+			"%s - submit read urb failed %i\n", __func__, status);
+		usb_free_urb(serial_priv->read_urb);
+		return status;
+	}
+
+	return 0;
+}
+
+static int qt2_attach(struct usb_serial *serial)
+{
+	struct qt2_serial_private *serial_priv;
+	int status;
+
+	/* power on unit */
+	status = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				 0xc2, 0x40, 0x8000, 0, NULL, 0,
+				 QT2_USB_TIMEOUT);
+	if (status < 0) {
+		dev_err(&serial->dev->dev,
+			"%s - failed to power on unit: %i\n", __func__, status);
+		return status;
+	}
+
+	serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
+	if (!serial_priv)
+		return -ENOMEM;
+
+	serial_priv->read_buffer = kmalloc(QT2_READ_BUFFER_SIZE, GFP_KERNEL);
+	if (!serial_priv->read_buffer) {
+		status = -ENOMEM;
+		goto err_buf;
+	}
+
+	usb_set_serial_data(serial, serial_priv);
+
+	status = qt2_setup_urbs(serial);
+	if (status != 0)
+		goto attach_failed;
+
+	return 0;
+
+attach_failed:
+	kfree(serial_priv->read_buffer);
+err_buf:
+	kfree(serial_priv);
+	return status;
+}
+
+static int qt2_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct qt2_port_private *port_priv;
+	u8 bEndpointAddress;
+
+	port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+	if (!port_priv)
+		return -ENOMEM;
+
+	spin_lock_init(&port_priv->lock);
+	spin_lock_init(&port_priv->urb_lock);
+	port_priv->port = port;
+
+	port_priv->write_buffer = kmalloc(QT2_WRITE_BUFFER_SIZE, GFP_KERNEL);
+	if (!port_priv->write_buffer)
+		goto err_buf;
+
+	port_priv->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!port_priv->write_urb)
+		goto err_urb;
+
+	bEndpointAddress = serial->port[0]->bulk_out_endpointAddress;
+	usb_fill_bulk_urb(port_priv->write_urb, serial->dev,
+				usb_sndbulkpipe(serial->dev, bEndpointAddress),
+				port_priv->write_buffer,
+				QT2_WRITE_BUFFER_SIZE,
+				qt2_write_bulk_callback, port);
+
+	usb_set_serial_port_data(port, port_priv);
+
+	return 0;
+err_urb:
+	kfree(port_priv->write_buffer);
+err_buf:
+	kfree(port_priv);
+	return -ENOMEM;
+}
+
+static int qt2_port_remove(struct usb_serial_port *port)
+{
+	struct qt2_port_private *port_priv;
+
+	port_priv = usb_get_serial_port_data(port);
+	usb_free_urb(port_priv->write_urb);
+	kfree(port_priv->write_buffer);
+	kfree(port_priv);
+
+	return 0;
+}
+
+static int qt2_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_device *dev = port->serial->dev;
+	struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
+	u8 *d;
+	int r;
+
+	d = kzalloc(2, GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	r = qt2_getregister(dev, port_priv->device_port, UART_MCR, d);
+	if (r < 0)
+		goto mget_out;
+
+	r = qt2_getregister(dev, port_priv->device_port, UART_MSR, d + 1);
+	if (r < 0)
+		goto mget_out;
+
+	r = (d[0] & UART_MCR_DTR ? TIOCM_DTR : 0) |
+	    (d[0] & UART_MCR_RTS ? TIOCM_RTS : 0) |
+	    (d[1] & UART_MSR_CTS ? TIOCM_CTS : 0) |
+	    (d[1] & UART_MSR_DCD ? TIOCM_CAR : 0) |
+	    (d[1] & UART_MSR_RI ? TIOCM_RI : 0) |
+	    (d[1] & UART_MSR_DSR ? TIOCM_DSR : 0);
+
+mget_out:
+	kfree(d);
+	return r;
+}
+
+static int qt2_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct qt2_port_private *port_priv;
+
+	port_priv = usb_get_serial_port_data(tty->driver_data);
+	return update_mctrl(port_priv, set, clear);
+}
+
+static void qt2_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct qt2_port_private *port_priv;
+	int status;
+	u16 val;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	val = (break_state == -1) ? 1 : 0;
+
+	status = qt2_control_msg(port->serial->dev, QT2_BREAK_CONTROL,
+				 val, port_priv->device_port);
+	if (status < 0)
+		dev_warn(&port->dev,
+			 "%s - failed to send control message: %i\n", __func__,
+			 status);
+}
+
+
+
+static void qt2_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
+
+	/* Disable flow control */
+	if (!on) {
+		if (qt2_setregister(dev, port_priv->device_port,
+					   UART_MCR, 0) < 0)
+			dev_warn(&port->dev, "error from flowcontrol urb\n");
+	}
+	/* drop RTS and DTR */
+	if (on)
+		update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
+	else
+		update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)
+{
+	struct qt2_port_private *port_priv;
+	u8 newMSR = (u8) *ch;
+	unsigned long flags;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&port_priv->lock, flags);
+	port_priv->shadowMSR = newMSR;
+	spin_unlock_irqrestore(&port_priv->lock, flags);
+
+	if (newMSR & UART_MSR_ANY_DELTA) {
+		/* update input line counters */
+		if (newMSR & UART_MSR_DCTS)
+			port->icount.cts++;
+		if (newMSR & UART_MSR_DDSR)
+			port->icount.dsr++;
+		if (newMSR & UART_MSR_DDCD)
+			port->icount.dcd++;
+		if (newMSR & UART_MSR_TERI)
+			port->icount.rng++;
+
+		wake_up_interruptible(&port->port.delta_msr_wait);
+	}
+}
+
+static void qt2_update_lsr(struct usb_serial_port *port, unsigned char *ch)
+{
+	struct qt2_port_private *port_priv;
+	struct async_icount *icount;
+	unsigned long flags;
+	u8 newLSR = (u8) *ch;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	if (newLSR & UART_LSR_BI)
+		newLSR &= (u8) (UART_LSR_OE | UART_LSR_BI);
+
+	spin_lock_irqsave(&port_priv->lock, flags);
+	port_priv->shadowLSR = newLSR;
+	spin_unlock_irqrestore(&port_priv->lock, flags);
+
+	icount = &port->icount;
+
+	if (newLSR & UART_LSR_BRK_ERROR_BITS) {
+
+		if (newLSR & UART_LSR_BI)
+			icount->brk++;
+
+		if (newLSR & UART_LSR_OE)
+			icount->overrun++;
+
+		if (newLSR & UART_LSR_PE)
+			icount->parity++;
+
+		if (newLSR & UART_LSR_FE)
+			icount->frame++;
+	}
+
+}
+
+static int qt2_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct qt2_port_private *port_priv;
+	unsigned long flags = 0;
+	int r;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	spin_lock_irqsave(&port_priv->urb_lock, flags);
+
+	if (port_priv->urb_in_use)
+		r = 0;
+	else
+		r = QT2_WRITE_BUFFER_SIZE - QT2_WRITE_CONTROL_SIZE;
+
+	spin_unlock_irqrestore(&port_priv->urb_lock, flags);
+
+	return r;
+}
+
+static int qt2_write(struct tty_struct *tty,
+		     struct usb_serial_port *port,
+		     const unsigned char *buf, int count)
+{
+	struct qt2_port_private *port_priv;
+	struct urb *write_urb;
+	unsigned char *data;
+	unsigned long flags;
+	int status;
+	int bytes_out = 0;
+
+	port_priv = usb_get_serial_port_data(port);
+
+	if (port_priv->write_urb == NULL) {
+		dev_err(&port->dev, "%s - no output urb\n", __func__);
+		return 0;
+	}
+	write_urb = port_priv->write_urb;
+
+	count = min(count, QT2_WRITE_BUFFER_SIZE - QT2_WRITE_CONTROL_SIZE);
+
+	data = write_urb->transfer_buffer;
+	spin_lock_irqsave(&port_priv->urb_lock, flags);
+	if (port_priv->urb_in_use) {
+		dev_err(&port->dev, "qt2_write - urb is in use\n");
+		goto write_out;
+	}
+
+	*data++ = QT2_CONTROL_BYTE;
+	*data++ = QT2_CONTROL_BYTE;
+	*data++ = port_priv->device_port;
+	put_unaligned_le16(count, data);
+	data += 2;
+	memcpy(data, buf, count);
+
+	write_urb->transfer_buffer_length = count + QT2_WRITE_CONTROL_SIZE;
+
+	status = usb_submit_urb(write_urb, GFP_ATOMIC);
+	if (status == 0) {
+		port_priv->urb_in_use = true;
+		bytes_out += count;
+	}
+
+write_out:
+	spin_unlock_irqrestore(&port_priv->urb_lock, flags);
+	return bytes_out;
+}
+
+
+static struct usb_serial_driver qt2_device = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "quatech-serial",
+	},
+	.description	     = DRIVER_DESC,
+	.id_table	     = id_table,
+	.open		     = qt2_open,
+	.close		     = qt2_close,
+	.write               = qt2_write,
+	.write_room          = qt2_write_room,
+	.calc_num_ports      = qt2_calc_num_ports,
+	.attach              = qt2_attach,
+	.release             = qt2_release,
+	.disconnect          = qt2_disconnect,
+	.port_probe          = qt2_port_probe,
+	.port_remove         = qt2_port_remove,
+	.dtr_rts             = qt2_dtr_rts,
+	.break_ctl           = qt2_break_ctl,
+	.tiocmget            = qt2_tiocmget,
+	.tiocmset            = qt2_tiocmset,
+	.tiocmiwait          = usb_serial_generic_tiocmiwait,
+	.get_icount	     = usb_serial_generic_get_icount,
+	.ioctl               = qt2_ioctl,
+	.set_termios         = qt2_set_termios,
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+	&qt2_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
new file mode 100644
index 0000000..6accbec
--- /dev/null
+++ b/drivers/usb/serial/safe_serial.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Safe Encapsulated USB Serial Driver
+ *
+ *      Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ *      Copyright (C) 2001 Lineo
+ *      Copyright (C) 2001 Hewlett-Packard
+ *
+ * By:
+ *      Stuart Lynne <sl@lineo.com>, Tom Rushworth <tbr@lineo.com>
+ */
+
+/*
+ * The encapsultaion is designed to overcome difficulties with some USB
+ * hardware.
+ *
+ * While the USB protocol has a CRC over the data while in transit, i.e. while
+ * being carried over the bus, there is no end to end protection. If the
+ * hardware has any problems getting the data into or out of the USB transmit
+ * and receive FIFO's then data can be lost.
+ *
+ * This protocol adds a two byte trailer to each USB packet to specify the
+ * number of bytes of valid data and a 10 bit CRC that will allow the receiver
+ * to verify that the entire USB packet was received without error.
+ *
+ * Because in this case the sender and receiver are the class and function
+ * drivers there is now end to end protection.
+ *
+ * There is an additional option that can be used to force all transmitted
+ * packets to be padded to the maximum packet size. This provides a work
+ * around for some devices which have problems with small USB packets.
+ *
+ * Assuming a packetsize of N:
+ *
+ *      0..N-2  data and optional padding
+ *
+ *      N-2     bits 7-2 - number of bytes of valid data
+ *              bits 1-0 top two bits of 10 bit CRC
+ *      N-1     bottom 8 bits of 10 bit CRC
+ *
+ *
+ *      | Data Length       | 10 bit CRC                                |
+ *      + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 +
+ *
+ * The 10 bit CRC is computed across the sent data, followed by the trailer
+ * with the length set and the CRC set to zero. The CRC is then OR'd into
+ * the trailer.
+ *
+ * When received a 10 bit CRC is computed over the entire frame including
+ * the trailer and should be equal to zero.
+ *
+ * Two module parameters are used to control the encapsulation, if both are
+ * turned of the module works as a simple serial device with NO
+ * encapsulation.
+ *
+ * See linux/drivers/usbd/serial_fd for a device function driver
+ * implementation of this.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static bool safe = true;
+static bool padded = IS_ENABLED(CONFIG_USB_SERIAL_SAFE_PADDED);
+
+#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "USB Safe Encapsulated Serial"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(safe, bool, 0);
+MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off");
+
+module_param(padded, bool, 0);
+MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off");
+
+#define CDC_DEVICE_CLASS                        0x02
+
+#define CDC_INTERFACE_CLASS                     0x02
+#define CDC_INTERFACE_SUBCLASS                  0x06
+
+#define LINEO_INTERFACE_CLASS                   0xff
+
+#define LINEO_INTERFACE_SUBCLASS_SAFENET        0x01
+#define LINEO_SAFENET_CRC                       0x01
+#define LINEO_SAFENET_CRC_PADDED                0x02
+
+#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL     0x02
+#define LINEO_SAFESERIAL_CRC                    0x01
+#define LINEO_SAFESERIAL_CRC_PADDED             0x02
+
+
+#define MY_USB_DEVICE(vend, prod, dc, ic, isc) \
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+		       USB_DEVICE_ID_MATCH_DEV_CLASS | \
+		       USB_DEVICE_ID_MATCH_INT_CLASS | \
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS, \
+	.idVendor = (vend), \
+	.idProduct = (prod),\
+	.bDeviceClass = (dc),\
+	.bInterfaceClass = (ic), \
+	.bInterfaceSubClass = (isc),
+
+static const struct usb_device_id id_table[] = {
+	{MY_USB_DEVICE(0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Itsy */
+	{MY_USB_DEVICE(0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Calypso */
+	{MY_USB_DEVICE(0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Iris */
+	{MY_USB_DEVICE(0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Collie */
+	{MY_USB_DEVICE(0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Collie */
+	{MY_USB_DEVICE(0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Collie */
+	{MY_USB_DEVICE(0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	/* Sharp tmp */
+	{}			/* terminating entry  */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static const __u16 crc10_table[256] = {
+	0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff,
+	0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe,
+	0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce,
+	0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf,
+	0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d,
+	0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c,
+	0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac,
+	0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad,
+	0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b,
+	0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a,
+	0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a,
+	0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b,
+	0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259,
+	0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158,
+	0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268,
+	0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169,
+	0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377,
+	0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076,
+	0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346,
+	0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047,
+	0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315,
+	0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014,
+	0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324,
+	0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025,
+	0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3,
+	0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2,
+	0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382,
+	0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083,
+	0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1,
+	0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0,
+	0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0,
+	0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1,
+};
+
+#define CRC10_INITFCS     0x000	/* Initial FCS value */
+#define CRC10_GOODFCS     0x000	/* Good final FCS value */
+#define CRC10_FCS(fcs, c) ((((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c))
+
+/**
+ * fcs_compute10 - memcpy and calculate 10 bit CRC across buffer
+ * @sp: pointer to buffer
+ * @len: number of bytes
+ * @fcs: starting FCS
+ *
+ * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return
+ * new 10 bit FCS.
+ */
+static inline __u16 fcs_compute10(unsigned char *sp, int len, __u16 fcs)
+{
+	for (; len-- > 0; fcs = CRC10_FCS(fcs, *sp++));
+	return fcs;
+}
+
+static void safe_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned char length = urb->actual_length;
+	int actual_length;
+	__u16 fcs;
+
+	if (!length)
+		return;
+
+	if (!safe)
+		goto out;
+
+	if (length < 2) {
+		dev_err(&port->dev, "malformed packet\n");
+		return;
+	}
+
+	fcs = fcs_compute10(data, length, CRC10_INITFCS);
+	if (fcs) {
+		dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
+		return;
+	}
+
+	actual_length = data[length - 2] >> 2;
+	if (actual_length > (length - 2)) {
+		dev_err(&port->dev, "%s - inconsistent lengths %d:%d\n",
+				__func__, actual_length, length);
+		return;
+	}
+	dev_info(&urb->dev->dev, "%s - actual: %d\n", __func__, actual_length);
+	length = actual_length;
+out:
+	tty_insert_flip_string(&port->port, data, length);
+	tty_flip_buffer_push(&port->port);
+}
+
+static int safe_prepare_write_buffer(struct usb_serial_port *port,
+						void *dest, size_t size)
+{
+	unsigned char *buf = dest;
+	int count;
+	int trailer_len;
+	int pkt_len;
+	__u16 fcs;
+
+	trailer_len = safe ? 2 : 0;
+
+	count = kfifo_out_locked(&port->write_fifo, buf, size - trailer_len,
+								&port->lock);
+	if (!safe)
+		return count;
+
+	/* pad if necessary */
+	if (padded) {
+		pkt_len = size;
+		memset(buf + count, '0', pkt_len - count - trailer_len);
+	} else {
+		pkt_len = count + trailer_len;
+	}
+
+	/* set count */
+	buf[pkt_len - 2] = count << 2;
+	buf[pkt_len - 1] = 0;
+
+	/* compute fcs and insert into trailer */
+	fcs = fcs_compute10(buf, pkt_len, CRC10_INITFCS);
+	buf[pkt_len - 2] |= fcs >> 8;
+	buf[pkt_len - 1] |= fcs & 0xff;
+
+	return pkt_len;
+}
+
+static int safe_startup(struct usb_serial *serial)
+{
+	struct usb_interface_descriptor	*desc;
+
+	if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS)
+		return -ENODEV;
+
+	desc = &serial->interface->cur_altsetting->desc;
+
+	if (desc->bInterfaceClass != LINEO_INTERFACE_CLASS)
+		return -ENODEV;
+	if (desc->bInterfaceSubClass != LINEO_INTERFACE_SUBCLASS_SAFESERIAL)
+		return -ENODEV;
+
+	switch (desc->bInterfaceProtocol) {
+	case LINEO_SAFESERIAL_CRC:
+		break;
+	case LINEO_SAFESERIAL_CRC_PADDED:
+		padded = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct usb_serial_driver safe_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"safe_serial",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.process_read_urb =	safe_process_read_urb,
+	.prepare_write_buffer =	safe_prepare_write_buffer,
+	.attach =		safe_startup,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&safe_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
new file mode 100644
index 0000000..a43263a
--- /dev/null
+++ b/drivers/usb/serial/sierra.c
@@ -0,0 +1,1082 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+  USB Driver for Sierra Wireless
+
+  Copyright (C) 2006, 2007, 2008  Kevin Lloyd <klloyd@sierrawireless.com>,
+
+  Copyright (C) 2008, 2009  Elina Pasheva, Matthew Safar, Rory Filer
+			<linux@sierrawireless.com>
+
+  IMPORTANT DISCLAIMER: This driver is not commercially supported by
+  Sierra Wireless. Use at your own risk.
+
+  Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
+  Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
+*/
+/* Uncomment to log function calls */
+/* #define DEBUG */
+
+#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
+#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define SWIMS_USB_REQUEST_SetPower	0x00
+#define SWIMS_USB_REQUEST_SetNmea	0x07
+
+#define N_IN_URB_HM	8
+#define N_OUT_URB_HM	64
+#define N_IN_URB	4
+#define N_OUT_URB	4
+#define IN_BUFLEN	4096
+
+#define MAX_TRANSFER		(PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+   allocations > PAGE_SIZE and the number of packets in a page
+   is an integer 512 is the largest possible packet on EHCI */
+
+static bool nmea;
+
+/* Used in interface blacklisting */
+struct sierra_iface_info {
+	const u32 infolen;	/* number of interface numbers on blacklist */
+	const u8  *ifaceinfo;	/* pointer to the array holding the numbers */
+};
+
+struct sierra_intf_private {
+	spinlock_t susp_lock;
+	unsigned int suspended:1;
+	int in_flight;
+	unsigned int open_ports;
+};
+
+static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
+{
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			SWIMS_USB_REQUEST_SetPower,	/* __u8 request      */
+			USB_TYPE_VENDOR,		/* __u8 request type */
+			swiState,			/* __u16 value       */
+			0,				/* __u16 index       */
+			NULL,				/* void *data        */
+			0,				/* __u16 size 	     */
+			USB_CTRL_SET_TIMEOUT);		/* int timeout 	     */
+}
+
+static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
+{
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			SWIMS_USB_REQUEST_SetNmea,	/* __u8 request      */
+			USB_TYPE_VENDOR,		/* __u8 request type */
+			enable,				/* __u16 value       */
+			0x0000,				/* __u16 index       */
+			NULL,				/* void *data        */
+			0,				/* __u16 size 	     */
+			USB_CTRL_SET_TIMEOUT);		/* int timeout       */
+}
+
+static int sierra_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	int num_ports = 0;
+	u8 ifnum, numendpoints;
+
+	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+	numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints;
+
+	/* Dummy interface present on some SKUs should be ignored */
+	if (ifnum == 0x99)
+		num_ports = 0;
+	else if (numendpoints <= 3)
+		num_ports = 1;
+	else
+		num_ports = (numendpoints-1)/2;
+	return num_ports;
+}
+
+static int is_blacklisted(const u8 ifnum,
+				const struct sierra_iface_info *blacklist)
+{
+	const u8  *info;
+	int i;
+
+	if (blacklist) {
+		info = blacklist->ifaceinfo;
+
+		for (i = 0; i < blacklist->infolen; i++) {
+			if (info[i] == ifnum)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static int is_himemory(const u8 ifnum,
+				const struct sierra_iface_info *himemorylist)
+{
+	const u8  *info;
+	int i;
+
+	if (himemorylist) {
+		info = himemorylist->ifaceinfo;
+
+		for (i=0; i < himemorylist->infolen; i++) {
+			if (info[i] == ifnum)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static u8 sierra_interface_num(struct usb_serial *serial)
+{
+	return serial->interface->cur_altsetting->desc.bInterfaceNumber;
+}
+
+static int sierra_probe(struct usb_serial *serial,
+			const struct usb_device_id *id)
+{
+	int result = 0;
+	struct usb_device *udev;
+	u8 ifnum;
+
+	udev = serial->dev;
+	ifnum = sierra_interface_num(serial);
+
+	/*
+	 * If this interface supports more than 1 alternate
+	 * select the 2nd one
+	 */
+	if (serial->interface->num_altsetting == 2) {
+		dev_dbg(&udev->dev, "Selecting alt setting for interface %d\n",
+			ifnum);
+		/* We know the alternate setting is 1 for the MC8785 */
+		usb_set_interface(udev, ifnum, 1);
+	}
+
+	if (is_blacklisted(ifnum,
+				(struct sierra_iface_info *)id->driver_info)) {
+		dev_dbg(&serial->dev->dev,
+			"Ignoring blacklisted interface #%d\n", ifnum);
+		return -ENODEV;
+	}
+
+	return result;
+}
+
+/* interfaces with higher memory requirements */
+static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
+static const struct sierra_iface_info typeA_interface_list = {
+	.infolen = ARRAY_SIZE(hi_memory_typeA_ifaces),
+	.ifaceinfo = hi_memory_typeA_ifaces,
+};
+
+static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
+static const struct sierra_iface_info typeB_interface_list = {
+	.infolen = ARRAY_SIZE(hi_memory_typeB_ifaces),
+	.ifaceinfo = hi_memory_typeB_ifaces,
+};
+
+/* 'blacklist' of interfaces not served by this driver */
+static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11, 19, 20 };
+static const struct sierra_iface_info direct_ip_interface_blacklist = {
+	.infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
+	.ifaceinfo = direct_ip_non_serial_ifaces,
+};
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
+	{ USB_DEVICE(0x03F0, 0x1B1D) },	/* HP ev2200 a.k.a MC5720 */
+	{ USB_DEVICE(0x03F0, 0x211D) }, /* HP ev2210 a.k.a MC5725 */
+	{ USB_DEVICE(0x03F0, 0x1E1D) },	/* HP hs2300 a.k.a MC8775 */
+
+	{ USB_DEVICE(0x1199, 0x0017) },	/* Sierra Wireless EM5625 */
+	{ USB_DEVICE(0x1199, 0x0018) },	/* Sierra Wireless MC5720 */
+	{ USB_DEVICE(0x1199, 0x0218) },	/* Sierra Wireless MC5720 */
+	{ USB_DEVICE(0x1199, 0x0020) },	/* Sierra Wireless MC5725 */
+	{ USB_DEVICE(0x1199, 0x0220) },	/* Sierra Wireless MC5725 */
+	{ USB_DEVICE(0x1199, 0x0022) },	/* Sierra Wireless EM5725 */
+	{ USB_DEVICE(0x1199, 0x0024) },	/* Sierra Wireless MC5727 */
+	{ USB_DEVICE(0x1199, 0x0224) },	/* Sierra Wireless MC5727 */
+	{ USB_DEVICE(0x1199, 0x0019) },	/* Sierra Wireless AirCard 595 */
+	{ USB_DEVICE(0x1199, 0x0021) },	/* Sierra Wireless AirCard 597E */
+	{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
+	{ USB_DEVICE(0x1199, 0x0120) },	/* Sierra Wireless USB Dongle 595U */
+	{ USB_DEVICE(0x1199, 0x0301) },	/* Sierra Wireless USB Dongle 250U */
+	/* Sierra Wireless C597 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) },
+	/* Sierra Wireless T598 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) },
+	{ USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless T11 */
+	{ USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless AC402 */
+	{ USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless MC5728 */
+	{ USB_DEVICE(0x1199, 0x0029) }, /* Sierra Wireless Device */
+
+	{ USB_DEVICE(0x1199, 0x6802) },	/* Sierra Wireless MC8755 */
+	{ USB_DEVICE(0x1199, 0x6803) },	/* Sierra Wireless MC8765 */
+	{ USB_DEVICE(0x1199, 0x6804) },	/* Sierra Wireless MC8755 */
+	{ USB_DEVICE(0x1199, 0x6805) },	/* Sierra Wireless MC8765 */
+	{ USB_DEVICE(0x1199, 0x6808) },	/* Sierra Wireless MC8755 */
+	{ USB_DEVICE(0x1199, 0x6809) },	/* Sierra Wireless MC8765 */
+	{ USB_DEVICE(0x1199, 0x6812) },	/* Sierra Wireless MC8775 & AC 875U */
+	{ USB_DEVICE(0x1199, 0x6813) },	/* Sierra Wireless MC8775 */
+	{ USB_DEVICE(0x1199, 0x6815) },	/* Sierra Wireless MC8775 */
+	{ USB_DEVICE(0x1199, 0x6816) },	/* Sierra Wireless MC8775 */
+	{ USB_DEVICE(0x1199, 0x6820) },	/* Sierra Wireless AirCard 875 */
+	{ USB_DEVICE(0x1199, 0x6821) },	/* Sierra Wireless AirCard 875U */
+	{ USB_DEVICE(0x1199, 0x6822) },	/* Sierra Wireless AirCard 875E */
+	{ USB_DEVICE(0x1199, 0x6832) },	/* Sierra Wireless MC8780 */
+	{ USB_DEVICE(0x1199, 0x6833) },	/* Sierra Wireless MC8781 */
+	{ USB_DEVICE(0x1199, 0x6834) },	/* Sierra Wireless MC8780 */
+	{ USB_DEVICE(0x1199, 0x6835) },	/* Sierra Wireless MC8781 */
+	{ USB_DEVICE(0x1199, 0x6838) },	/* Sierra Wireless MC8780 */
+	{ USB_DEVICE(0x1199, 0x6839) },	/* Sierra Wireless MC8781 */
+	{ USB_DEVICE(0x1199, 0x683A) },	/* Sierra Wireless MC8785 */
+	{ USB_DEVICE(0x1199, 0x683B) },	/* Sierra Wireless MC8785 Composite */
+	/* Sierra Wireless MC8790, MC8791, MC8792 Composite */
+	{ USB_DEVICE(0x1199, 0x683C) },
+	{ USB_DEVICE(0x1199, 0x683D) },	/* Sierra Wireless MC8791 Composite */
+	/* Sierra Wireless MC8790, MC8791, MC8792 */
+	{ USB_DEVICE(0x1199, 0x683E) },
+	{ USB_DEVICE(0x1199, 0x6850) },	/* Sierra Wireless AirCard 880 */
+	{ USB_DEVICE(0x1199, 0x6851) },	/* Sierra Wireless AirCard 881 */
+	{ USB_DEVICE(0x1199, 0x6852) },	/* Sierra Wireless AirCard 880 E */
+	{ USB_DEVICE(0x1199, 0x6853) },	/* Sierra Wireless AirCard 881 E */
+	{ USB_DEVICE(0x1199, 0x6855) },	/* Sierra Wireless AirCard 880 U */
+	{ USB_DEVICE(0x1199, 0x6856) },	/* Sierra Wireless AirCard 881 U */
+	{ USB_DEVICE(0x1199, 0x6859) },	/* Sierra Wireless AirCard 885 E */
+	{ USB_DEVICE(0x1199, 0x685A) },	/* Sierra Wireless AirCard 885 E */
+	/* Sierra Wireless C885 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)},
+	/* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)},
+	/* Sierra Wireless C22/C33 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6891, 0xFF, 0xFF, 0xFF)},
+	/* Sierra Wireless HSPA Non-Composite Device */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
+	{ USB_DEVICE(0x1199, 0x6893) },	/* Sierra Wireless Device */
+	/* Sierra Wireless Direct IP modems */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68A3, 0xFF, 0xFF, 0xFF),
+	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+	},
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF),
+	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+	},
+	{ USB_DEVICE(0x1199, 0x68AB) }, /* Sierra Wireless AR8550 */
+	/* AT&T Direct IP LTE modems */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
+	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+	},
+	/* Airprime/Sierra Wireless Direct IP modems */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68A3, 0xFF, 0xFF, 0xFF),
+	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+	},
+
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+struct sierra_port_private {
+	spinlock_t lock;	/* lock the structure */
+	int outstanding_urbs;	/* number of out urbs in flight */
+	struct usb_anchor active;
+	struct usb_anchor delayed;
+
+	int num_out_urbs;
+	int num_in_urbs;
+	/* Input endpoints and buffers for this port */
+	struct urb *in_urbs[N_IN_URB_HM];
+
+	/* Settings for the port */
+	int rts_state;	/* Handshaking pins (outputs) */
+	int dtr_state;
+	int cts_state;	/* Handshaking pins (inputs) */
+	int dsr_state;
+	int dcd_state;
+	int ri_state;
+};
+
+static int sierra_send_setup(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct sierra_port_private *portdata;
+	__u16 interface = 0;
+	int val = 0;
+	int do_send = 0;
+	int retval;
+
+	portdata = usb_get_serial_port_data(port);
+
+	if (portdata->dtr_state)
+		val |= 0x01;
+	if (portdata->rts_state)
+		val |= 0x02;
+
+	/* If composite device then properly report interface */
+	if (serial->num_ports == 1) {
+		interface = sierra_interface_num(serial);
+		/* Control message is sent only to interfaces with
+		 * interrupt_in endpoints
+		 */
+		if (port->interrupt_in_urb) {
+			/* send control message */
+			do_send = 1;
+		}
+	}
+
+	/* Otherwise the need to do non-composite mapping */
+	else {
+		if (port->bulk_out_endpointAddress == 2)
+			interface = 0;
+		else if (port->bulk_out_endpointAddress == 4)
+			interface = 1;
+		else if (port->bulk_out_endpointAddress == 5)
+			interface = 2;
+
+		do_send = 1;
+	}
+	if (!do_send)
+		return 0;
+
+	retval = usb_autopm_get_interface(serial->interface);
+	if (retval < 0)
+		return retval;
+
+	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+		0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	usb_autopm_put_interface(serial->interface);
+
+	return retval;
+}
+
+static int sierra_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int value;
+	struct sierra_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+
+	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+		((portdata->dtr_state) ? TIOCM_DTR : 0) |
+		((portdata->cts_state) ? TIOCM_CTS : 0) |
+		((portdata->dsr_state) ? TIOCM_DSR : 0) |
+		((portdata->dcd_state) ? TIOCM_CAR : 0) |
+		((portdata->ri_state) ? TIOCM_RNG : 0);
+
+	return value;
+}
+
+static int sierra_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct sierra_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+
+	if (set & TIOCM_RTS)
+		portdata->rts_state = 1;
+	if (set & TIOCM_DTR)
+		portdata->dtr_state = 1;
+
+	if (clear & TIOCM_RTS)
+		portdata->rts_state = 0;
+	if (clear & TIOCM_DTR)
+		portdata->dtr_state = 0;
+	return sierra_send_setup(port);
+}
+
+static void sierra_release_urb(struct urb *urb)
+{
+	if (urb) {
+		kfree(urb->transfer_buffer);
+		usb_free_urb(urb);
+	}
+}
+
+static void sierra_outdat_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	struct sierra_intf_private *intfdata;
+	int status = urb->status;
+	unsigned long flags;
+
+	intfdata = usb_get_serial_data(port->serial);
+
+	/* free up the transfer buffer, as usb_free_urb() does not do this */
+	kfree(urb->transfer_buffer);
+	usb_autopm_put_interface_async(port->serial->interface);
+	if (status)
+		dev_dbg(&port->dev, "%s - nonzero write bulk status "
+		    "received: %d\n", __func__, status);
+
+	spin_lock_irqsave(&portdata->lock, flags);
+	--portdata->outstanding_urbs;
+	spin_unlock_irqrestore(&portdata->lock, flags);
+	spin_lock_irqsave(&intfdata->susp_lock, flags);
+	--intfdata->in_flight;
+	spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+
+	usb_serial_port_softint(port);
+}
+
+/* Write */
+static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *buf, int count)
+{
+	struct sierra_port_private *portdata;
+	struct sierra_intf_private *intfdata;
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+	unsigned char *buffer;
+	struct urb *urb;
+	size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER);
+	int retval = 0;
+
+	/* verify that we actually have some data to write */
+	if (count == 0)
+		return 0;
+
+	portdata = usb_get_serial_port_data(port);
+	intfdata = usb_get_serial_data(serial);
+
+	dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize);
+	spin_lock_irqsave(&portdata->lock, flags);
+	dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
+		portdata->outstanding_urbs);
+	if (portdata->outstanding_urbs > portdata->num_out_urbs) {
+		spin_unlock_irqrestore(&portdata->lock, flags);
+		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
+		return 0;
+	}
+	portdata->outstanding_urbs++;
+	dev_dbg(&port->dev, "%s - 1, outstanding_urbs: %d\n", __func__,
+		portdata->outstanding_urbs);
+	spin_unlock_irqrestore(&portdata->lock, flags);
+
+	retval = usb_autopm_get_interface_async(serial->interface);
+	if (retval < 0) {
+		spin_lock_irqsave(&portdata->lock, flags);
+		portdata->outstanding_urbs--;
+		spin_unlock_irqrestore(&portdata->lock, flags);
+		goto error_simple;
+	}
+
+	buffer = kmalloc(writesize, GFP_ATOMIC);
+	if (!buffer) {
+		retval = -ENOMEM;
+		goto error_no_buffer;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		retval = -ENOMEM;
+		goto error_no_urb;
+	}
+
+	memcpy(buffer, buf, writesize);
+
+	usb_serial_debug_data(&port->dev, __func__, writesize, buffer);
+
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev,
+					  port->bulk_out_endpointAddress),
+			  buffer, writesize, sierra_outdat_callback, port);
+
+	/* Handle the need to send a zero length packet */
+	urb->transfer_flags |= URB_ZERO_PACKET;
+
+	spin_lock_irqsave(&intfdata->susp_lock, flags);
+
+	if (intfdata->suspended) {
+		usb_anchor_urb(urb, &portdata->delayed);
+		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+		goto skip_power;
+	} else {
+		usb_anchor_urb(urb, &portdata->active);
+	}
+	/* send it down the pipe */
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval) {
+		usb_unanchor_urb(urb);
+		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
+			"with status = %d\n", __func__, retval);
+		goto error;
+	} else {
+		intfdata->in_flight++;
+		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+	}
+
+skip_power:
+	/* we are done with this urb, so let the host driver
+	 * really free it when it is finished with it */
+	usb_free_urb(urb);
+
+	return writesize;
+error:
+	usb_free_urb(urb);
+error_no_urb:
+	kfree(buffer);
+error_no_buffer:
+	spin_lock_irqsave(&portdata->lock, flags);
+	--portdata->outstanding_urbs;
+	dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__,
+		portdata->outstanding_urbs);
+	spin_unlock_irqrestore(&portdata->lock, flags);
+	usb_autopm_put_interface_async(serial->interface);
+error_simple:
+	return retval;
+}
+
+static void sierra_indat_callback(struct urb *urb)
+{
+	int err;
+	int endpoint;
+	struct usb_serial_port *port;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+	port = urb->context;
+
+	if (status) {
+		dev_dbg(&port->dev, "%s: nonzero status: %d on"
+			" endpoint %02x\n", __func__, status, endpoint);
+	} else {
+		if (urb->actual_length) {
+			tty_insert_flip_string(&port->port, data,
+				urb->actual_length);
+			tty_flip_buffer_push(&port->port);
+
+			usb_serial_debug_data(&port->dev, __func__,
+					      urb->actual_length, data);
+		} else {
+			dev_dbg(&port->dev, "%s: empty read urb"
+				" received\n", __func__);
+		}
+	}
+
+	/* Resubmit urb so we continue receiving */
+	if (status != -ESHUTDOWN && status != -EPERM) {
+		usb_mark_last_busy(port->serial->dev);
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err && err != -EPERM)
+			dev_err(&port->dev, "resubmit read urb failed."
+				"(%d)\n", err);
+	}
+}
+
+static void sierra_instat_callback(struct urb *urb)
+{
+	int err;
+	int status = urb->status;
+	struct usb_serial_port *port =  urb->context;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+
+	dev_dbg(&port->dev, "%s: urb %p port %p has data %p\n", __func__,
+		urb, port, portdata);
+
+	if (status == 0) {
+		struct usb_ctrlrequest *req_pkt =
+				(struct usb_ctrlrequest *)urb->transfer_buffer;
+
+		if (!req_pkt) {
+			dev_dbg(&port->dev, "%s: NULL req_pkt\n",
+				__func__);
+			return;
+		}
+		if ((req_pkt->bRequestType == 0xA1) &&
+				(req_pkt->bRequest == 0x20)) {
+			int old_dcd_state;
+			unsigned char signals = *((unsigned char *)
+					urb->transfer_buffer +
+					sizeof(struct usb_ctrlrequest));
+
+			dev_dbg(&port->dev, "%s: signal x%x\n", __func__,
+				signals);
+
+			old_dcd_state = portdata->dcd_state;
+			portdata->cts_state = 1;
+			portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
+			portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
+			portdata->ri_state = ((signals & 0x08) ? 1 : 0);
+
+			if (old_dcd_state && !portdata->dcd_state)
+				tty_port_tty_hangup(&port->port, true);
+		} else {
+			dev_dbg(&port->dev, "%s: type %x req %x\n",
+				__func__, req_pkt->bRequestType,
+				req_pkt->bRequest);
+		}
+	} else
+		dev_dbg(&port->dev, "%s: error %d\n", __func__, status);
+
+	/* Resubmit urb so we continue receiving IRQ data */
+	if (status != -ESHUTDOWN && status != -ENOENT) {
+		usb_mark_last_busy(serial->dev);
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err && err != -EPERM)
+			dev_err(&port->dev, "%s: resubmit intr urb "
+				"failed. (%d)\n", __func__, err);
+	}
+}
+
+static int sierra_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	/* try to give a good number back based on if we have any free urbs at
+	 * this point in time */
+	spin_lock_irqsave(&portdata->lock, flags);
+	if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) {
+		spin_unlock_irqrestore(&portdata->lock, flags);
+		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
+		return 0;
+	}
+	spin_unlock_irqrestore(&portdata->lock, flags);
+
+	return 2048;
+}
+
+static int sierra_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int chars;
+
+	/* NOTE: This overcounts somewhat. */
+	spin_lock_irqsave(&portdata->lock, flags);
+	chars = portdata->outstanding_urbs * MAX_TRANSFER;
+	spin_unlock_irqrestore(&portdata->lock, flags);
+
+	dev_dbg(&port->dev, "%s - %d\n", __func__, chars);
+
+	return chars;
+}
+
+static void sierra_stop_rx_urbs(struct usb_serial_port *port)
+{
+	int i;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+
+	for (i = 0; i < portdata->num_in_urbs; i++)
+		usb_kill_urb(portdata->in_urbs[i]);
+
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
+{
+	int ok_cnt;
+	int err = -EINVAL;
+	int i;
+	struct urb *urb;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+
+	ok_cnt = 0;
+	for (i = 0; i < portdata->num_in_urbs; i++) {
+		urb = portdata->in_urbs[i];
+		if (!urb)
+			continue;
+		err = usb_submit_urb(urb, mem_flags);
+		if (err) {
+			dev_err(&port->dev, "%s: submit urb failed: %d\n",
+				__func__, err);
+		} else {
+			ok_cnt++;
+		}
+	}
+
+	if (ok_cnt && port->interrupt_in_urb) {
+		err = usb_submit_urb(port->interrupt_in_urb, mem_flags);
+		if (err) {
+			dev_err(&port->dev, "%s: submit intr urb failed: %d\n",
+				__func__, err);
+		}
+	}
+
+	if (ok_cnt > 0) /* at least one rx urb submitted */
+		return 0;
+	else
+		return err;
+}
+
+static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
+					int dir, void *ctx, int len,
+					gfp_t mem_flags,
+					usb_complete_t callback)
+{
+	struct urb	*urb;
+	u8		*buf;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (!urb)
+		return NULL;
+
+	buf = kmalloc(len, mem_flags);
+	if (buf) {
+		/* Fill URB using supplied data */
+		usb_fill_bulk_urb(urb, serial->dev,
+			usb_sndbulkpipe(serial->dev, endpoint) | dir,
+			buf, len, callback, ctx);
+
+		dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
+				dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
+	} else {
+		sierra_release_urb(urb);
+		urb = NULL;
+	}
+
+	return urb;
+}
+
+static void sierra_close(struct usb_serial_port *port)
+{
+	int i;
+	struct usb_serial *serial = port->serial;
+	struct sierra_port_private *portdata;
+	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+	struct urb *urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	/*
+	 * Need to take susp_lock to make sure port is not already being
+	 * resumed, but no need to hold it due to initialized
+	 */
+	spin_lock_irq(&intfdata->susp_lock);
+	if (--intfdata->open_ports == 0)
+		serial->interface->needs_remote_wakeup = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	for (;;) {
+		urb = usb_get_from_anchor(&portdata->delayed);
+		if (!urb)
+			break;
+		kfree(urb->transfer_buffer);
+		usb_free_urb(urb);
+		usb_autopm_put_interface_async(serial->interface);
+		spin_lock_irq(&portdata->lock);
+		portdata->outstanding_urbs--;
+		spin_unlock_irq(&portdata->lock);
+	}
+
+	sierra_stop_rx_urbs(port);
+	usb_kill_anchored_urbs(&portdata->active);
+
+	for (i = 0; i < portdata->num_in_urbs; i++) {
+		sierra_release_urb(portdata->in_urbs[i]);
+		portdata->in_urbs[i] = NULL;
+	}
+
+	usb_autopm_get_interface_no_resume(serial->interface);
+}
+
+static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct sierra_port_private *portdata;
+	struct usb_serial *serial = port->serial;
+	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+	int i;
+	int err;
+	int endpoint;
+	struct urb *urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	endpoint = port->bulk_in_endpointAddress;
+	for (i = 0; i < portdata->num_in_urbs; i++) {
+		urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
+					IN_BUFLEN, GFP_KERNEL,
+					sierra_indat_callback);
+		portdata->in_urbs[i] = urb;
+	}
+	/* clear halt condition */
+	usb_clear_halt(serial->dev,
+			usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
+
+	err = sierra_submit_rx_urbs(port, GFP_KERNEL);
+	if (err)
+		goto err_submit;
+
+	spin_lock_irq(&intfdata->susp_lock);
+	if (++intfdata->open_ports == 1)
+		serial->interface->needs_remote_wakeup = 1;
+	spin_unlock_irq(&intfdata->susp_lock);
+	usb_autopm_put_interface(serial->interface);
+
+	return 0;
+
+err_submit:
+	sierra_stop_rx_urbs(port);
+
+	for (i = 0; i < portdata->num_in_urbs; i++) {
+		sierra_release_urb(portdata->in_urbs[i]);
+		portdata->in_urbs[i] = NULL;
+	}
+
+	return err;
+}
+
+
+static void sierra_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct sierra_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+	portdata->rts_state = on;
+	portdata->dtr_state = on;
+
+	sierra_send_setup(port);
+}
+
+static int sierra_startup(struct usb_serial *serial)
+{
+	struct sierra_intf_private *intfdata;
+
+	intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
+	if (!intfdata)
+		return -ENOMEM;
+
+	spin_lock_init(&intfdata->susp_lock);
+
+	usb_set_serial_data(serial, intfdata);
+
+	/* Set Device mode to D0 */
+	sierra_set_power_state(serial->dev, 0x0000);
+
+	/* Check NMEA and set */
+	if (nmea)
+		sierra_vsc_set_nmea(serial->dev, 1);
+
+	return 0;
+}
+
+static void sierra_release(struct usb_serial *serial)
+{
+	struct sierra_intf_private *intfdata;
+
+	intfdata = usb_get_serial_data(serial);
+	kfree(intfdata);
+}
+
+static int sierra_port_probe(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct sierra_port_private *portdata;
+	const struct sierra_iface_info *himemoryp;
+	u8 ifnum;
+
+	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+	if (!portdata)
+		return -ENOMEM;
+
+	spin_lock_init(&portdata->lock);
+	init_usb_anchor(&portdata->active);
+	init_usb_anchor(&portdata->delayed);
+
+	/* Assume low memory requirements */
+	portdata->num_out_urbs = N_OUT_URB;
+	portdata->num_in_urbs  = N_IN_URB;
+
+	/* Determine actual memory requirements */
+	if (serial->num_ports == 1) {
+		/* Get interface number for composite device */
+		ifnum = sierra_interface_num(serial);
+		himemoryp = &typeB_interface_list;
+	} else {
+		/* This is really the usb-serial port number of the interface
+		 * rather than the interface number.
+		 */
+		ifnum = port->port_number;
+		himemoryp = &typeA_interface_list;
+	}
+
+	if (is_himemory(ifnum, himemoryp)) {
+		portdata->num_out_urbs = N_OUT_URB_HM;
+		portdata->num_in_urbs  = N_IN_URB_HM;
+	}
+
+	dev_dbg(&port->dev,
+			"Memory usage (urbs) interface #%d, in=%d, out=%d\n",
+			ifnum, portdata->num_in_urbs, portdata->num_out_urbs);
+
+	usb_set_serial_port_data(port, portdata);
+
+	return 0;
+}
+
+static int sierra_port_remove(struct usb_serial_port *port)
+{
+	struct sierra_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+	usb_set_serial_port_data(port, NULL);
+	kfree(portdata);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static void stop_read_write_urbs(struct usb_serial *serial)
+{
+	int i;
+	struct usb_serial_port *port;
+	struct sierra_port_private *portdata;
+
+	/* Stop reading/writing urbs */
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+		sierra_stop_rx_urbs(port);
+		usb_kill_anchored_urbs(&portdata->active);
+	}
+}
+
+static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
+{
+	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+
+	spin_lock_irq(&intfdata->susp_lock);
+	if (PMSG_IS_AUTO(message)) {
+		if (intfdata->in_flight) {
+			spin_unlock_irq(&intfdata->susp_lock);
+			return -EBUSY;
+		}
+	}
+	intfdata->suspended = 1;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	stop_read_write_urbs(serial);
+
+	return 0;
+}
+
+/* Caller must hold susp_lock. */
+static int sierra_submit_delayed_urbs(struct usb_serial_port *port)
+{
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	struct sierra_intf_private *intfdata;
+	struct urb *urb;
+	int ec = 0;
+	int err;
+
+	intfdata = usb_get_serial_data(port->serial);
+
+	for (;;) {
+		urb = usb_get_from_anchor(&portdata->delayed);
+		if (!urb)
+			break;
+
+		usb_anchor_urb(urb, &portdata->active);
+		intfdata->in_flight++;
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err) {
+			dev_err(&port->dev, "%s - submit urb failed: %d",
+					__func__, err);
+			ec++;
+			intfdata->in_flight--;
+			usb_unanchor_urb(urb);
+			kfree(urb->transfer_buffer);
+			usb_free_urb(urb);
+
+			spin_lock(&portdata->lock);
+			portdata->outstanding_urbs--;
+			spin_unlock(&portdata->lock);
+		}
+	}
+
+	if (ec)
+		return -EIO;
+
+	return 0;
+}
+
+static int sierra_resume(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+	int ec = 0;
+	int i, err;
+
+	spin_lock_irq(&intfdata->susp_lock);
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+
+		if (!tty_port_initialized(&port->port))
+			continue;
+
+		err = sierra_submit_delayed_urbs(port);
+		if (err)
+			ec++;
+
+		err = sierra_submit_rx_urbs(port, GFP_ATOMIC);
+		if (err)
+			ec++;
+	}
+	intfdata->suspended = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	return ec ? -EIO : 0;
+}
+
+#else
+#define sierra_suspend NULL
+#define sierra_resume NULL
+#endif
+
+static struct usb_serial_driver sierra_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"sierra",
+	},
+	.description       = "Sierra USB modem",
+	.id_table          = id_table,
+	.calc_num_ports	   = sierra_calc_num_ports,
+	.probe		   = sierra_probe,
+	.open              = sierra_open,
+	.close             = sierra_close,
+	.dtr_rts	   = sierra_dtr_rts,
+	.write             = sierra_write,
+	.write_room        = sierra_write_room,
+	.chars_in_buffer   = sierra_chars_in_buffer,
+	.tiocmget          = sierra_tiocmget,
+	.tiocmset          = sierra_tiocmset,
+	.attach            = sierra_startup,
+	.release           = sierra_release,
+	.port_probe        = sierra_port_probe,
+	.port_remove       = sierra_port_remove,
+	.suspend	   = sierra_suspend,
+	.resume		   = sierra_resume,
+	.read_int_callback = sierra_instat_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&sierra_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
+
+module_param(nmea, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nmea, "NMEA streaming");
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
new file mode 100644
index 0000000..b427148
--- /dev/null
+++ b/drivers/usb/serial/spcp8x5.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * spcp8x5 USB to serial adaptor driver
+ *
+ * Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com)
+ * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn)
+ * Copyright (C) 2006 S1 Corp.
+ *
+ * Original driver for 2.6.10 pl2303 driver by
+ *   Greg Kroah-Hartman (greg@kroah.com)
+ * Changes for 2.6.20 by Harald Klein <hari@vt100.at>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_DESC	"SPCP8x5 USB to serial adaptor driver"
+
+#define SPCP825_QUIRK_NO_UART_STATUS	0x01
+#define SPCP825_QUIRK_NO_WORK_MODE	0x02
+
+#define SPCP8x5_007_VID		0x04FC
+#define SPCP8x5_007_PID		0x0201
+#define SPCP8x5_008_VID		0x04fc
+#define SPCP8x5_008_PID		0x0235
+#define SPCP8x5_PHILIPS_VID	0x0471
+#define SPCP8x5_PHILIPS_PID	0x081e
+#define SPCP8x5_INTERMATIC_VID	0x04FC
+#define SPCP8x5_INTERMATIC_PID	0x0204
+#define SPCP8x5_835_VID		0x04fc
+#define SPCP8x5_835_PID		0x0231
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(SPCP8x5_PHILIPS_VID , SPCP8x5_PHILIPS_PID)},
+	{ USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)},
+	{ USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)},
+	{ USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)},
+	{ USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID),
+	  .driver_info = SPCP825_QUIRK_NO_UART_STATUS |
+				SPCP825_QUIRK_NO_WORK_MODE },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct spcp8x5_usb_ctrl_arg {
+	u8	type;
+	u8	cmd;
+	u8	cmd_type;
+	u16	value;
+	u16	index;
+	u16	length;
+};
+
+
+/* spcp8x5 spec register define */
+#define MCR_CONTROL_LINE_RTS		0x02
+#define MCR_CONTROL_LINE_DTR		0x01
+#define MCR_DTR				0x01
+#define MCR_RTS				0x02
+
+#define MSR_STATUS_LINE_DCD		0x80
+#define MSR_STATUS_LINE_RI		0x40
+#define MSR_STATUS_LINE_DSR		0x20
+#define MSR_STATUS_LINE_CTS		0x10
+
+/* verdor command here , we should define myself */
+#define SET_DEFAULT			0x40
+#define SET_DEFAULT_TYPE		0x20
+
+#define SET_UART_FORMAT			0x40
+#define SET_UART_FORMAT_TYPE		0x21
+#define SET_UART_FORMAT_SIZE_5		0x00
+#define SET_UART_FORMAT_SIZE_6		0x01
+#define SET_UART_FORMAT_SIZE_7		0x02
+#define SET_UART_FORMAT_SIZE_8		0x03
+#define SET_UART_FORMAT_STOP_1		0x00
+#define SET_UART_FORMAT_STOP_2		0x04
+#define SET_UART_FORMAT_PAR_NONE	0x00
+#define SET_UART_FORMAT_PAR_ODD		0x10
+#define SET_UART_FORMAT_PAR_EVEN	0x30
+#define SET_UART_FORMAT_PAR_MASK	0xD0
+#define SET_UART_FORMAT_PAR_SPACE	0x90
+
+#define GET_UART_STATUS_TYPE		0xc0
+#define GET_UART_STATUS			0x22
+#define GET_UART_STATUS_MSR		0x06
+
+#define SET_UART_STATUS			0x40
+#define SET_UART_STATUS_TYPE		0x23
+#define SET_UART_STATUS_MCR		0x0004
+#define SET_UART_STATUS_MCR_DTR		0x01
+#define SET_UART_STATUS_MCR_RTS		0x02
+#define SET_UART_STATUS_MCR_LOOP	0x10
+
+#define SET_WORKING_MODE		0x40
+#define SET_WORKING_MODE_TYPE		0x24
+#define SET_WORKING_MODE_U2C		0x00
+#define SET_WORKING_MODE_RS485		0x01
+#define SET_WORKING_MODE_PDMA		0x02
+#define SET_WORKING_MODE_SPP		0x03
+
+#define SET_FLOWCTL_CHAR		0x40
+#define SET_FLOWCTL_CHAR_TYPE		0x25
+
+#define GET_VERSION			0xc0
+#define GET_VERSION_TYPE		0x26
+
+#define SET_REGISTER			0x40
+#define SET_REGISTER_TYPE		0x27
+
+#define	GET_REGISTER			0xc0
+#define GET_REGISTER_TYPE		0x28
+
+#define SET_RAM				0x40
+#define SET_RAM_TYPE			0x31
+
+#define GET_RAM				0xc0
+#define GET_RAM_TYPE			0x32
+
+/* how come ??? */
+#define UART_STATE			0x08
+#define UART_STATE_TRANSIENT_MASK	0x75
+#define UART_DCD			0x01
+#define UART_DSR			0x02
+#define UART_BREAK_ERROR		0x04
+#define UART_RING			0x08
+#define UART_FRAME_ERROR		0x10
+#define UART_PARITY_ERROR		0x20
+#define UART_OVERRUN_ERROR		0x40
+#define UART_CTS			0x80
+
+struct spcp8x5_private {
+	unsigned		quirks;
+	spinlock_t		lock;
+	u8			line_control;
+};
+
+static int spcp8x5_probe(struct usb_serial *serial,
+						const struct usb_device_id *id)
+{
+	usb_set_serial_data(serial, (void *)id);
+
+	return 0;
+}
+
+static int spcp8x5_port_probe(struct usb_serial_port *port)
+{
+	const struct usb_device_id *id = usb_get_serial_data(port->serial);
+	struct spcp8x5_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->quirks = id->driver_info;
+
+	usb_set_serial_port_data(port, priv);
+
+	port->port.drain_delay = 256;
+
+	return 0;
+}
+
+static int spcp8x5_port_remove(struct usb_serial_port *port)
+{
+	struct spcp8x5_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *dev = port->serial->dev;
+	int retval;
+
+	if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS)
+		return -EPERM;
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 SET_UART_STATUS_TYPE, SET_UART_STATUS,
+				 mcr, 0x04, NULL, 0, 100);
+	if (retval != 0) {
+		dev_err(&port->dev, "failed to set control lines: %d\n",
+								retval);
+	}
+	return retval;
+}
+
+static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *dev = port->serial->dev;
+	u8 *buf;
+	int ret;
+
+	if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS)
+		return -EPERM;
+
+	buf = kzalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			      GET_UART_STATUS, GET_UART_STATUS_TYPE,
+			      0, GET_UART_STATUS_MSR, buf, 1, 100);
+	if (ret < 1) {
+		dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+
+	dev_dbg(&port->dev, "0xc0:0x22:0:6  %d - 0x02%x\n", ret, *buf);
+	*status = *buf;
+	ret = 0;
+out:
+	kfree(buf);
+
+	return ret;
+}
+
+static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value,
+								 u16 index)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *dev = port->serial->dev;
+	int ret;
+
+	if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE)
+		return;
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			      SET_WORKING_MODE_TYPE, SET_WORKING_MODE,
+			      value, index, NULL, 0, 100);
+	dev_dbg(&port->dev, "value = %#x , index = %#x\n", value, index);
+	if (ret < 0)
+		dev_err(&port->dev, "failed to set work mode: %d\n", ret);
+}
+
+static int spcp8x5_carrier_raised(struct usb_serial_port *port)
+{
+	u8 msr;
+	int ret;
+
+	ret = spcp8x5_get_msr(port, &msr);
+	if (ret || msr & MSR_STATUS_LINE_DCD)
+		return 1;
+
+	return 0;
+}
+
+static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (on)
+		priv->line_control = MCR_CONTROL_LINE_DTR
+						| MCR_CONTROL_LINE_RTS;
+	else
+		priv->line_control &= ~ (MCR_CONTROL_LINE_DTR
+						| MCR_CONTROL_LINE_RTS);
+	control = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	spcp8x5_set_ctrl_line(port, control);
+}
+
+static void spcp8x5_init_termios(struct tty_struct *tty)
+{
+	tty->termios = tty_std_termios;
+	tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty->termios.c_ispeed = 115200;
+	tty->termios.c_ospeed = 115200;
+}
+
+static void spcp8x5_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int cflag = tty->termios.c_cflag;
+	unsigned short uartdata;
+	unsigned char buf[2] = {0, 0};
+	int baud;
+	int i;
+	u8 control;
+
+	/* check that they really want us to change something */
+	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+		return;
+
+	/* set DTR/RTS active */
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	if (old_termios && (old_termios->c_cflag & CBAUD) == B0) {
+		priv->line_control |= MCR_DTR;
+		if (!(old_termios->c_cflag & CRTSCTS))
+			priv->line_control |= MCR_RTS;
+	}
+	if (control != priv->line_control) {
+		control = priv->line_control;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		spcp8x5_set_ctrl_line(port, control);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	/* Set Baud Rate */
+	baud = tty_get_baud_rate(tty);
+	switch (baud) {
+	case 300:	buf[0] = 0x00;	break;
+	case 600:	buf[0] = 0x01;	break;
+	case 1200:	buf[0] = 0x02;	break;
+	case 2400:	buf[0] = 0x03;	break;
+	case 4800:	buf[0] = 0x04;	break;
+	case 9600:	buf[0] = 0x05;	break;
+	case 19200:	buf[0] = 0x07;	break;
+	case 38400:	buf[0] = 0x09;	break;
+	case 57600:	buf[0] = 0x0a;	break;
+	case 115200:	buf[0] = 0x0b;	break;
+	case 230400:	buf[0] = 0x0c;	break;
+	case 460800:	buf[0] = 0x0d;	break;
+	case 921600:	buf[0] = 0x0e;	break;
+/*	case 1200000:	buf[0] = 0x0f;	break; */
+/*	case 2400000:	buf[0] = 0x10;	break; */
+	case 3000000:	buf[0] = 0x11;	break;
+/*	case 6000000:	buf[0] = 0x12;	break; */
+	case 0:
+	case 1000000:
+			buf[0] = 0x0b;	break;
+	default:
+		dev_err(&port->dev, "unsupported baudrate, using 9600\n");
+	}
+
+	/* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */
+	switch (cflag & CSIZE) {
+	case CS5:
+		buf[1] |= SET_UART_FORMAT_SIZE_5;
+		break;
+	case CS6:
+		buf[1] |= SET_UART_FORMAT_SIZE_6;
+		break;
+	case CS7:
+		buf[1] |= SET_UART_FORMAT_SIZE_7;
+		break;
+	default:
+	case CS8:
+		buf[1] |= SET_UART_FORMAT_SIZE_8;
+		break;
+	}
+
+	/* Set Stop bit2 : 0:1bit 1:2bit */
+	buf[1] |= (cflag & CSTOPB) ? SET_UART_FORMAT_STOP_2 :
+				     SET_UART_FORMAT_STOP_1;
+
+	/* Set Parity bit3-4 01:Odd 11:Even */
+	if (cflag & PARENB) {
+		buf[1] |= (cflag & PARODD) ?
+		SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ;
+	} else {
+		buf[1] |= SET_UART_FORMAT_PAR_NONE;
+	}
+	uartdata = buf[0] | buf[1]<<8;
+
+	i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    SET_UART_FORMAT_TYPE, SET_UART_FORMAT,
+			    uartdata, 0, NULL, 0, 100);
+	if (i < 0)
+		dev_err(&port->dev, "Set UART format %#x failed (error = %d)\n",
+			uartdata, i);
+	dev_dbg(&port->dev, "0x21:0x40:0:0  %d\n", i);
+
+	if (cflag & CRTSCTS) {
+		/* enable hardware flow control */
+		spcp8x5_set_work_mode(port, 0x000a, SET_WORKING_MODE_U2C);
+	}
+}
+
+static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	int ret;
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			      0x09, 0x00,
+			      0x01, 0x00, NULL, 0x00, 100);
+	if (ret)
+		return ret;
+
+	spcp8x5_set_ctrl_line(port, priv->line_control);
+
+	if (tty)
+		spcp8x5_set_termios(tty, port, NULL);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static int spcp8x5_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (set & TIOCM_RTS)
+		priv->line_control |= MCR_RTS;
+	if (set & TIOCM_DTR)
+		priv->line_control |= MCR_DTR;
+	if (clear & TIOCM_RTS)
+		priv->line_control &= ~MCR_RTS;
+	if (clear & TIOCM_DTR)
+		priv->line_control &= ~MCR_DTR;
+	control = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return spcp8x5_set_ctrl_line(port, control);
+}
+
+static int spcp8x5_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int mcr;
+	u8 status;
+	unsigned int result;
+
+	result = spcp8x5_get_msr(port, &status);
+	if (result)
+		return result;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	mcr = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	result = ((mcr & MCR_DTR)			? TIOCM_DTR : 0)
+		  | ((mcr & MCR_RTS)			? TIOCM_RTS : 0)
+		  | ((status & MSR_STATUS_LINE_CTS)	? TIOCM_CTS : 0)
+		  | ((status & MSR_STATUS_LINE_DSR)	? TIOCM_DSR : 0)
+		  | ((status & MSR_STATUS_LINE_RI)	? TIOCM_RI  : 0)
+		  | ((status & MSR_STATUS_LINE_DCD)	? TIOCM_CD  : 0);
+
+	return result;
+}
+
+static struct usb_serial_driver spcp8x5_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"SPCP8x5",
+	},
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.open			= spcp8x5_open,
+	.dtr_rts		= spcp8x5_dtr_rts,
+	.carrier_raised		= spcp8x5_carrier_raised,
+	.set_termios		= spcp8x5_set_termios,
+	.init_termios		= spcp8x5_init_termios,
+	.tiocmget		= spcp8x5_tiocmget,
+	.tiocmset		= spcp8x5_tiocmset,
+	.probe			= spcp8x5_probe,
+	.port_probe		= spcp8x5_port_probe,
+	.port_remove		= spcp8x5_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&spcp8x5_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
new file mode 100644
index 0000000..0900b47
--- /dev/null
+++ b/drivers/usb/serial/ssu100.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * usb-serial driver for Quatech SSU-100
+ *
+ * based on ftdi_sio.c and the original serqt_usb.c from Quatech
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/uaccess.h>
+
+#define QT_OPEN_CLOSE_CHANNEL       0xca
+#define QT_SET_GET_DEVICE           0xc2
+#define QT_SET_GET_REGISTER         0xc0
+#define QT_GET_SET_PREBUF_TRIG_LVL  0xcc
+#define QT_SET_ATF                  0xcd
+#define QT_GET_SET_UART             0xc1
+#define QT_TRANSFER_IN              0xc0
+#define QT_HW_FLOW_CONTROL_MASK     0xc5
+#define QT_SW_FLOW_CONTROL_MASK     0xc6
+
+#define  SERIAL_MSR_MASK            0xf0
+
+#define  SERIAL_CRTSCTS ((UART_MCR_RTS << 8) | UART_MSR_CTS)
+
+#define  SERIAL_EVEN_PARITY         (UART_LCR_PARITY | UART_LCR_EPAR)
+
+#define  MAX_BAUD_RATE              460800
+
+#define ATC_DISABLED                0x00
+#define DUPMODE_BITS        0xc0
+#define RR_BITS             0x03
+#define LOOPMODE_BITS       0x41
+#define RS232_MODE          0x00
+#define RTSCTS_TO_CONNECTOR 0x40
+#define CLKS_X4             0x02
+#define FULLPWRBIT          0x00000080
+#define NEXT_BOARD_POWER_BIT        0x00000004
+
+#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
+
+#define	USB_VENDOR_ID_QUATECH	0x061d	/* Quatech VID */
+#define QUATECH_SSU100	0xC020	/* SSU100 */
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)},
+	{}			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct ssu100_port_private {
+	spinlock_t status_lock;
+	u8 shadowLSR;
+	u8 shadowMSR;
+};
+
+static inline int ssu100_control_msg(struct usb_device *dev,
+				     u8 request, u16 data, u16 index)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       request, 0x40, data, index,
+			       NULL, 0, 300);
+}
+
+static inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
+{
+	u16 x = ((u16)(data[1] << 8) | (u16)(data[0]));
+
+	return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
+}
+
+
+static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
+{
+	int ret;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			      QT_SET_GET_DEVICE, 0xc0, 0, 0,
+			      data, 3, 300);
+	if (ret < 3) {
+		if (ret >= 0)
+			ret = -EIO;
+	}
+
+	return ret;
+}
+
+static inline int ssu100_getregister(struct usb_device *dev,
+				     unsigned short uart,
+				     unsigned short reg,
+				     u8 *data)
+{
+	int ret;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			      QT_SET_GET_REGISTER, 0xc0, reg,
+			      uart, data, sizeof(*data), 300);
+	if (ret < (int)sizeof(*data)) {
+		if (ret >= 0)
+			ret = -EIO;
+	}
+
+	return ret;
+}
+
+
+static inline int ssu100_setregister(struct usb_device *dev,
+				     unsigned short uart,
+				     unsigned short reg,
+				     u16 data)
+{
+	u16 value = (data << 8) | reg;
+
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       QT_SET_GET_REGISTER, 0x40, value, uart,
+			       NULL, 0, 300);
+
+}
+
+#define set_mctrl(dev, set)		update_mctrl((dev), (set), 0)
+#define clear_mctrl(dev, clear)	update_mctrl((dev), 0, (clear))
+
+/* these do not deal with device that have more than 1 port */
+static inline int update_mctrl(struct usb_device *dev, unsigned int set,
+			       unsigned int clear)
+{
+	unsigned urb_value;
+	int result;
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+		dev_dbg(&dev->dev, "%s - DTR|RTS not being set|cleared\n", __func__);
+		return 0;	/* no change */
+	}
+
+	clear &= ~set;	/* 'set' takes precedence over 'clear' */
+	urb_value = 0;
+	if (set & TIOCM_DTR)
+		urb_value |= UART_MCR_DTR;
+	if (set & TIOCM_RTS)
+		urb_value |= UART_MCR_RTS;
+
+	result = ssu100_setregister(dev, 0, UART_MCR, urb_value);
+	if (result < 0)
+		dev_dbg(&dev->dev, "%s Error from MODEM_CTRL urb\n", __func__);
+
+	return result;
+}
+
+static int ssu100_initdevice(struct usb_device *dev)
+{
+	u8 *data;
+	int result = 0;
+
+	data = kzalloc(3, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	result = ssu100_getdevice(dev, data);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
+		goto out;
+	}
+
+	data[1] &= ~FULLPWRBIT;
+
+	result = ssu100_setdevice(dev, data);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - set prebuffer level failed %i\n", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - set ATFprebuffer level failed %i\n", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_getdevice(dev, data);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
+		goto out;
+	}
+
+	data[0] &= ~(RR_BITS | DUPMODE_BITS);
+	data[0] |= CLKS_X4;
+	data[1] &= ~(LOOPMODE_BITS);
+	data[1] |= RS232_MODE;
+
+	result = ssu100_setdevice(dev, data);
+	if (result < 0) {
+		dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
+		goto out;
+	}
+
+out:	kfree(data);
+	return result;
+
+}
+
+
+static void ssu100_set_termios(struct tty_struct *tty,
+			       struct usb_serial_port *port,
+			       struct ktermios *old_termios)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct ktermios *termios = &tty->termios;
+	u16 baud, divisor, remainder;
+	unsigned int cflag = termios->c_cflag;
+	u16 urb_value = 0; /* will hold the new flags */
+	int result;
+
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			urb_value |= UART_LCR_PARITY;
+		else
+			urb_value |= SERIAL_EVEN_PARITY;
+	}
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		urb_value |= UART_LCR_WLEN5;
+		break;
+	case CS6:
+		urb_value |= UART_LCR_WLEN6;
+		break;
+	case CS7:
+		urb_value |= UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		urb_value |= UART_LCR_WLEN8;
+		break;
+	}
+
+	baud = tty_get_baud_rate(tty);
+	if (!baud)
+		baud = 9600;
+
+	dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
+
+
+	divisor = MAX_BAUD_RATE / baud;
+	remainder = MAX_BAUD_RATE % baud;
+	if (((remainder * 2) >= baud) && (baud != 110))
+		divisor++;
+
+	urb_value = urb_value << 8;
+
+	result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
+	if (result < 0)
+		dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
+
+	if (cflag & CRTSCTS)
+		result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+					    SERIAL_CRTSCTS, 0);
+	else
+		result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+					    0, 0);
+	if (result < 0)
+		dev_dbg(&port->dev, "%s - set HW flow control failed\n", __func__);
+
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
+
+		result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+					    x, 0);
+	} else
+		result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+					    0, 0);
+
+	if (result < 0)
+		dev_dbg(&port->dev, "%s - set SW flow control failed\n", __func__);
+
+}
+
+
+static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_device *dev = port->serial->dev;
+	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+	u8 *data;
+	int result;
+	unsigned long flags;
+
+	data = kzalloc(2, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				 QT_OPEN_CLOSE_CHANNEL,
+				 QT_TRANSFER_IN, 0x01,
+				 0, data, 2, 300);
+	if (result < 2) {
+		dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
+		if (result >= 0)
+			result = -EIO;
+		kfree(data);
+		return result;
+	}
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	priv->shadowLSR = data[0];
+	priv->shadowMSR = data[1];
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	kfree(data);
+
+/* set to 9600 */
+	result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
+	if (result < 0)
+		dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
+
+	if (tty)
+		ssu100_set_termios(tty, port, &tty->termios);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line		= port->minor;
+	tmp.port		= 0;
+	tmp.irq			= 0;
+	tmp.xmit_fifo_size	= port->bulk_out_size;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int ssu100_ioctl(struct tty_struct *tty,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int ssu100_attach(struct usb_serial *serial)
+{
+	return ssu100_initdevice(serial->dev);
+}
+
+static int ssu100_port_probe(struct usb_serial_port *port)
+{
+	struct ssu100_port_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->status_lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int ssu100_port_remove(struct usb_serial_port *port)
+{
+	struct ssu100_port_private *priv;
+
+	priv = usb_get_serial_port_data(port);
+	kfree(priv);
+
+	return 0;
+}
+
+static int ssu100_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_device *dev = port->serial->dev;
+	u8 *d;
+	int r;
+
+	d = kzalloc(2, GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	r = ssu100_getregister(dev, 0, UART_MCR, d);
+	if (r < 0)
+		goto mget_out;
+
+	r = ssu100_getregister(dev, 0, UART_MSR, d+1);
+	if (r < 0)
+		goto mget_out;
+
+	r = (d[0] & UART_MCR_DTR ? TIOCM_DTR : 0) |
+		(d[0] & UART_MCR_RTS ? TIOCM_RTS : 0) |
+		(d[1] & UART_MSR_CTS ? TIOCM_CTS : 0) |
+		(d[1] & UART_MSR_DCD ? TIOCM_CAR : 0) |
+		(d[1] & UART_MSR_RI ? TIOCM_RI : 0) |
+		(d[1] & UART_MSR_DSR ? TIOCM_DSR : 0);
+
+mget_out:
+	kfree(d);
+	return r;
+}
+
+static int ssu100_tiocmset(struct tty_struct *tty,
+			   unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_device *dev = port->serial->dev;
+
+	return update_mctrl(dev, set, clear);
+}
+
+static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_device *dev = port->serial->dev;
+
+	/* Disable flow control */
+	if (!on) {
+		if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
+			dev_err(&port->dev, "error from flowcontrol urb\n");
+	}
+	/* drop RTS and DTR */
+	if (on)
+		set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+	else
+		clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+}
+
+static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
+{
+	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	priv->shadowMSR = msr;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	if (msr & UART_MSR_ANY_DELTA) {
+		/* 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);
+	}
+}
+
+static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
+			      char *tty_flag)
+{
+	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	priv->shadowLSR = lsr;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	*tty_flag = TTY_NORMAL;
+	if (lsr & UART_LSR_BRK_ERROR_BITS) {
+		/* we always want to update icount, but we only want to
+		 * update tty_flag for one case */
+		if (lsr & UART_LSR_BI) {
+			port->icount.brk++;
+			*tty_flag = TTY_BREAK;
+			usb_serial_handle_break(port);
+		}
+		if (lsr & UART_LSR_PE) {
+			port->icount.parity++;
+			if (*tty_flag == TTY_NORMAL)
+				*tty_flag = TTY_PARITY;
+		}
+		if (lsr & UART_LSR_FE) {
+			port->icount.frame++;
+			if (*tty_flag == TTY_NORMAL)
+				*tty_flag = TTY_FRAME;
+		}
+		if (lsr & UART_LSR_OE) {
+			port->icount.overrun++;
+			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+		}
+	}
+
+}
+
+static void ssu100_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	char *packet = (char *)urb->transfer_buffer;
+	char flag = TTY_NORMAL;
+	u32 len = urb->actual_length;
+	int i;
+	char *ch;
+
+	if ((len >= 4) &&
+	    (packet[0] == 0x1b) && (packet[1] == 0x1b) &&
+	    ((packet[2] == 0x00) || (packet[2] == 0x01))) {
+		if (packet[2] == 0x00)
+			ssu100_update_lsr(port, packet[3], &flag);
+		if (packet[2] == 0x01)
+			ssu100_update_msr(port, packet[3]);
+
+		len -= 4;
+		ch = packet + 4;
+	} else
+		ch = packet;
+
+	if (!len)
+		return;	/* status only */
+
+	if (port->port.console && port->sysrq) {
+		for (i = 0; i < len; i++, ch++) {
+			if (!usb_serial_handle_sysrq_char(port, *ch))
+				tty_insert_flip_char(&port->port, *ch, flag);
+		}
+	} else
+		tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
+
+	tty_flip_buffer_push(&port->port);
+}
+
+static struct usb_serial_driver ssu100_device = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "ssu100",
+	},
+	.description	     = DRIVER_DESC,
+	.id_table	     = id_table,
+	.num_ports	     = 1,
+	.open		     = ssu100_open,
+	.attach              = ssu100_attach,
+	.port_probe          = ssu100_port_probe,
+	.port_remove         = ssu100_port_remove,
+	.dtr_rts             = ssu100_dtr_rts,
+	.process_read_urb    = ssu100_process_read_urb,
+	.tiocmget            = ssu100_tiocmget,
+	.tiocmset            = ssu100_tiocmset,
+	.tiocmiwait          = usb_serial_generic_tiocmiwait,
+	.get_icount	     = usb_serial_generic_get_icount,
+	.ioctl               = ssu100_ioctl,
+	.set_termios         = ssu100_set_termios,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ssu100_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
new file mode 100644
index 0000000..6ca24e8
--- /dev/null
+++ b/drivers/usb/serial/symbolserial.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Symbol USB barcode to serial driver
+ *
+ * Copyright (C) 2013 Johan Hovold <jhovold@gmail.com>
+ * Copyright (C) 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2009 Novell Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x05e0, 0x0600) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct symbol_private {
+	spinlock_t lock;	/* protects the following flags */
+	bool throttled;
+	bool actually_throttled;
+};
+
+static void symbol_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct symbol_private *priv = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	unsigned long flags;
+	int result;
+	int data_length;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+	/*
+	 * Data from the device comes with a 1 byte header:
+	 *
+	 * <size of data> <data>...
+	 */
+	if (urb->actual_length > 1) {
+		data_length = data[0];
+		if (data_length > (urb->actual_length - 1))
+			data_length = urb->actual_length - 1;
+		tty_insert_flip_string(&port->port, &data[1], data_length);
+		tty_flip_buffer_push(&port->port);
+	} else {
+		dev_dbg(&port->dev, "%s - short packet\n", __func__);
+	}
+
+exit:
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Continue trying to always read if we should */
+	if (!priv->throttled) {
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+		if (result)
+			dev_err(&port->dev,
+			    "%s - failed resubmitting read urb, error %d\n",
+							__func__, result);
+	} else
+		priv->actually_throttled = true;
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct symbol_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int result = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->throttled = false;
+	priv->actually_throttled = false;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Start reading from the device */
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result)
+		dev_err(&port->dev,
+			"%s - failed resubmitting read urb, error %d\n",
+			__func__, result);
+	return result;
+}
+
+static void symbol_close(struct usb_serial_port *port)
+{
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static void symbol_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct symbol_private *priv = usb_get_serial_port_data(port);
+
+	spin_lock_irq(&priv->lock);
+	priv->throttled = true;
+	spin_unlock_irq(&priv->lock);
+}
+
+static void symbol_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct symbol_private *priv = usb_get_serial_port_data(port);
+	int result;
+	bool was_throttled;
+
+	spin_lock_irq(&priv->lock);
+	priv->throttled = false;
+	was_throttled = priv->actually_throttled;
+	priv->actually_throttled = false;
+	spin_unlock_irq(&priv->lock);
+
+	if (was_throttled) {
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (result)
+			dev_err(&port->dev,
+				"%s - failed submitting read urb, error %d\n",
+							__func__, result);
+	}
+}
+
+static int symbol_port_probe(struct usb_serial_port *port)
+{
+	struct symbol_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+static int symbol_port_remove(struct usb_serial_port *port)
+{
+	struct symbol_private *priv = usb_get_serial_port_data(port);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct usb_serial_driver symbol_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"symbol",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.num_interrupt_in =	1,
+	.port_probe =		symbol_port_probe,
+	.port_remove =		symbol_port_remove,
+	.open =			symbol_open,
+	.close =		symbol_close,
+	.throttle = 		symbol_throttle,
+	.unthrottle =		symbol_unthrottle,
+	.read_int_callback =	symbol_int_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&symbol_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
new file mode 100644
index 0000000..e3c5832
--- /dev/null
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -0,0 +1,1726 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI 3410/5052 USB Serial Driver
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This driver is based on the Linux io_ti driver, which is
+ *   Copyright (C) 2000-2002 Inside Out Networks
+ *   Copyright (C) 2001-2002 Greg Kroah-Hartman
+ *
+ * For questions or problems with this driver, contact Texas Instruments
+ * technical support, or Al Borchers <alborchers@steinerpoint.com>, or
+ * Peter Berger <pberger@brimson.com>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+#include <linux/serial.h>
+#include <linux/kfifo.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* Configuration ids */
+#define TI_BOOT_CONFIG			1
+#define TI_ACTIVE_CONFIG		2
+
+/* Vendor and product ids */
+#define TI_VENDOR_ID			0x0451
+#define IBM_VENDOR_ID			0x04b3
+#define TI_3410_PRODUCT_ID		0x3410
+#define IBM_4543_PRODUCT_ID		0x4543
+#define IBM_454B_PRODUCT_ID		0x454b
+#define IBM_454C_PRODUCT_ID		0x454c
+#define TI_3410_EZ430_ID		0xF430  /* TI ez430 development tool */
+#define TI_5052_BOOT_PRODUCT_ID		0x5052	/* no EEPROM, no firmware */
+#define TI_5152_BOOT_PRODUCT_ID		0x5152	/* no EEPROM, no firmware */
+#define TI_5052_EEPROM_PRODUCT_ID	0x505A	/* EEPROM, no firmware */
+#define TI_5052_FIRMWARE_PRODUCT_ID	0x505F	/* firmware is running */
+#define FRI2_PRODUCT_ID			0x5053  /* Fish River Island II */
+
+/* Multi-Tech vendor and product ids */
+#define MTS_VENDOR_ID			0x06E0
+#define MTS_GSM_NO_FW_PRODUCT_ID	0xF108
+#define MTS_CDMA_NO_FW_PRODUCT_ID	0xF109
+#define MTS_CDMA_PRODUCT_ID		0xF110
+#define MTS_GSM_PRODUCT_ID		0xF111
+#define MTS_EDGE_PRODUCT_ID		0xF112
+#define MTS_MT9234MU_PRODUCT_ID		0xF114
+#define MTS_MT9234ZBA_PRODUCT_ID	0xF115
+#define MTS_MT9234ZBAOLD_PRODUCT_ID	0x0319
+
+/* Abbott Diabetics vendor and product ids */
+#define ABBOTT_VENDOR_ID		0x1a61
+#define ABBOTT_STEREO_PLUG_ID		0x3410
+#define ABBOTT_PRODUCT_ID		ABBOTT_STEREO_PLUG_ID
+#define ABBOTT_STRIP_PORT_ID		0x3420
+
+/* Honeywell vendor and product IDs */
+#define HONEYWELL_VENDOR_ID		0x10ac
+#define HONEYWELL_HGI80_PRODUCT_ID	0x0102  /* Honeywell HGI80 */
+
+/* Moxa UPORT 11x0 vendor and product IDs */
+#define MXU1_VENDOR_ID				0x110a
+#define MXU1_1110_PRODUCT_ID			0x1110
+#define MXU1_1130_PRODUCT_ID			0x1130
+#define MXU1_1150_PRODUCT_ID			0x1150
+#define MXU1_1151_PRODUCT_ID			0x1151
+#define MXU1_1131_PRODUCT_ID			0x1131
+
+/* Commands */
+#define TI_GET_VERSION			0x01
+#define TI_GET_PORT_STATUS		0x02
+#define TI_GET_PORT_DEV_INFO		0x03
+#define TI_GET_CONFIG			0x04
+#define TI_SET_CONFIG			0x05
+#define TI_OPEN_PORT			0x06
+#define TI_CLOSE_PORT			0x07
+#define TI_START_PORT			0x08
+#define TI_STOP_PORT			0x09
+#define TI_TEST_PORT			0x0A
+#define TI_PURGE_PORT			0x0B
+#define TI_RESET_EXT_DEVICE		0x0C
+#define TI_WRITE_DATA			0x80
+#define TI_READ_DATA			0x81
+#define TI_REQ_TYPE_CLASS		0x82
+
+/* Module identifiers */
+#define TI_I2C_PORT			0x01
+#define TI_IEEE1284_PORT		0x02
+#define TI_UART1_PORT			0x03
+#define TI_UART2_PORT			0x04
+#define TI_RAM_PORT			0x05
+
+/* Modem status */
+#define TI_MSR_DELTA_CTS		0x01
+#define TI_MSR_DELTA_DSR		0x02
+#define TI_MSR_DELTA_RI			0x04
+#define TI_MSR_DELTA_CD			0x08
+#define TI_MSR_CTS			0x10
+#define TI_MSR_DSR			0x20
+#define TI_MSR_RI			0x40
+#define TI_MSR_CD			0x80
+#define TI_MSR_DELTA_MASK		0x0F
+#define TI_MSR_MASK			0xF0
+
+/* Line status */
+#define TI_LSR_OVERRUN_ERROR		0x01
+#define TI_LSR_PARITY_ERROR		0x02
+#define TI_LSR_FRAMING_ERROR		0x04
+#define TI_LSR_BREAK			0x08
+#define TI_LSR_ERROR			0x0F
+#define TI_LSR_RX_FULL			0x10
+#define TI_LSR_TX_EMPTY			0x20
+
+/* Line control */
+#define TI_LCR_BREAK			0x40
+
+/* Modem control */
+#define TI_MCR_LOOP			0x04
+#define TI_MCR_DTR			0x10
+#define TI_MCR_RTS			0x20
+
+/* Mask settings */
+#define TI_UART_ENABLE_RTS_IN		0x0001
+#define TI_UART_DISABLE_RTS		0x0002
+#define TI_UART_ENABLE_PARITY_CHECKING	0x0008
+#define TI_UART_ENABLE_DSR_OUT		0x0010
+#define TI_UART_ENABLE_CTS_OUT		0x0020
+#define TI_UART_ENABLE_X_OUT		0x0040
+#define TI_UART_ENABLE_XA_OUT		0x0080
+#define TI_UART_ENABLE_X_IN		0x0100
+#define TI_UART_ENABLE_DTR_IN		0x0800
+#define TI_UART_DISABLE_DTR		0x1000
+#define TI_UART_ENABLE_MS_INTS		0x2000
+#define TI_UART_ENABLE_AUTO_START_DMA	0x4000
+
+/* Parity */
+#define TI_UART_NO_PARITY		0x00
+#define TI_UART_ODD_PARITY		0x01
+#define TI_UART_EVEN_PARITY		0x02
+#define TI_UART_MARK_PARITY		0x03
+#define TI_UART_SPACE_PARITY		0x04
+
+/* Stop bits */
+#define TI_UART_1_STOP_BITS		0x00
+#define TI_UART_1_5_STOP_BITS		0x01
+#define TI_UART_2_STOP_BITS		0x02
+
+/* Bits per character */
+#define TI_UART_5_DATA_BITS		0x00
+#define TI_UART_6_DATA_BITS		0x01
+#define TI_UART_7_DATA_BITS		0x02
+#define TI_UART_8_DATA_BITS		0x03
+
+/* 232/485 modes */
+#define TI_UART_232			0x00
+#define TI_UART_485_RECEIVER_DISABLED	0x01
+#define TI_UART_485_RECEIVER_ENABLED	0x02
+
+/* Pipe transfer mode and timeout */
+#define TI_PIPE_MODE_CONTINUOUS		0x01
+#define TI_PIPE_MODE_MASK		0x03
+#define TI_PIPE_TIMEOUT_MASK		0x7C
+#define TI_PIPE_TIMEOUT_ENABLE		0x80
+
+/* Config struct */
+struct ti_uart_config {
+	__be16	wBaudRate;
+	__be16	wFlags;
+	u8	bDataBits;
+	u8	bParity;
+	u8	bStopBits;
+	char	cXon;
+	char	cXoff;
+	u8	bUartMode;
+} __packed;
+
+/* Get port status */
+struct ti_port_status {
+	u8 bCmdCode;
+	u8 bModuleId;
+	u8 bErrorCode;
+	u8 bMSR;
+	u8 bLSR;
+} __packed;
+
+/* Purge modes */
+#define TI_PURGE_OUTPUT			0x00
+#define TI_PURGE_INPUT			0x80
+
+/* Read/Write data */
+#define TI_RW_DATA_ADDR_SFR		0x10
+#define TI_RW_DATA_ADDR_IDATA		0x20
+#define TI_RW_DATA_ADDR_XDATA		0x30
+#define TI_RW_DATA_ADDR_CODE		0x40
+#define TI_RW_DATA_ADDR_GPIO		0x50
+#define TI_RW_DATA_ADDR_I2C		0x60
+#define TI_RW_DATA_ADDR_FLASH		0x70
+#define TI_RW_DATA_ADDR_DSP		0x80
+
+#define TI_RW_DATA_UNSPECIFIED		0x00
+#define TI_RW_DATA_BYTE			0x01
+#define TI_RW_DATA_WORD			0x02
+#define TI_RW_DATA_DOUBLE_WORD		0x04
+
+struct ti_write_data_bytes {
+	u8	bAddrType;
+	u8	bDataType;
+	u8	bDataCounter;
+	__be16	wBaseAddrHi;
+	__be16	wBaseAddrLo;
+	u8	bData[0];
+} __packed;
+
+struct ti_read_data_request {
+	__u8	bAddrType;
+	__u8	bDataType;
+	__u8	bDataCounter;
+	__be16	wBaseAddrHi;
+	__be16	wBaseAddrLo;
+} __packed;
+
+struct ti_read_data_bytes {
+	__u8	bCmdCode;
+	__u8	bModuleId;
+	__u8	bErrorCode;
+	__u8	bData[0];
+} __packed;
+
+/* Interrupt struct */
+struct ti_interrupt {
+	__u8	bICode;
+	__u8	bIInfo;
+} __packed;
+
+/* Interrupt codes */
+#define TI_CODE_HARDWARE_ERROR		0xFF
+#define TI_CODE_DATA_ERROR		0x03
+#define TI_CODE_MODEM_STATUS		0x04
+
+/* Download firmware max packet size */
+#define TI_DOWNLOAD_MAX_PACKET_SIZE	64
+
+/* Firmware image header */
+struct ti_firmware_header {
+	__le16	wLength;
+	u8	bCheckSum;
+} __packed;
+
+/* UART addresses */
+#define TI_UART1_BASE_ADDR		0xFFA0	/* UART 1 base address */
+#define TI_UART2_BASE_ADDR		0xFFB0	/* UART 2 base address */
+#define TI_UART_OFFSET_LCR		0x0002	/* UART MCR register offset */
+#define TI_UART_OFFSET_MCR		0x0004	/* UART MCR register offset */
+
+#define TI_DRIVER_AUTHOR	"Al Borchers <alborchers@steinerpoint.com>"
+#define TI_DRIVER_DESC		"TI USB 3410/5052 Serial Driver"
+
+#define TI_FIRMWARE_BUF_SIZE	16284
+
+#define TI_TRANSFER_TIMEOUT	2
+
+#define TI_DEFAULT_CLOSING_WAIT	4000		/* in .01 secs */
+
+/* read urb states */
+#define TI_READ_URB_RUNNING	0
+#define TI_READ_URB_STOPPING	1
+#define TI_READ_URB_STOPPED	2
+
+#define TI_EXTRA_VID_PID_COUNT	5
+
+struct ti_port {
+	int			tp_is_open;
+	u8			tp_msr;
+	u8			tp_shadow_mcr;
+	u8			tp_uart_mode;	/* 232 or 485 modes */
+	unsigned int		tp_uart_base_addr;
+	struct ti_device	*tp_tdev;
+	struct usb_serial_port	*tp_port;
+	spinlock_t		tp_lock;
+	int			tp_read_urb_state;
+	int			tp_write_urb_in_use;
+};
+
+struct ti_device {
+	struct mutex		td_open_close_lock;
+	int			td_open_port_count;
+	struct usb_serial	*td_serial;
+	int			td_is_3410;
+	bool			td_rs485_only;
+};
+
+static int ti_startup(struct usb_serial *serial);
+static void ti_release(struct usb_serial *serial);
+static int ti_port_probe(struct usb_serial_port *port);
+static int ti_port_remove(struct usb_serial_port *port);
+static int ti_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void ti_close(struct usb_serial_port *port);
+static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
+		const unsigned char *data, int count);
+static int ti_write_room(struct tty_struct *tty);
+static int ti_chars_in_buffer(struct tty_struct *tty);
+static bool ti_tx_empty(struct usb_serial_port *port);
+static void ti_throttle(struct tty_struct *tty);
+static void ti_unthrottle(struct tty_struct *tty);
+static int ti_ioctl(struct tty_struct *tty,
+		unsigned int cmd, unsigned long arg);
+static void ti_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios);
+static int ti_tiocmget(struct tty_struct *tty);
+static int ti_tiocmset(struct tty_struct *tty,
+		unsigned int set, unsigned int clear);
+static void ti_break(struct tty_struct *tty, int break_state);
+static void ti_interrupt_callback(struct urb *urb);
+static void ti_bulk_in_callback(struct urb *urb);
+static void ti_bulk_out_callback(struct urb *urb);
+
+static void ti_recv(struct usb_serial_port *port, unsigned char *data,
+		int length);
+static void ti_send(struct ti_port *tport);
+static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);
+static int ti_get_lsr(struct ti_port *tport, u8 *lsr);
+static int ti_get_serial_info(struct ti_port *tport,
+	struct serial_struct __user *ret_arg);
+static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
+	struct serial_struct __user *new_arg);
+static void ti_handle_new_msr(struct ti_port *tport, u8 msr);
+
+static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
+static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty);
+
+static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
+	__u16 moduleid, __u16 value, __u8 *data, int size);
+static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
+	__u16 moduleid, __u16 value, __u8 *data, int size);
+
+static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev,
+			 unsigned long addr, u8 mask, u8 byte);
+
+static int ti_download_firmware(struct ti_device *tdev);
+
+static int closing_wait = TI_DEFAULT_CLOSING_WAIT;
+
+static const struct usb_device_id ti_id_table_3410[] = {
+	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_NO_FW_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234MU_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBA_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBAOLD_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
+	{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) },
+	{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
+	{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
+	{ }	/* terminator */
+};
+
+static const struct usb_device_id ti_id_table_5052[] = {
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
+	{ }
+};
+
+static const struct usb_device_id ti_id_table_combined[] = {
+	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_NO_FW_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234MU_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBA_PRODUCT_ID) },
+	{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBAOLD_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
+	{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
+	{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
+	{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
+	{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
+	{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
+	{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
+	{ }	/* terminator */
+};
+
+static struct usb_serial_driver ti_1port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "ti_usb_3410_5052_1",
+	},
+	.description		= "TI USB 3410 1 port adapter",
+	.id_table		= ti_id_table_3410,
+	.num_ports		= 1,
+	.num_bulk_out		= 1,
+	.attach			= ti_startup,
+	.release		= ti_release,
+	.port_probe		= ti_port_probe,
+	.port_remove		= ti_port_remove,
+	.open			= ti_open,
+	.close			= ti_close,
+	.write			= ti_write,
+	.write_room		= ti_write_room,
+	.chars_in_buffer	= ti_chars_in_buffer,
+	.tx_empty		= ti_tx_empty,
+	.throttle		= ti_throttle,
+	.unthrottle		= ti_unthrottle,
+	.ioctl			= ti_ioctl,
+	.set_termios		= ti_set_termios,
+	.tiocmget		= ti_tiocmget,
+	.tiocmset		= ti_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.break_ctl		= ti_break,
+	.read_int_callback	= ti_interrupt_callback,
+	.read_bulk_callback	= ti_bulk_in_callback,
+	.write_bulk_callback	= ti_bulk_out_callback,
+};
+
+static struct usb_serial_driver ti_2port_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "ti_usb_3410_5052_2",
+	},
+	.description		= "TI USB 5052 2 port adapter",
+	.id_table		= ti_id_table_5052,
+	.num_ports		= 2,
+	.num_bulk_out		= 1,
+	.attach			= ti_startup,
+	.release		= ti_release,
+	.port_probe		= ti_port_probe,
+	.port_remove		= ti_port_remove,
+	.open			= ti_open,
+	.close			= ti_close,
+	.write			= ti_write,
+	.write_room		= ti_write_room,
+	.chars_in_buffer	= ti_chars_in_buffer,
+	.tx_empty		= ti_tx_empty,
+	.throttle		= ti_throttle,
+	.unthrottle		= ti_unthrottle,
+	.ioctl			= ti_ioctl,
+	.set_termios		= ti_set_termios,
+	.tiocmget		= ti_tiocmget,
+	.tiocmset		= ti_tiocmset,
+	.tiocmiwait		= usb_serial_generic_tiocmiwait,
+	.get_icount		= usb_serial_generic_get_icount,
+	.break_ctl		= ti_break,
+	.read_int_callback	= ti_interrupt_callback,
+	.read_bulk_callback	= ti_bulk_in_callback,
+	.write_bulk_callback	= ti_bulk_out_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ti_1port_device, &ti_2port_device, NULL
+};
+
+MODULE_AUTHOR(TI_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(TI_DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("ti_3410.fw");
+MODULE_FIRMWARE("ti_5052.fw");
+MODULE_FIRMWARE("mts_cdma.fw");
+MODULE_FIRMWARE("mts_gsm.fw");
+MODULE_FIRMWARE("mts_edge.fw");
+MODULE_FIRMWARE("mts_mt9234mu.fw");
+MODULE_FIRMWARE("mts_mt9234zba.fw");
+MODULE_FIRMWARE("moxa/moxa-1110.fw");
+MODULE_FIRMWARE("moxa/moxa-1130.fw");
+MODULE_FIRMWARE("moxa/moxa-1131.fw");
+MODULE_FIRMWARE("moxa/moxa-1150.fw");
+MODULE_FIRMWARE("moxa/moxa-1151.fw");
+
+module_param(closing_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(closing_wait,
+    "Maximum wait for data to drain in close, in .01 secs, default is 4000");
+
+MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
+
+module_usb_serial_driver(serial_drivers, ti_id_table_combined);
+
+static int ti_startup(struct usb_serial *serial)
+{
+	struct ti_device *tdev;
+	struct usb_device *dev = serial->dev;
+	struct usb_host_interface *cur_altsetting;
+	int num_endpoints;
+	u16 vid, pid;
+	int status;
+
+	dev_dbg(&dev->dev,
+		"%s - product 0x%4X, num configurations %d, configuration value %d\n",
+		__func__, le16_to_cpu(dev->descriptor.idProduct),
+		dev->descriptor.bNumConfigurations,
+		dev->actconfig->desc.bConfigurationValue);
+
+	tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
+	if (!tdev)
+		return -ENOMEM;
+
+	mutex_init(&tdev->td_open_close_lock);
+	tdev->td_serial = serial;
+	usb_set_serial_data(serial, tdev);
+
+	/* determine device type */
+	if (serial->type == &ti_1port_device)
+		tdev->td_is_3410 = 1;
+	dev_dbg(&dev->dev, "%s - device type is %s\n", __func__,
+		tdev->td_is_3410 ? "3410" : "5052");
+
+	vid = le16_to_cpu(dev->descriptor.idVendor);
+	pid = le16_to_cpu(dev->descriptor.idProduct);
+	if (vid == MXU1_VENDOR_ID) {
+		switch (pid) {
+		case MXU1_1130_PRODUCT_ID:
+		case MXU1_1131_PRODUCT_ID:
+			tdev->td_rs485_only = true;
+			break;
+		}
+	}
+
+	cur_altsetting = serial->interface->cur_altsetting;
+	num_endpoints = cur_altsetting->desc.bNumEndpoints;
+
+	/* if we have only 1 configuration and 1 endpoint, download firmware */
+	if (dev->descriptor.bNumConfigurations == 1 && num_endpoints == 1) {
+		status = ti_download_firmware(tdev);
+
+		if (status != 0)
+			goto free_tdev;
+
+		/* 3410 must be reset, 5052 resets itself */
+		if (tdev->td_is_3410) {
+			msleep_interruptible(100);
+			usb_reset_device(dev);
+		}
+
+		status = -ENODEV;
+		goto free_tdev;
+	}
+
+	/* the second configuration must be set */
+	if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) {
+		status = usb_driver_set_configuration(dev, TI_ACTIVE_CONFIG);
+		status = status ? status : -ENODEV;
+		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:
+	kfree(tdev);
+	usb_set_serial_data(serial, NULL);
+	return status;
+}
+
+
+static void ti_release(struct usb_serial *serial)
+{
+	struct ti_device *tdev = usb_get_serial_data(serial);
+
+	kfree(tdev);
+}
+
+static int ti_port_probe(struct usb_serial_port *port)
+{
+	struct ti_port *tport;
+
+	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+	if (!tport)
+		return -ENOMEM;
+
+	spin_lock_init(&tport->tp_lock);
+	if (port == port->serial->port[0])
+		tport->tp_uart_base_addr = TI_UART1_BASE_ADDR;
+	else
+		tport->tp_uart_base_addr = TI_UART2_BASE_ADDR;
+	port->port.closing_wait = msecs_to_jiffies(10 * closing_wait);
+	tport->tp_port = port;
+	tport->tp_tdev = usb_get_serial_data(port->serial);
+
+	if (tport->tp_tdev->td_rs485_only)
+		tport->tp_uart_mode = TI_UART_485_RECEIVER_DISABLED;
+	else
+		tport->tp_uart_mode = TI_UART_232;
+
+	usb_set_serial_port_data(port, tport);
+
+	port->port.drain_delay = 3;
+
+	return 0;
+}
+
+static int ti_port_remove(struct usb_serial_port *port)
+{
+	struct ti_port *tport;
+
+	tport = usb_get_serial_port_data(port);
+	kfree(tport);
+
+	return 0;
+}
+
+static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	struct ti_device *tdev;
+	struct usb_device *dev;
+	struct urb *urb;
+	int port_number;
+	int status;
+	u16 open_settings;
+
+	open_settings = (TI_PIPE_MODE_CONTINUOUS |
+			 TI_PIPE_TIMEOUT_ENABLE |
+			 (TI_TRANSFER_TIMEOUT << 2));
+
+	dev = port->serial->dev;
+	tdev = tport->tp_tdev;
+
+	/* only one open on any port on a device at a time */
+	if (mutex_lock_interruptible(&tdev->td_open_close_lock))
+		return -ERESTARTSYS;
+
+	port_number = port->port_number;
+
+	tport->tp_msr = 0;
+	tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR);
+
+	/* start interrupt urb the first time a port is opened on this device */
+	if (tdev->td_open_port_count == 0) {
+		dev_dbg(&port->dev, "%s - start interrupt in urb\n", __func__);
+		urb = tdev->td_serial->port[0]->interrupt_in_urb;
+		if (!urb) {
+			dev_err(&port->dev, "%s - no interrupt urb\n", __func__);
+			status = -EINVAL;
+			goto release_lock;
+		}
+		urb->context = tdev;
+		status = usb_submit_urb(urb, GFP_KERNEL);
+		if (status) {
+			dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __func__, status);
+			goto release_lock;
+		}
+	}
+
+	if (tty)
+		ti_set_termios(tty, port, &tty->termios);
+
+	status = ti_command_out_sync(tdev, TI_OPEN_PORT,
+		(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send open command, %d\n",
+			__func__, status);
+		goto unlink_int_urb;
+	}
+
+	status = ti_command_out_sync(tdev, TI_START_PORT,
+		(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send start command, %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+
+	status = ti_command_out_sync(tdev, TI_PURGE_PORT,
+		(__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot clear input buffers, %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+	status = ti_command_out_sync(tdev, TI_PURGE_PORT,
+		(__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot clear output buffers, %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+
+	/* reset the data toggle on the bulk endpoints to work around bug in
+	 * host controllers where things get out of sync some times */
+	usb_clear_halt(dev, port->write_urb->pipe);
+	usb_clear_halt(dev, port->read_urb->pipe);
+
+	if (tty)
+		ti_set_termios(tty, port, &tty->termios);
+
+	status = ti_command_out_sync(tdev, TI_OPEN_PORT,
+		(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send open command (2), %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+
+	status = ti_command_out_sync(tdev, TI_START_PORT,
+		(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+	if (status) {
+		dev_err(&port->dev, "%s - cannot send start command (2), %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+
+	/* start read urb */
+	urb = port->read_urb;
+	if (!urb) {
+		dev_err(&port->dev, "%s - no read urb\n", __func__);
+		status = -EINVAL;
+		goto unlink_int_urb;
+	}
+	tport->tp_read_urb_state = TI_READ_URB_RUNNING;
+	urb->context = tport;
+	status = usb_submit_urb(urb, GFP_KERNEL);
+	if (status) {
+		dev_err(&port->dev, "%s - submit read urb failed, %d\n",
+							__func__, status);
+		goto unlink_int_urb;
+	}
+
+	tport->tp_is_open = 1;
+	++tdev->td_open_port_count;
+
+	goto release_lock;
+
+unlink_int_urb:
+	if (tdev->td_open_port_count == 0)
+		usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+release_lock:
+	mutex_unlock(&tdev->td_open_close_lock);
+	return status;
+}
+
+
+static void ti_close(struct usb_serial_port *port)
+{
+	struct ti_device *tdev;
+	struct ti_port *tport;
+	int port_number;
+	int status;
+	int do_unlock;
+	unsigned long flags;
+
+	tdev = usb_get_serial_data(port->serial);
+	tport = usb_get_serial_port_data(port);
+
+	tport->tp_is_open = 0;
+
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->write_urb);
+	tport->tp_write_urb_in_use = 0;
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	kfifo_reset_out(&port->write_fifo);
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	port_number = port->port_number;
+
+	status = ti_command_out_sync(tdev, TI_CLOSE_PORT,
+		     (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+	if (status)
+		dev_err(&port->dev,
+			"%s - cannot send close port command, %d\n"
+							, __func__, status);
+
+	/* if mutex_lock is interrupted, continue anyway */
+	do_unlock = !mutex_lock_interruptible(&tdev->td_open_close_lock);
+	--tport->tp_tdev->td_open_port_count;
+	if (tport->tp_tdev->td_open_port_count <= 0) {
+		/* last port is closed, shut down interrupt urb */
+		usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+		tport->tp_tdev->td_open_port_count = 0;
+	}
+	if (do_unlock)
+		mutex_unlock(&tdev->td_open_close_lock);
+}
+
+
+static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *data, int count)
+{
+	struct ti_port *tport = usb_get_serial_port_data(port);
+
+	if (count == 0) {
+		return 0;
+	}
+
+	if (!tport->tp_is_open)
+		return -ENODEV;
+
+	count = kfifo_in_locked(&port->write_fifo, data, count,
+							&tport->tp_lock);
+	ti_send(tport);
+
+	return count;
+}
+
+
+static int ti_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	int room = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	room = kfifo_avail(&port->write_fifo);
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+	return room;
+}
+
+
+static int ti_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	int chars = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	chars = kfifo_len(&port->write_fifo);
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+	return chars;
+}
+
+static bool ti_tx_empty(struct usb_serial_port *port)
+{
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	int ret;
+	u8 lsr;
+
+	ret = ti_get_lsr(tport, &lsr);
+	if (!ret && !(lsr & TI_LSR_TX_EMPTY))
+		return false;
+
+	return true;
+}
+
+static void ti_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+
+	if (I_IXOFF(tty) || C_CRTSCTS(tty))
+		ti_stop_read(tport, tty);
+
+}
+
+
+static void ti_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	int status;
+
+	if (I_IXOFF(tty) || C_CRTSCTS(tty)) {
+		status = ti_restart_read(tport, tty);
+		if (status)
+			dev_err(&port->dev, "%s - cannot restart read, %d\n",
+							__func__, status);
+	}
+}
+
+static int ti_ioctl(struct tty_struct *tty,
+	unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return ti_get_serial_info(tport,
+				(struct serial_struct __user *)arg);
+	case TIOCSSERIAL:
+		return ti_set_serial_info(tty, tport,
+				(struct serial_struct __user *)arg);
+	}
+	return -ENOIOCTLCMD;
+}
+
+
+static void ti_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	struct ti_uart_config *config;
+	int baud;
+	int status;
+	int port_number = port->port_number;
+	unsigned int mcr;
+	u16 wbaudrate;
+	u16 wflags = 0;
+
+	config = kmalloc(sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return;
+
+	/* these flags must be set */
+	wflags |= TI_UART_ENABLE_MS_INTS;
+	wflags |= TI_UART_ENABLE_AUTO_START_DMA;
+	config->bUartMode = tport->tp_uart_mode;
+
+	switch (C_CSIZE(tty)) {
+	case CS5:
+		    config->bDataBits = TI_UART_5_DATA_BITS;
+		    break;
+	case CS6:
+		    config->bDataBits = TI_UART_6_DATA_BITS;
+		    break;
+	case CS7:
+		    config->bDataBits = TI_UART_7_DATA_BITS;
+		    break;
+	default:
+	case CS8:
+		    config->bDataBits = TI_UART_8_DATA_BITS;
+		    break;
+	}
+
+	/* CMSPAR isn't supported by this driver */
+	tty->termios.c_cflag &= ~CMSPAR;
+
+	if (C_PARENB(tty)) {
+		if (C_PARODD(tty)) {
+			wflags |= TI_UART_ENABLE_PARITY_CHECKING;
+			config->bParity = TI_UART_ODD_PARITY;
+		} else {
+			wflags |= TI_UART_ENABLE_PARITY_CHECKING;
+			config->bParity = TI_UART_EVEN_PARITY;
+		}
+	} else {
+		wflags &= ~TI_UART_ENABLE_PARITY_CHECKING;
+		config->bParity = TI_UART_NO_PARITY;
+	}
+
+	if (C_CSTOPB(tty))
+		config->bStopBits = TI_UART_2_STOP_BITS;
+	else
+		config->bStopBits = TI_UART_1_STOP_BITS;
+
+	if (C_CRTSCTS(tty)) {
+		/* RTS flow control must be off to drop RTS for baud rate B0 */
+		if ((C_BAUD(tty)) != B0)
+			wflags |= TI_UART_ENABLE_RTS_IN;
+		wflags |= TI_UART_ENABLE_CTS_OUT;
+	} else {
+		ti_restart_read(tport, tty);
+	}
+
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		config->cXon  = START_CHAR(tty);
+		config->cXoff = STOP_CHAR(tty);
+
+		if (I_IXOFF(tty))
+			wflags |= TI_UART_ENABLE_X_IN;
+		else
+			ti_restart_read(tport, tty);
+
+		if (I_IXON(tty))
+			wflags |= TI_UART_ENABLE_X_OUT;
+	}
+
+	baud = tty_get_baud_rate(tty);
+	if (!baud)
+		baud = 9600;
+	if (tport->tp_tdev->td_is_3410)
+		wbaudrate = (923077 + baud/2) / baud;
+	else
+		wbaudrate = (461538 + baud/2) / baud;
+
+	/* FIXME: Should calculate resulting baud here and report it back */
+	if ((C_BAUD(tty)) != B0)
+		tty_encode_baud_rate(tty, baud, baud);
+
+	dev_dbg(&port->dev,
+		"%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n",
+		__func__, baud, wbaudrate, wflags,
+		config->bDataBits, config->bParity, config->bStopBits,
+		config->cXon, config->cXoff, config->bUartMode);
+
+	config->wBaudRate = cpu_to_be16(wbaudrate);
+	config->wFlags = cpu_to_be16(wflags);
+
+	status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG,
+		(__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config,
+		sizeof(*config));
+	if (status)
+		dev_err(&port->dev, "%s - cannot set config on port %d, %d\n",
+					__func__, port_number, status);
+
+	/* SET_CONFIG asserts RTS and DTR, reset them correctly */
+	mcr = tport->tp_shadow_mcr;
+	/* if baud rate is B0, clear RTS and DTR */
+	if (C_BAUD(tty) == B0)
+		mcr &= ~(TI_MCR_DTR | TI_MCR_RTS);
+	status = ti_set_mcr(tport, mcr);
+	if (status)
+		dev_err(&port->dev,
+			"%s - cannot set modem control on port %d, %d\n",
+						__func__, port_number, status);
+
+	kfree(config);
+}
+
+
+static int ti_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	unsigned int result;
+	unsigned int msr;
+	unsigned int mcr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	msr = tport->tp_msr;
+	mcr = tport->tp_shadow_mcr;
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	result = ((mcr & TI_MCR_DTR) ? TIOCM_DTR : 0)
+		| ((mcr & TI_MCR_RTS) ? TIOCM_RTS : 0)
+		| ((mcr & TI_MCR_LOOP) ? TIOCM_LOOP : 0)
+		| ((msr & TI_MSR_CTS) ? TIOCM_CTS : 0)
+		| ((msr & TI_MSR_CD) ? TIOCM_CAR : 0)
+		| ((msr & TI_MSR_RI) ? TIOCM_RI : 0)
+		| ((msr & TI_MSR_DSR) ? TIOCM_DSR : 0);
+
+	dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
+
+	return result;
+}
+
+
+static int ti_tiocmset(struct tty_struct *tty,
+				unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	unsigned int mcr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	mcr = tport->tp_shadow_mcr;
+
+	if (set & TIOCM_RTS)
+		mcr |= TI_MCR_RTS;
+	if (set & TIOCM_DTR)
+		mcr |= TI_MCR_DTR;
+	if (set & TIOCM_LOOP)
+		mcr |= TI_MCR_LOOP;
+
+	if (clear & TIOCM_RTS)
+		mcr &= ~TI_MCR_RTS;
+	if (clear & TIOCM_DTR)
+		mcr &= ~TI_MCR_DTR;
+	if (clear & TIOCM_LOOP)
+		mcr &= ~TI_MCR_LOOP;
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	return ti_set_mcr(tport, mcr);
+}
+
+
+static void ti_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	int status;
+
+	dev_dbg(&port->dev, "%s - state = %d\n", __func__, break_state);
+
+	status = ti_write_byte(port, tport->tp_tdev,
+		tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
+		TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
+
+	if (status)
+		dev_dbg(&port->dev, "%s - error setting break, %d\n", __func__, status);
+}
+
+static int ti_get_port_from_code(unsigned char code)
+{
+	return (code >> 6) & 0x01;
+}
+
+static int ti_get_func_from_code(unsigned char code)
+{
+	return code & 0x0f;
+}
+
+static void ti_interrupt_callback(struct urb *urb)
+{
+	struct ti_device *tdev = urb->context;
+	struct usb_serial_port *port;
+	struct usb_serial *serial = tdev->td_serial;
+	struct ti_port *tport;
+	struct device *dev = &urb->dev->dev;
+	unsigned char *data = urb->transfer_buffer;
+	int length = urb->actual_length;
+	int port_number;
+	int function;
+	int status = urb->status;
+	int retval;
+	u8 msr;
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
+		return;
+	default:
+		dev_err(dev, "%s - nonzero urb status, %d\n", __func__, status);
+		goto exit;
+	}
+
+	if (length != 2) {
+		dev_dbg(dev, "%s - bad packet size, %d\n", __func__, length);
+		goto exit;
+	}
+
+	if (data[0] == TI_CODE_HARDWARE_ERROR) {
+		dev_err(dev, "%s - hardware error, %d\n", __func__, data[1]);
+		goto exit;
+	}
+
+	port_number = ti_get_port_from_code(data[0]);
+	function = ti_get_func_from_code(data[0]);
+
+	dev_dbg(dev, "%s - port_number %d, function %d, data 0x%02X\n",
+		__func__, port_number, function, data[1]);
+
+	if (port_number >= serial->num_ports) {
+		dev_err(dev, "%s - bad port number, %d\n",
+						__func__, port_number);
+		goto exit;
+	}
+
+	port = serial->port[port_number];
+
+	tport = usb_get_serial_port_data(port);
+	if (!tport)
+		goto exit;
+
+	switch (function) {
+	case TI_CODE_DATA_ERROR:
+		dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n",
+			__func__, port_number, data[1]);
+		break;
+
+	case TI_CODE_MODEM_STATUS:
+		msr = data[1];
+		dev_dbg(dev, "%s - port %d, msr 0x%02X\n", __func__, port_number, msr);
+		ti_handle_new_msr(tport, msr);
+		break;
+
+	default:
+		dev_err(dev, "%s - unknown interrupt code, 0x%02X\n",
+							__func__, data[1]);
+		break;
+	}
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - resubmit interrupt urb failed, %d\n",
+			__func__, retval);
+}
+
+
+static void ti_bulk_in_callback(struct urb *urb)
+{
+	struct ti_port *tport = urb->context;
+	struct usb_serial_port *port = tport->tp_port;
+	struct device *dev = &urb->dev->dev;
+	int status = urb->status;
+	unsigned long flags;
+	int retval = 0;
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
+		return;
+	default:
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__func__, status);
+	}
+
+	if (status == -EPIPE)
+		goto exit;
+
+	if (status) {
+		dev_err(dev, "%s - stopping read!\n", __func__);
+		return;
+	}
+
+	if (urb->actual_length) {
+		usb_serial_debug_data(dev, __func__, urb->actual_length,
+				      urb->transfer_buffer);
+
+		if (!tport->tp_is_open)
+			dev_dbg(dev, "%s - port closed, dropping data\n",
+				__func__);
+		else
+			ti_recv(port, urb->transfer_buffer, urb->actual_length);
+		spin_lock_irqsave(&tport->tp_lock, flags);
+		port->icount.rx += urb->actual_length;
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+	}
+
+exit:
+	/* continue to read unless stopping */
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
+	else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING)
+		tport->tp_read_urb_state = TI_READ_URB_STOPPED;
+
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+	if (retval)
+		dev_err(dev, "%s - resubmit read urb failed, %d\n",
+			__func__, retval);
+}
+
+
+static void ti_bulk_out_callback(struct urb *urb)
+{
+	struct ti_port *tport = urb->context;
+	struct usb_serial_port *port = tport->tp_port;
+	int status = urb->status;
+
+	tport->tp_write_urb_in_use = 0;
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&port->dev, "%s - urb shutting down, %d\n", __func__, status);
+		return;
+	default:
+		dev_err_console(port, "%s - nonzero urb status, %d\n",
+			__func__, status);
+	}
+
+	/* send any buffered data */
+	ti_send(tport);
+}
+
+
+static void ti_recv(struct usb_serial_port *port, unsigned char *data,
+		int length)
+{
+	int cnt;
+
+	do {
+		cnt = tty_insert_flip_string(&port->port, data, length);
+		if (cnt < length) {
+			dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
+						__func__, length - cnt);
+			if (cnt == 0)
+				break;
+		}
+		tty_flip_buffer_push(&port->port);
+		data += cnt;
+		length -= cnt;
+	} while (length > 0);
+}
+
+
+static void ti_send(struct ti_port *tport)
+{
+	int count, result;
+	struct usb_serial_port *port = tport->tp_port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+
+	if (tport->tp_write_urb_in_use)
+		goto unlock;
+
+	count = kfifo_out(&port->write_fifo,
+				port->write_urb->transfer_buffer,
+				port->bulk_out_size);
+
+	if (count == 0)
+		goto unlock;
+
+	tport->tp_write_urb_in_use = 1;
+
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	usb_serial_debug_data(&port->dev, __func__, count,
+			      port->write_urb->transfer_buffer);
+
+	usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+			   usb_sndbulkpipe(port->serial->dev,
+					    port->bulk_out_endpointAddress),
+			   port->write_urb->transfer_buffer, count,
+			   ti_bulk_out_callback, tport);
+
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	if (result) {
+		dev_err_console(port, "%s - submit write urb failed, %d\n",
+							__func__, result);
+		tport->tp_write_urb_in_use = 0;
+		/* TODO: reschedule ti_send */
+	} else {
+		spin_lock_irqsave(&tport->tp_lock, flags);
+		port->icount.tx += count;
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+	}
+
+	/* more room in the buffer for new writes, wakeup */
+	tty_port_tty_wakeup(&port->port);
+
+	return;
+unlock:
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+	return;
+}
+
+
+static int ti_set_mcr(struct ti_port *tport, unsigned int mcr)
+{
+	unsigned long flags;
+	int status;
+
+	status = ti_write_byte(tport->tp_port, tport->tp_tdev,
+		tport->tp_uart_base_addr + TI_UART_OFFSET_MCR,
+		TI_MCR_RTS | TI_MCR_DTR | TI_MCR_LOOP, mcr);
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	if (!status)
+		tport->tp_shadow_mcr = mcr;
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	return status;
+}
+
+
+static int ti_get_lsr(struct ti_port *tport, u8 *lsr)
+{
+	int size, status;
+	struct ti_device *tdev = tport->tp_tdev;
+	struct usb_serial_port *port = tport->tp_port;
+	int port_number = port->port_number;
+	struct ti_port_status *data;
+
+	size = sizeof(struct ti_port_status);
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS,
+		(__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size);
+	if (status) {
+		dev_err(&port->dev,
+			"%s - get port status command failed, %d\n",
+							__func__, status);
+		goto free_data;
+	}
+
+	dev_dbg(&port->dev, "%s - lsr 0x%02X\n", __func__, data->bLSR);
+
+	*lsr = data->bLSR;
+
+free_data:
+	kfree(data);
+	return status;
+}
+
+
+static int ti_get_serial_info(struct ti_port *tport,
+	struct serial_struct __user *ret_arg)
+{
+	struct usb_serial_port *port = tport->tp_port;
+	struct serial_struct ret_serial;
+	unsigned cwait;
+
+	cwait = port->port.closing_wait;
+	if (cwait != ASYNC_CLOSING_WAIT_NONE)
+		cwait = jiffies_to_msecs(cwait) / 10;
+
+	memset(&ret_serial, 0, sizeof(ret_serial));
+
+	ret_serial.type = PORT_16550A;
+	ret_serial.line = port->minor;
+	ret_serial.port = port->port_number;
+	ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo);
+	ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
+	ret_serial.closing_wait = cwait;
+
+	if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
+		return -EFAULT;
+
+	return 0;
+}
+
+
+static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
+	struct serial_struct __user *new_arg)
+{
+	struct serial_struct new_serial;
+	unsigned cwait;
+
+	if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
+		return -EFAULT;
+
+	cwait = new_serial.closing_wait;
+	if (cwait != ASYNC_CLOSING_WAIT_NONE)
+		cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
+
+	tport->tp_port->port.closing_wait = cwait;
+
+	return 0;
+}
+
+
+static void ti_handle_new_msr(struct ti_port *tport, u8 msr)
+{
+	struct async_icount *icount;
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	dev_dbg(&tport->tp_port->dev, "%s - msr 0x%02X\n", __func__, msr);
+
+	if (msr & TI_MSR_DELTA_MASK) {
+		spin_lock_irqsave(&tport->tp_lock, flags);
+		icount = &tport->tp_port->icount;
+		if (msr & TI_MSR_DELTA_CTS)
+			icount->cts++;
+		if (msr & TI_MSR_DELTA_DSR)
+			icount->dsr++;
+		if (msr & TI_MSR_DELTA_CD)
+			icount->dcd++;
+		if (msr & TI_MSR_DELTA_RI)
+			icount->rng++;
+		wake_up_interruptible(&tport->tp_port->port.delta_msr_wait);
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+	}
+
+	tport->tp_msr = msr & TI_MSR_MASK;
+
+	/* handle CTS flow control */
+	tty = tty_port_tty_get(&tport->tp_port->port);
+	if (tty && C_CRTSCTS(tty)) {
+		if (msr & TI_MSR_CTS)
+			tty_wakeup(tty);
+	}
+	tty_kref_put(tty);
+}
+
+
+static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+
+	if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
+		tport->tp_read_urb_state = TI_READ_URB_STOPPING;
+
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+}
+
+
+static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty)
+{
+	struct urb *urb;
+	int status = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+
+	if (tport->tp_read_urb_state == TI_READ_URB_STOPPED) {
+		tport->tp_read_urb_state = TI_READ_URB_RUNNING;
+		urb = tport->tp_port->read_urb;
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+		urb->context = tport;
+		status = usb_submit_urb(urb, GFP_KERNEL);
+	} else  {
+		tport->tp_read_urb_state = TI_READ_URB_RUNNING;
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+	}
+
+	return status;
+}
+
+
+static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
+	__u16 moduleid, __u16 value, __u8 *data, int size)
+{
+	int status;
+
+	status = usb_control_msg(tdev->td_serial->dev,
+		usb_sndctrlpipe(tdev->td_serial->dev, 0), command,
+		(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
+		value, moduleid, data, size, 1000);
+
+	if (status < 0)
+		return status;
+
+	return 0;
+}
+
+
+static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
+	__u16 moduleid, __u16 value, __u8 *data, int size)
+{
+	int status;
+
+	status = usb_control_msg(tdev->td_serial->dev,
+		usb_rcvctrlpipe(tdev->td_serial->dev, 0), command,
+		(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN),
+		value, moduleid, data, size, 1000);
+
+	if (status == size)
+		status = 0;
+	else if (status >= 0)
+		status = -ECOMM;
+
+	return status;
+}
+
+
+static int ti_write_byte(struct usb_serial_port *port,
+			 struct ti_device *tdev, unsigned long addr,
+			 u8 mask, u8 byte)
+{
+	int status;
+	unsigned int size;
+	struct ti_write_data_bytes *data;
+
+	dev_dbg(&port->dev, "%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X\n", __func__,
+		addr, mask, byte);
+
+	size = sizeof(struct ti_write_data_bytes) + 2;
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->bAddrType = TI_RW_DATA_ADDR_XDATA;
+	data->bDataType = TI_RW_DATA_BYTE;
+	data->bDataCounter = 1;
+	data->wBaseAddrHi = cpu_to_be16(addr>>16);
+	data->wBaseAddrLo = cpu_to_be16(addr);
+	data->bData[0] = mask;
+	data->bData[1] = byte;
+
+	status = ti_command_out_sync(tdev, TI_WRITE_DATA, TI_RAM_PORT, 0,
+		(__u8 *)data, size);
+
+	if (status < 0)
+		dev_err(&port->dev, "%s - failed, %d\n", __func__, status);
+
+	kfree(data);
+
+	return status;
+}
+
+static int ti_do_download(struct usb_device *dev, int pipe,
+						u8 *buffer, int size)
+{
+	int pos;
+	u8 cs = 0;
+	int done;
+	struct ti_firmware_header *header;
+	int status = 0;
+	int len;
+
+	for (pos = sizeof(struct ti_firmware_header); pos < size; pos++)
+		cs = (u8)(cs + buffer[pos]);
+
+	header = (struct ti_firmware_header *)buffer;
+	header->wLength = cpu_to_le16(size - sizeof(*header));
+	header->bCheckSum = cs;
+
+	dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__);
+	for (pos = 0; pos < size; pos += done) {
+		len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
+		status = usb_bulk_msg(dev, pipe, buffer + pos, len,
+								&done, 1000);
+		if (status)
+			break;
+	}
+	return status;
+}
+
+static int ti_download_firmware(struct ti_device *tdev)
+{
+	int status;
+	int buffer_size;
+	u8 *buffer;
+	struct usb_device *dev = tdev->td_serial->dev;
+	unsigned int pipe = usb_sndbulkpipe(dev,
+		tdev->td_serial->port[0]->bulk_out_endpointAddress);
+	const struct firmware *fw_p;
+	char buf[32];
+
+	if (le16_to_cpu(dev->descriptor.idVendor) == MXU1_VENDOR_ID) {
+		snprintf(buf,
+			sizeof(buf),
+			"moxa/moxa-%04x.fw",
+			le16_to_cpu(dev->descriptor.idProduct));
+
+		status = request_firmware(&fw_p, buf, &dev->dev);
+		goto check_firmware;
+	}
+
+	/* try ID specific firmware first, then try generic firmware */
+	sprintf(buf, "ti_usb-v%04x-p%04x.fw",
+			le16_to_cpu(dev->descriptor.idVendor),
+			le16_to_cpu(dev->descriptor.idProduct));
+	status = request_firmware(&fw_p, buf, &dev->dev);
+
+	if (status != 0) {
+		buf[0] = '\0';
+		if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) {
+			switch (le16_to_cpu(dev->descriptor.idProduct)) {
+			case MTS_CDMA_PRODUCT_ID:
+				strcpy(buf, "mts_cdma.fw");
+				break;
+			case MTS_GSM_PRODUCT_ID:
+				strcpy(buf, "mts_gsm.fw");
+				break;
+			case MTS_EDGE_PRODUCT_ID:
+				strcpy(buf, "mts_edge.fw");
+				break;
+			case MTS_MT9234MU_PRODUCT_ID:
+				strcpy(buf, "mts_mt9234mu.fw");
+				break;
+			case MTS_MT9234ZBA_PRODUCT_ID:
+				strcpy(buf, "mts_mt9234zba.fw");
+				break;
+			case MTS_MT9234ZBAOLD_PRODUCT_ID:
+				strcpy(buf, "mts_mt9234zba.fw");
+				break;			}
+		}
+		if (buf[0] == '\0') {
+			if (tdev->td_is_3410)
+				strcpy(buf, "ti_3410.fw");
+			else
+				strcpy(buf, "ti_5052.fw");
+		}
+		status = request_firmware(&fw_p, buf, &dev->dev);
+	}
+
+check_firmware:
+	if (status) {
+		dev_err(&dev->dev, "%s - firmware not found\n", __func__);
+		return -ENOENT;
+	}
+	if (fw_p->size > TI_FIRMWARE_BUF_SIZE) {
+		dev_err(&dev->dev, "%s - firmware too large %zu\n", __func__, fw_p->size);
+		release_firmware(fw_p);
+		return -ENOENT;
+	}
+
+	buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (buffer) {
+		memcpy(buffer, fw_p->data, fw_p->size);
+		memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size);
+		status = ti_do_download(dev, pipe, buffer, fw_p->size);
+		kfree(buffer);
+	} else {
+		status = -ENOMEM;
+	}
+	release_firmware(fw_p);
+	if (status) {
+		dev_err(&dev->dev, "%s - error downloading firmware, %d\n",
+							__func__, status);
+		return status;
+	}
+
+	dev_dbg(&dev->dev, "%s - download successful\n", __func__);
+
+	return 0;
+}
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
new file mode 100644
index 0000000..1ba1401
--- /dev/null
+++ b/drivers/usb/serial/upd78f0730.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas Electronics uPD78F0730 USB to serial converter driver
+ *
+ * Copyright (C) 2014,2016 Maksim Salau <maksim.salau@gmail.com>
+ *
+ * Protocol of the adaptor is described in the application note U19660EJ1V0AN00
+ * μPD78F0730 8-bit Single-Chip Microcontroller
+ * USB-to-Serial Conversion Software
+ * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf>
+ *
+ * The adaptor functionality is limited to the following:
+ * - data bits: 7 or 8
+ * - stop bits: 1 or 2
+ * - parity: even, odd or none
+ * - flow control: none
+ * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
+ * - signals: DTR, RTS and BREAK
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver"
+
+#define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>"
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */
+	{ USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */
+	{ USB_DEVICE(0x064B, 0x7825) }, /* Analog Devices EVAL-ADXL362Z-DB */
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/*
+ * Each adaptor is associated with a private structure, that holds the current
+ * state of control signals (DTR, RTS and BREAK).
+ */
+struct upd78f0730_port_private {
+	struct mutex	lock;		/* mutex to protect line_signals */
+	u8		line_signals;
+};
+
+/* Op-codes of control commands */
+#define UPD78F0730_CMD_LINE_CONTROL	0x00
+#define UPD78F0730_CMD_SET_DTR_RTS	0x01
+#define UPD78F0730_CMD_SET_XON_XOFF_CHR	0x02
+#define UPD78F0730_CMD_OPEN_CLOSE	0x03
+#define UPD78F0730_CMD_SET_ERR_CHR	0x04
+
+/* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_DATA_SIZE_7_BITS	0x00
+#define UPD78F0730_DATA_SIZE_8_BITS	0x01
+#define UPD78F0730_DATA_SIZE_MASK	0x01
+
+/* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_STOP_BIT_1_BIT	0x00
+#define UPD78F0730_STOP_BIT_2_BIT	0x02
+#define UPD78F0730_STOP_BIT_MASK	0x02
+
+/* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_PARITY_NONE	0x00
+#define UPD78F0730_PARITY_EVEN	0x04
+#define UPD78F0730_PARITY_ODD	0x08
+#define UPD78F0730_PARITY_MASK	0x0C
+
+/* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_FLOW_CONTROL_NONE	0x00
+#define UPD78F0730_FLOW_CONTROL_HW	0x10
+#define UPD78F0730_FLOW_CONTROL_SW	0x20
+#define UPD78F0730_FLOW_CONTROL_MASK	0x30
+
+/* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */
+#define UPD78F0730_RTS		0x01
+#define UPD78F0730_DTR		0x02
+#define UPD78F0730_BREAK	0x04
+
+/* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */
+#define UPD78F0730_PORT_CLOSE	0x00
+#define UPD78F0730_PORT_OPEN	0x01
+
+/* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */
+#define UPD78F0730_ERR_CHR_DISABLED	0x00
+#define UPD78F0730_ERR_CHR_ENABLED	0x01
+
+/*
+ * Declaration of command structures
+ */
+
+/* UPD78F0730_CMD_LINE_CONTROL command */
+struct upd78f0730_line_control {
+	u8	opcode;
+	__le32	baud_rate;
+	u8	params;
+} __packed;
+
+/* UPD78F0730_CMD_SET_DTR_RTS command */
+struct upd78f0730_set_dtr_rts {
+	u8 opcode;
+	u8 params;
+};
+
+/* UPD78F0730_CMD_SET_XON_OFF_CHR command */
+struct upd78f0730_set_xon_xoff_chr {
+	u8 opcode;
+	u8 xon;
+	u8 xoff;
+};
+
+/* UPD78F0730_CMD_OPEN_CLOSE command */
+struct upd78f0730_open_close {
+	u8 opcode;
+	u8 state;
+};
+
+/* UPD78F0730_CMD_SET_ERR_CHR command */
+struct upd78f0730_set_err_chr {
+	u8 opcode;
+	u8 state;
+	u8 err_char;
+};
+
+static int upd78f0730_send_ctl(struct usb_serial_port *port,
+			const void *data, int size)
+{
+	struct usb_device *usbdev = port->serial->dev;
+	void *buf;
+	int res;
+
+	if (size <= 0 || !data)
+		return -EINVAL;
+
+	buf = kmemdup(data, size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00,
+			USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+			0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT);
+
+	kfree(buf);
+
+	if (res != size) {
+		struct device *dev = &port->dev;
+
+		dev_err(dev, "failed to send control request %02x: %d\n",
+			*(u8 *)data, res);
+		/* The maximum expected length of a transfer is 6 bytes */
+		if (res >= 0)
+			res = -EIO;
+
+		return res;
+	}
+
+	return 0;
+}
+
+static int upd78f0730_port_probe(struct usb_serial_port *port)
+{
+	struct upd78f0730_port_private *private;
+
+	private = kzalloc(sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	mutex_init(&private->lock);
+	usb_set_serial_port_data(port, private);
+
+	return 0;
+}
+
+static int upd78f0730_port_remove(struct usb_serial_port *port)
+{
+	struct upd78f0730_port_private *private;
+
+	private = usb_get_serial_port_data(port);
+	mutex_destroy(&private->lock);
+	kfree(private);
+
+	return 0;
+}
+
+static int upd78f0730_tiocmget(struct tty_struct *tty)
+{
+	struct device *dev = tty->dev;
+	struct upd78f0730_port_private *private;
+	struct usb_serial_port *port = tty->driver_data;
+	int signals;
+	int res;
+
+	private = usb_get_serial_port_data(port);
+
+	mutex_lock(&private->lock);
+	signals = private->line_signals;
+	mutex_unlock(&private->lock);
+
+	res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) |
+		((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0);
+
+	dev_dbg(dev, "%s - res = %x\n", __func__, res);
+
+	return res;
+}
+
+static int upd78f0730_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct device *dev = tty->dev;
+	struct usb_serial_port *port = tty->driver_data;
+	struct upd78f0730_port_private *private;
+	struct upd78f0730_set_dtr_rts request;
+	int res;
+
+	private = usb_get_serial_port_data(port);
+
+	mutex_lock(&private->lock);
+	if (set & TIOCM_DTR) {
+		private->line_signals |= UPD78F0730_DTR;
+		dev_dbg(dev, "%s - set DTR\n", __func__);
+	}
+	if (set & TIOCM_RTS) {
+		private->line_signals |= UPD78F0730_RTS;
+		dev_dbg(dev, "%s - set RTS\n", __func__);
+	}
+	if (clear & TIOCM_DTR) {
+		private->line_signals &= ~UPD78F0730_DTR;
+		dev_dbg(dev, "%s - clear DTR\n", __func__);
+	}
+	if (clear & TIOCM_RTS) {
+		private->line_signals &= ~UPD78F0730_RTS;
+		dev_dbg(dev, "%s - clear RTS\n", __func__);
+	}
+	request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
+	request.params = private->line_signals;
+
+	res = upd78f0730_send_ctl(port, &request, sizeof(request));
+	mutex_unlock(&private->lock);
+
+	return res;
+}
+
+static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct device *dev = tty->dev;
+	struct upd78f0730_port_private *private;
+	struct usb_serial_port *port = tty->driver_data;
+	struct upd78f0730_set_dtr_rts request;
+
+	private = usb_get_serial_port_data(port);
+
+	mutex_lock(&private->lock);
+	if (break_state) {
+		private->line_signals |= UPD78F0730_BREAK;
+		dev_dbg(dev, "%s - set BREAK\n", __func__);
+	} else {
+		private->line_signals &= ~UPD78F0730_BREAK;
+		dev_dbg(dev, "%s - clear BREAK\n", __func__);
+	}
+	request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
+	request.params = private->line_signals;
+
+	upd78f0730_send_ctl(port, &request, sizeof(request));
+	mutex_unlock(&private->lock);
+}
+
+static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct tty_struct *tty = port->port.tty;
+	unsigned int set = 0;
+	unsigned int clear = 0;
+
+	if (on)
+		set = TIOCM_DTR | TIOCM_RTS;
+	else
+		clear = TIOCM_DTR | TIOCM_RTS;
+
+	upd78f0730_tiocmset(tty, set, clear);
+}
+
+static speed_t upd78f0730_get_baud_rate(struct tty_struct *tty)
+{
+	const speed_t baud_rate = tty_get_baud_rate(tty);
+	static const speed_t supported[] = {
+		0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
+	};
+	int i;
+
+	for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) {
+		if (baud_rate == supported[i])
+			return baud_rate;
+	}
+
+	/* If the baud rate is not supported, switch to the default one */
+	tty_encode_baud_rate(tty, 9600, 9600);
+
+	return tty_get_baud_rate(tty);
+}
+
+static void upd78f0730_set_termios(struct tty_struct *tty,
+				struct usb_serial_port *port,
+				struct ktermios *old_termios)
+{
+	struct device *dev = &port->dev;
+	struct upd78f0730_line_control request;
+	speed_t baud_rate;
+
+	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+		return;
+
+	if (C_BAUD(tty) == B0)
+		upd78f0730_dtr_rts(port, 0);
+	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+		upd78f0730_dtr_rts(port, 1);
+
+	baud_rate = upd78f0730_get_baud_rate(tty);
+	request.opcode = UPD78F0730_CMD_LINE_CONTROL;
+	request.baud_rate = cpu_to_le32(baud_rate);
+	request.params = 0;
+	dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate);
+
+	switch (C_CSIZE(tty)) {
+	case CS7:
+		request.params |= UPD78F0730_DATA_SIZE_7_BITS;
+		dev_dbg(dev, "%s - 7 data bits\n", __func__);
+		break;
+	default:
+		tty->termios.c_cflag &= ~CSIZE;
+		tty->termios.c_cflag |= CS8;
+		dev_warn(dev, "data size is not supported, using 8 bits\n");
+		/* fall through */
+	case CS8:
+		request.params |= UPD78F0730_DATA_SIZE_8_BITS;
+		dev_dbg(dev, "%s - 8 data bits\n", __func__);
+		break;
+	}
+
+	if (C_PARENB(tty)) {
+		if (C_PARODD(tty)) {
+			request.params |= UPD78F0730_PARITY_ODD;
+			dev_dbg(dev, "%s - odd parity\n", __func__);
+		} else {
+			request.params |= UPD78F0730_PARITY_EVEN;
+			dev_dbg(dev, "%s - even parity\n", __func__);
+		}
+
+		if (C_CMSPAR(tty)) {
+			tty->termios.c_cflag &= ~CMSPAR;
+			dev_warn(dev, "MARK/SPACE parity is not supported\n");
+		}
+	} else {
+		request.params |= UPD78F0730_PARITY_NONE;
+		dev_dbg(dev, "%s - no parity\n", __func__);
+	}
+
+	if (C_CSTOPB(tty)) {
+		request.params |= UPD78F0730_STOP_BIT_2_BIT;
+		dev_dbg(dev, "%s - 2 stop bits\n", __func__);
+	} else {
+		request.params |= UPD78F0730_STOP_BIT_1_BIT;
+		dev_dbg(dev, "%s - 1 stop bit\n", __func__);
+	}
+
+	if (C_CRTSCTS(tty)) {
+		tty->termios.c_cflag &= ~CRTSCTS;
+		dev_warn(dev, "RTSCTS flow control is not supported\n");
+	}
+	if (I_IXOFF(tty) || I_IXON(tty)) {
+		tty->termios.c_iflag &= ~(IXOFF | IXON);
+		dev_warn(dev, "XON/XOFF flow control is not supported\n");
+	}
+	request.params |= UPD78F0730_FLOW_CONTROL_NONE;
+	dev_dbg(dev, "%s - no flow control\n", __func__);
+
+	upd78f0730_send_ctl(port, &request, sizeof(request));
+}
+
+static int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	static const struct upd78f0730_open_close request = {
+		.opcode = UPD78F0730_CMD_OPEN_CLOSE,
+		.state = UPD78F0730_PORT_OPEN
+	};
+	int res;
+
+	res = upd78f0730_send_ctl(port, &request, sizeof(request));
+	if (res)
+		return res;
+
+	if (tty)
+		upd78f0730_set_termios(tty, port, NULL);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static void upd78f0730_close(struct usb_serial_port *port)
+{
+	static const struct upd78f0730_open_close request = {
+		.opcode = UPD78F0730_CMD_OPEN_CLOSE,
+		.state = UPD78F0730_PORT_CLOSE
+	};
+
+	usb_serial_generic_close(port);
+	upd78f0730_send_ctl(port, &request, sizeof(request));
+}
+
+static struct usb_serial_driver upd78f0730_device = {
+	.driver	 = {
+		.owner	= THIS_MODULE,
+		.name	= "upd78f0730",
+	},
+	.id_table	= id_table,
+	.num_ports	= 1,
+	.port_probe	= upd78f0730_port_probe,
+	.port_remove	= upd78f0730_port_remove,
+	.open		= upd78f0730_open,
+	.close		= upd78f0730_close,
+	.set_termios	= upd78f0730_set_termios,
+	.tiocmget	= upd78f0730_tiocmget,
+	.tiocmset	= upd78f0730_tiocmset,
+	.dtr_rts	= upd78f0730_dtr_rts,
+	.break_ctl	= upd78f0730_break_ctl,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&upd78f0730_device,
+	NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
new file mode 100644
index 0000000..4d02735
--- /dev/null
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Serial "Simple" driver
+ *
+ * Copyright (C) 2001-2006,2008,2013 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net)
+ * Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de>
+ * Copyright (C) 2009 Outpost Embedded, LLC
+ * Copyright (C) 2010 Zilogic Systems <code@zilogic.com>
+ * Copyright (C) 2013 Wei Shuai <cpuwolf@gmail.com>
+ * Copyright (C) 2013 Linux Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DEVICE_N(vendor, IDS, nport)				\
+static const struct usb_device_id vendor##_id_table[] = {	\
+	IDS(),							\
+	{ },							\
+};								\
+static struct usb_serial_driver vendor##_device = {		\
+	.driver = {						\
+		.owner =	THIS_MODULE,			\
+		.name =		#vendor,			\
+	},							\
+	.id_table =		vendor##_id_table,		\
+	.num_ports =		nport,				\
+};
+
+#define DEVICE(vendor, IDS)	DEVICE_N(vendor, IDS, 1)
+
+/* Medtronic CareLink USB driver */
+#define CARELINK_IDS()			\
+	{ USB_DEVICE(0x0a21, 0x8001) }	/* MMT-7305WW */
+DEVICE(carelink, CARELINK_IDS);
+
+/* ZIO Motherboard USB driver */
+#define ZIO_IDS()			\
+	{ USB_DEVICE(0x1CBE, 0x0103) }
+DEVICE(zio, ZIO_IDS);
+
+/* Funsoft Serial USB driver */
+#define FUNSOFT_IDS()			\
+	{ USB_DEVICE(0x1404, 0xcddc) }
+DEVICE(funsoft, FUNSOFT_IDS);
+
+/* Infineon Flashloader driver */
+#define FLASHLOADER_IDS()		\
+	{ USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \
+	{ USB_DEVICE(0x8087, 0x0716) }, \
+	{ USB_DEVICE(0x8087, 0x0801) }
+DEVICE(flashloader, FLASHLOADER_IDS);
+
+/* Google Serial USB SubClass */
+#define GOOGLE_IDS()						\
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x18d1,			\
+					USB_CLASS_VENDOR_SPEC,	\
+					0x50,			\
+					0x01) }
+DEVICE(google, GOOGLE_IDS);
+
+/* Libtransistor USB console */
+#define LIBTRANSISTOR_IDS()			\
+	{ USB_DEVICE(0x1209, 0x8b00) }
+DEVICE(libtransistor, LIBTRANSISTOR_IDS);
+
+/* ViVOpay USB Serial Driver */
+#define VIVOPAY_IDS()			\
+	{ USB_DEVICE(0x1d5f, 0x1004) }	/* ViVOpay 8800 */
+DEVICE(vivopay, VIVOPAY_IDS);
+
+/* Motorola USB Phone driver */
+#define MOTO_IDS()			\
+	{ USB_DEVICE(0x05c6, 0x3197) },	/* unknown Motorola phone */	\
+	{ USB_DEVICE(0x0c44, 0x0022) },	/* unknown Motorola phone */	\
+	{ USB_DEVICE(0x22b8, 0x2a64) },	/* Motorola KRZR K1m */		\
+	{ USB_DEVICE(0x22b8, 0x2c84) },	/* Motorola VE240 phone */	\
+	{ USB_DEVICE(0x22b8, 0x2c64) }	/* Motorola V950 phone */
+DEVICE(moto_modem, MOTO_IDS);
+
+/* Motorola Tetra driver */
+#define MOTOROLA_TETRA_IDS()			\
+	{ USB_DEVICE(0x0cad, 0x9011) },	/* Motorola Solutions TETRA PEI */ \
+	{ USB_DEVICE(0x0cad, 0x9012) }	/* MTP6550 */
+DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS);
+
+/* Novatel Wireless GPS driver */
+#define NOVATEL_IDS()			\
+	{ USB_DEVICE(0x09d7, 0x0100) }	/* NovAtel FlexPack GPS */
+DEVICE_N(novatel_gps, NOVATEL_IDS, 3);
+
+/* HP4x (48/49) Generic Serial driver */
+#define HP4X_IDS()			\
+	{ USB_DEVICE(0x03f0, 0x0121) }
+DEVICE(hp4x, HP4X_IDS);
+
+/* Suunto ANT+ USB Driver */
+#define SUUNTO_IDS()			\
+	{ USB_DEVICE(0x0fcf, 0x1008) },	\
+	{ USB_DEVICE(0x0fcf, 0x1009) } /* Dynastream ANT USB-m Stick */
+DEVICE(suunto, SUUNTO_IDS);
+
+/* Siemens USB/MPI adapter */
+#define SIEMENS_IDS()			\
+	{ USB_DEVICE(0x908, 0x0004) }
+DEVICE(siemens_mpi, SIEMENS_IDS);
+
+/* All of the above structures mushed into two lists */
+static struct usb_serial_driver * const serial_drivers[] = {
+	&carelink_device,
+	&zio_device,
+	&funsoft_device,
+	&flashloader_device,
+	&google_device,
+	&libtransistor_device,
+	&vivopay_device,
+	&moto_modem_device,
+	&motorola_tetra_device,
+	&novatel_gps_device,
+	&hp4x_device,
+	&suunto_device,
+	&siemens_mpi_device,
+	NULL
+};
+
+static const struct usb_device_id id_table[] = {
+	CARELINK_IDS(),
+	ZIO_IDS(),
+	FUNSOFT_IDS(),
+	FLASHLOADER_IDS(),
+	GOOGLE_IDS(),
+	LIBTRANSISTOR_IDS(),
+	VIVOPAY_IDS(),
+	MOTO_IDS(),
+	MOTOROLA_TETRA_IDS(),
+	NOVATEL_IDS(),
+	HP4X_IDS(),
+	SUUNTO_IDS(),
+	SIEMENS_IDS(),
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+module_usb_serial_driver(serial_drivers, id_table);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
new file mode 100644
index 0000000..f7aaa7f
--- /dev/null
+++ b/drivers/usb/serial/usb-serial.c
@@ -0,0 +1,1425 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Serial Converter driver
+ *
+ * Copyright (C) 2009 - 2013 Johan Hovold (jhovold@gmail.com)
+ * Copyright (C) 1999 - 2012 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com)
+ *
+ * This driver was originally based on the ACM driver by Armin Fuerst (which was
+ * based on a driver by Brad Keryan)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/kfifo.h>
+#include <linux/idr.h>
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
+#define DRIVER_DESC "USB Serial Driver core"
+
+#define USB_SERIAL_TTY_MAJOR	188
+#define USB_SERIAL_TTY_MINORS	512	/* should be enough for a while */
+
+/* There is no MODULE_DEVICE_TABLE for usbserial.c.  Instead
+   the MODULE_DEVICE_TABLE declarations in each serial driver
+   cause the "hotplug" program to pull in whatever module is necessary
+   via modprobe, and modprobe will load usbserial because the serial
+   drivers depend on it.
+*/
+
+static DEFINE_IDR(serial_minors);
+static DEFINE_MUTEX(table_lock);
+static LIST_HEAD(usb_serial_driver_list);
+
+/*
+ * Look up the serial port structure.  If it is found and it hasn't been
+ * disconnected, return with the parent usb_serial structure's disc_mutex held
+ * and its refcount incremented.  Otherwise return NULL.
+ */
+struct usb_serial_port *usb_serial_port_get_by_minor(unsigned minor)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+
+	mutex_lock(&table_lock);
+	port = idr_find(&serial_minors, minor);
+	if (!port)
+		goto exit;
+
+	serial = port->serial;
+	mutex_lock(&serial->disc_mutex);
+	if (serial->disconnected) {
+		mutex_unlock(&serial->disc_mutex);
+		port = NULL;
+	} else {
+		kref_get(&serial->kref);
+	}
+exit:
+	mutex_unlock(&table_lock);
+	return port;
+}
+
+static int allocate_minors(struct usb_serial *serial, int num_ports)
+{
+	struct usb_serial_port *port;
+	unsigned int i, j;
+	int minor;
+
+	dev_dbg(&serial->interface->dev, "%s %d\n", __func__, num_ports);
+
+	mutex_lock(&table_lock);
+	for (i = 0; i < num_ports; ++i) {
+		port = serial->port[i];
+		minor = idr_alloc(&serial_minors, port, 0,
+					USB_SERIAL_TTY_MINORS, GFP_KERNEL);
+		if (minor < 0)
+			goto error;
+		port->minor = minor;
+		port->port_number = i;
+	}
+	serial->minors_reserved = 1;
+	mutex_unlock(&table_lock);
+	return 0;
+error:
+	/* unwind the already allocated minors */
+	for (j = 0; j < i; ++j)
+		idr_remove(&serial_minors, serial->port[j]->minor);
+	mutex_unlock(&table_lock);
+	return minor;
+}
+
+static void release_minors(struct usb_serial *serial)
+{
+	int i;
+
+	mutex_lock(&table_lock);
+	for (i = 0; i < serial->num_ports; ++i)
+		idr_remove(&serial_minors, serial->port[i]->minor);
+	mutex_unlock(&table_lock);
+	serial->minors_reserved = 0;
+}
+
+static void destroy_serial(struct kref *kref)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int i;
+
+	serial = to_usb_serial(kref);
+
+	/* return the minor range that this device had */
+	if (serial->minors_reserved)
+		release_minors(serial);
+
+	if (serial->attached && serial->type->release)
+		serial->type->release(serial);
+
+	/* Now that nothing is using the ports, they can be freed */
+	for (i = 0; i < serial->num_port_pointers; ++i) {
+		port = serial->port[i];
+		if (port) {
+			port->serial = NULL;
+			put_device(&port->dev);
+		}
+	}
+
+	usb_put_intf(serial->interface);
+	usb_put_dev(serial->dev);
+	kfree(serial);
+}
+
+void usb_serial_put(struct usb_serial *serial)
+{
+	kref_put(&serial->kref, destroy_serial);
+}
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+
+/**
+ * serial_install - install tty
+ * @driver: the driver (USB in our case)
+ * @tty: the tty being created
+ *
+ * Create the termios objects for this tty.  We use the default
+ * USB serial settings but permit them to be overridden by
+ * serial->type->init_termios.
+ *
+ * This is the first place a new tty gets used.  Hence this is where we
+ * acquire references to the usb_serial structure and the driver module,
+ * where we store a pointer to the port, and where we do an autoresume.
+ * All these actions are reversed in serial_cleanup().
+ */
+static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	int idx = tty->index;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int retval = -ENODEV;
+
+	port = usb_serial_port_get_by_minor(idx);
+	if (!port)
+		return retval;
+
+	serial = port->serial;
+	if (!try_module_get(serial->type->driver.owner))
+		goto error_module_get;
+
+	retval = usb_autopm_get_interface(serial->interface);
+	if (retval)
+		goto error_get_interface;
+
+	retval = tty_standard_install(driver, tty);
+	if (retval)
+		goto error_init_termios;
+
+	mutex_unlock(&serial->disc_mutex);
+
+	/* allow the driver to update the settings */
+	if (serial->type->init_termios)
+		serial->type->init_termios(tty);
+
+	tty->driver_data = port;
+
+	return retval;
+
+ error_init_termios:
+	usb_autopm_put_interface(serial->interface);
+ error_get_interface:
+	module_put(serial->type->driver.owner);
+ error_module_get:
+	usb_serial_put(serial);
+	mutex_unlock(&serial->disc_mutex);
+	return retval;
+}
+
+static int serial_port_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+	struct usb_serial_port *port =
+		container_of(tport, struct usb_serial_port, port);
+	struct usb_serial *serial = port->serial;
+	int retval;
+
+	mutex_lock(&serial->disc_mutex);
+	if (serial->disconnected)
+		retval = -ENODEV;
+	else
+		retval = port->serial->type->open(tty, port);
+	mutex_unlock(&serial->disc_mutex);
+
+	if (retval < 0)
+		retval = usb_translate_errors(retval);
+
+	return retval;
+}
+
+static int serial_open(struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	return tty_port_open(&port->port, tty, filp);
+}
+
+/**
+ * serial_port_shutdown - shut down hardware
+ * @tport: tty port to shut down
+ *
+ * Shut down a USB serial port. Serialized against activate by the
+ * tport mutex and kept to matching open/close pairs
+ * of calls by the initialized flag.
+ *
+ * Not called if tty is console.
+ */
+static void serial_port_shutdown(struct tty_port *tport)
+{
+	struct usb_serial_port *port =
+		container_of(tport, struct usb_serial_port, port);
+	struct usb_serial_driver *drv = port->serial->type;
+
+	if (drv->close)
+		drv->close(port);
+}
+
+static void serial_hangup(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	tty_port_hangup(&port->port);
+}
+
+static void serial_close(struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	tty_port_close(&port->port, tty, filp);
+}
+
+/**
+ * serial_cleanup - free resources post close/hangup
+ * @port: port to free up
+ *
+ * Do the resource freeing and refcount dropping for the port.
+ * Avoid freeing the console.
+ *
+ * Called asynchronously after the last tty kref is dropped.
+ */
+static void serial_cleanup(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial;
+	struct module *owner;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	/* The console is magical.  Do not hang up the console hardware
+	 * or there will be tears.
+	 */
+	if (port->port.console)
+		return;
+
+	tty->driver_data = NULL;
+
+	serial = port->serial;
+	owner = serial->type->driver.owner;
+
+	mutex_lock(&serial->disc_mutex);
+	if (!serial->disconnected)
+		usb_autopm_put_interface(serial->interface);
+	mutex_unlock(&serial->disc_mutex);
+
+	usb_serial_put(serial);
+	module_put(owner);
+}
+
+static int serial_write(struct tty_struct *tty, const unsigned char *buf,
+								int count)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int retval = -ENODEV;
+
+	if (port->serial->dev->state == USB_STATE_NOTATTACHED)
+		goto exit;
+
+	dev_dbg(tty->dev, "%s - %d byte(s)\n", __func__, count);
+
+	retval = port->serial->type->write(tty, port, buf, count);
+	if (retval < 0)
+		retval = usb_translate_errors(retval);
+exit:
+	return retval;
+}
+
+static int serial_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	return port->serial->type->write_room(tty);
+}
+
+static int serial_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (serial->disconnected)
+		return 0;
+
+	return serial->type->chars_in_buffer(tty);
+}
+
+static void serial_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (!port->serial->type->wait_until_sent)
+		return;
+
+	mutex_lock(&serial->disc_mutex);
+	if (!serial->disconnected)
+		port->serial->type->wait_until_sent(tty, timeout);
+	mutex_unlock(&serial->disc_mutex);
+}
+
+static void serial_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->throttle)
+		port->serial->type->throttle(tty);
+}
+
+static void serial_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->unthrottle)
+		port->serial->type->unthrottle(tty);
+}
+
+static int serial_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int retval = -ENOIOCTLCMD;
+
+	dev_dbg(tty->dev, "%s - cmd 0x%04x\n", __func__, cmd);
+
+	switch (cmd) {
+	case TIOCMIWAIT:
+		if (port->serial->type->tiocmiwait)
+			retval = port->serial->type->tiocmiwait(tty, arg);
+		break;
+	default:
+		if (port->serial->type->ioctl)
+			retval = port->serial->type->ioctl(tty, cmd, arg);
+	}
+
+	return retval;
+}
+
+static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->set_termios)
+		port->serial->type->set_termios(tty, port, old);
+	else
+		tty_termios_copy_hw(&tty->termios, old);
+}
+
+static int serial_break(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->break_ctl)
+		port->serial->type->break_ctl(tty, break_state);
+
+	return 0;
+}
+
+static int serial_proc_show(struct seq_file *m, void *v)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int i;
+	char tmp[40];
+
+	seq_puts(m, "usbserinfo:1.0 driver:2.0\n");
+	for (i = 0; i < USB_SERIAL_TTY_MINORS; ++i) {
+		port = usb_serial_port_get_by_minor(i);
+		if (port == NULL)
+			continue;
+		serial = port->serial;
+
+		seq_printf(m, "%d:", i);
+		if (serial->type->driver.owner)
+			seq_printf(m, " module:%s",
+				module_name(serial->type->driver.owner));
+		seq_printf(m, " name:\"%s\"",
+				serial->type->description);
+		seq_printf(m, " vendor:%04x product:%04x",
+			le16_to_cpu(serial->dev->descriptor.idVendor),
+			le16_to_cpu(serial->dev->descriptor.idProduct));
+		seq_printf(m, " num_ports:%d", serial->num_ports);
+		seq_printf(m, " port:%d", port->port_number);
+		usb_make_path(serial->dev, tmp, sizeof(tmp));
+		seq_printf(m, " path:%s", tmp);
+
+		seq_putc(m, '\n');
+		usb_serial_put(serial);
+		mutex_unlock(&serial->disc_mutex);
+	}
+	return 0;
+}
+
+static int serial_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->tiocmget)
+		return port->serial->type->tiocmget(tty);
+	return -EINVAL;
+}
+
+static int serial_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->tiocmset)
+		return port->serial->type->tiocmset(tty, set, clear);
+	return -EINVAL;
+}
+
+static int serial_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	if (port->serial->type->get_icount)
+		return port->serial->type->get_icount(tty, icount);
+	return -EINVAL;
+}
+
+/*
+ * We would be calling tty_wakeup here, but unfortunately some line
+ * disciplines have an annoying habit of calling tty->write from
+ * the write wakeup callback (e.g. n_hdlc.c).
+ */
+void usb_serial_port_softint(struct usb_serial_port *port)
+{
+	schedule_work(&port->work);
+}
+EXPORT_SYMBOL_GPL(usb_serial_port_softint);
+
+static void usb_serial_port_work(struct work_struct *work)
+{
+	struct usb_serial_port *port =
+		container_of(work, struct usb_serial_port, work);
+
+	tty_port_tty_wakeup(&port->port);
+}
+
+static void usb_serial_port_poison_urbs(struct usb_serial_port *port)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+		usb_poison_urb(port->read_urbs[i]);
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+		usb_poison_urb(port->write_urbs[i]);
+
+	usb_poison_urb(port->interrupt_in_urb);
+	usb_poison_urb(port->interrupt_out_urb);
+}
+
+static void usb_serial_port_unpoison_urbs(struct usb_serial_port *port)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+		usb_unpoison_urb(port->read_urbs[i]);
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+		usb_unpoison_urb(port->write_urbs[i]);
+
+	usb_unpoison_urb(port->interrupt_in_urb);
+	usb_unpoison_urb(port->interrupt_out_urb);
+}
+
+static void usb_serial_port_release(struct device *dev)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	int i;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	usb_free_urb(port->interrupt_in_urb);
+	usb_free_urb(port->interrupt_out_urb);
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+		usb_free_urb(port->read_urbs[i]);
+		kfree(port->bulk_in_buffers[i]);
+	}
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
+		usb_free_urb(port->write_urbs[i]);
+		kfree(port->bulk_out_buffers[i]);
+	}
+	kfifo_free(&port->write_fifo);
+	kfree(port->interrupt_in_buffer);
+	kfree(port->interrupt_out_buffer);
+	tty_port_destroy(&port->port);
+	kfree(port);
+}
+
+static struct usb_serial *create_serial(struct usb_device *dev,
+					struct usb_interface *interface,
+					struct usb_serial_driver *driver)
+{
+	struct usb_serial *serial;
+
+	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
+	if (!serial)
+		return NULL;
+	serial->dev = usb_get_dev(dev);
+	serial->type = driver;
+	serial->interface = usb_get_intf(interface);
+	kref_init(&serial->kref);
+	mutex_init(&serial->disc_mutex);
+	serial->minors_reserved = 0;
+
+	return serial;
+}
+
+static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf,
+					    struct usb_serial_driver *drv)
+{
+	struct usb_dynid *dynid;
+
+	spin_lock(&drv->dynids.lock);
+	list_for_each_entry(dynid, &drv->dynids.list, node) {
+		if (usb_match_one_id(intf, &dynid->id)) {
+			spin_unlock(&drv->dynids.lock);
+			return &dynid->id;
+		}
+	}
+	spin_unlock(&drv->dynids.lock);
+	return NULL;
+}
+
+static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv,
+						struct usb_interface *intf)
+{
+	const struct usb_device_id *id;
+
+	id = usb_match_id(intf, drv->id_table);
+	if (id) {
+		dev_dbg(&intf->dev, "static descriptor matches\n");
+		goto exit;
+	}
+	id = match_dynamic_id(intf, drv);
+	if (id)
+		dev_dbg(&intf->dev, "dynamic descriptor matches\n");
+exit:
+	return id;
+}
+
+/* Caller must hold table_lock */
+static struct usb_serial_driver *search_serial_device(
+					struct usb_interface *iface)
+{
+	const struct usb_device_id *id = NULL;
+	struct usb_serial_driver *drv;
+	struct usb_driver *driver = to_usb_driver(iface->dev.driver);
+
+	/* Check if the usb id matches a known device */
+	list_for_each_entry(drv, &usb_serial_driver_list, driver_list) {
+		if (drv->usb_driver == driver)
+			id = get_iface_id(drv, iface);
+		if (id)
+			return drv;
+	}
+
+	return NULL;
+}
+
+static int serial_port_carrier_raised(struct tty_port *port)
+{
+	struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+	struct usb_serial_driver *drv = p->serial->type;
+
+	if (drv->carrier_raised)
+		return drv->carrier_raised(p);
+	/* No carrier control - don't block */
+	return 1;
+}
+
+static void serial_port_dtr_rts(struct tty_port *port, int on)
+{
+	struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+	struct usb_serial_driver *drv = p->serial->type;
+
+	if (drv->dtr_rts)
+		drv->dtr_rts(p, on);
+}
+
+static ssize_t port_number_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+
+	return sprintf(buf, "%u\n", port->port_number);
+}
+static DEVICE_ATTR_RO(port_number);
+
+static struct attribute *usb_serial_port_attrs[] = {
+	&dev_attr_port_number.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(usb_serial_port);
+
+static const struct tty_port_operations serial_port_ops = {
+	.carrier_raised		= serial_port_carrier_raised,
+	.dtr_rts		= serial_port_dtr_rts,
+	.activate		= serial_port_activate,
+	.shutdown		= serial_port_shutdown,
+};
+
+static void find_endpoints(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	struct device *dev = &serial->interface->dev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *epd;
+	unsigned int i;
+
+	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_in) < USB_MAXENDPOINTS / 2);
+	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < USB_MAXENDPOINTS / 2);
+	BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_in) < USB_MAXENDPOINTS / 2);
+	BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_out) < USB_MAXENDPOINTS / 2);
+
+	iface_desc = serial->interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		epd = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_bulk_in(epd)) {
+			dev_dbg(dev, "found bulk in on endpoint %u\n", i);
+			epds->bulk_in[epds->num_bulk_in++] = epd;
+		} else if (usb_endpoint_is_bulk_out(epd)) {
+			dev_dbg(dev, "found bulk out on endpoint %u\n", i);
+			epds->bulk_out[epds->num_bulk_out++] = epd;
+		} else if (usb_endpoint_is_int_in(epd)) {
+			dev_dbg(dev, "found interrupt in on endpoint %u\n", i);
+			epds->interrupt_in[epds->num_interrupt_in++] = epd;
+		} else if (usb_endpoint_is_int_out(epd)) {
+			dev_dbg(dev, "found interrupt out on endpoint %u\n", i);
+			epds->interrupt_out[epds->num_interrupt_out++] = epd;
+		}
+	}
+}
+
+static int setup_port_bulk_in(struct usb_serial_port *port,
+					struct usb_endpoint_descriptor *epd)
+{
+	struct usb_serial_driver *type = port->serial->type;
+	struct usb_device *udev = port->serial->dev;
+	int buffer_size;
+	int i;
+
+	buffer_size = max_t(int, type->bulk_in_size, usb_endpoint_maxp(epd));
+	port->bulk_in_size = buffer_size;
+	port->bulk_in_endpointAddress = epd->bEndpointAddress;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+		set_bit(i, &port->read_urbs_free);
+		port->read_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!port->read_urbs[i])
+			return -ENOMEM;
+		port->bulk_in_buffers[i] = kmalloc(buffer_size, GFP_KERNEL);
+		if (!port->bulk_in_buffers[i])
+			return -ENOMEM;
+		usb_fill_bulk_urb(port->read_urbs[i], udev,
+				usb_rcvbulkpipe(udev, epd->bEndpointAddress),
+				port->bulk_in_buffers[i], buffer_size,
+				type->read_bulk_callback, port);
+	}
+
+	port->read_urb = port->read_urbs[0];
+	port->bulk_in_buffer = port->bulk_in_buffers[0];
+
+	return 0;
+}
+
+static int setup_port_bulk_out(struct usb_serial_port *port,
+					struct usb_endpoint_descriptor *epd)
+{
+	struct usb_serial_driver *type = port->serial->type;
+	struct usb_device *udev = port->serial->dev;
+	int buffer_size;
+	int i;
+
+	if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+		return -ENOMEM;
+	if (type->bulk_out_size)
+		buffer_size = type->bulk_out_size;
+	else
+		buffer_size = usb_endpoint_maxp(epd);
+	port->bulk_out_size = buffer_size;
+	port->bulk_out_endpointAddress = epd->bEndpointAddress;
+
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
+		set_bit(i, &port->write_urbs_free);
+		port->write_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!port->write_urbs[i])
+			return -ENOMEM;
+		port->bulk_out_buffers[i] = kmalloc(buffer_size, GFP_KERNEL);
+		if (!port->bulk_out_buffers[i])
+			return -ENOMEM;
+		usb_fill_bulk_urb(port->write_urbs[i], udev,
+				usb_sndbulkpipe(udev, epd->bEndpointAddress),
+				port->bulk_out_buffers[i], buffer_size,
+				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 setup_port_interrupt_in(struct usb_serial_port *port,
+					struct usb_endpoint_descriptor *epd)
+{
+	struct usb_serial_driver *type = port->serial->type;
+	struct usb_device *udev = port->serial->dev;
+	int buffer_size;
+
+	port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!port->interrupt_in_urb)
+		return -ENOMEM;
+	buffer_size = usb_endpoint_maxp(epd);
+	port->interrupt_in_endpointAddress = epd->bEndpointAddress;
+	port->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!port->interrupt_in_buffer)
+		return -ENOMEM;
+	usb_fill_int_urb(port->interrupt_in_urb, udev,
+			usb_rcvintpipe(udev, epd->bEndpointAddress),
+			port->interrupt_in_buffer, buffer_size,
+			type->read_int_callback, port,
+			epd->bInterval);
+
+	return 0;
+}
+
+static int setup_port_interrupt_out(struct usb_serial_port *port,
+					struct usb_endpoint_descriptor *epd)
+{
+	struct usb_serial_driver *type = port->serial->type;
+	struct usb_device *udev = port->serial->dev;
+	int buffer_size;
+
+	port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!port->interrupt_out_urb)
+		return -ENOMEM;
+	buffer_size = usb_endpoint_maxp(epd);
+	port->interrupt_out_size = buffer_size;
+	port->interrupt_out_endpointAddress = epd->bEndpointAddress;
+	port->interrupt_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!port->interrupt_out_buffer)
+		return -ENOMEM;
+	usb_fill_int_urb(port->interrupt_out_urb, udev,
+			usb_sndintpipe(udev, epd->bEndpointAddress),
+			port->interrupt_out_buffer, buffer_size,
+			type->write_int_callback, port,
+			epd->bInterval);
+
+	return 0;
+}
+
+static int usb_serial_probe(struct usb_interface *interface,
+			       const struct usb_device_id *id)
+{
+	struct device *ddev = &interface->dev;
+	struct usb_device *dev = interface_to_usbdev(interface);
+	struct usb_serial *serial = NULL;
+	struct usb_serial_port *port;
+	struct usb_serial_endpoints *epds;
+	struct usb_serial_driver *type = NULL;
+	int retval;
+	int i;
+	int num_ports = 0;
+	unsigned char max_endpoints;
+
+	mutex_lock(&table_lock);
+	type = search_serial_device(interface);
+	if (!type) {
+		mutex_unlock(&table_lock);
+		dev_dbg(ddev, "none matched\n");
+		return -ENODEV;
+	}
+
+	if (!try_module_get(type->driver.owner)) {
+		mutex_unlock(&table_lock);
+		dev_err(ddev, "module get failed, exiting\n");
+		return -EIO;
+	}
+	mutex_unlock(&table_lock);
+
+	serial = create_serial(dev, interface, type);
+	if (!serial) {
+		retval = -ENOMEM;
+		goto err_put_module;
+	}
+
+	/* if this device type has a probe function, call it */
+	if (type->probe) {
+		const struct usb_device_id *id;
+
+		id = get_iface_id(type, interface);
+		retval = type->probe(serial, id);
+
+		if (retval) {
+			dev_dbg(ddev, "sub driver rejected device\n");
+			goto err_put_serial;
+		}
+	}
+
+	/* descriptor matches, let's find the endpoints needed */
+	epds = kzalloc(sizeof(*epds), GFP_KERNEL);
+	if (!epds) {
+		retval = -ENOMEM;
+		goto err_put_serial;
+	}
+
+	find_endpoints(serial, epds);
+
+	if (epds->num_bulk_in < type->num_bulk_in ||
+			epds->num_bulk_out < type->num_bulk_out ||
+			epds->num_interrupt_in < type->num_interrupt_in ||
+			epds->num_interrupt_out < type->num_interrupt_out) {
+		dev_err(ddev, "required endpoints missing\n");
+		retval = -ENODEV;
+		goto err_free_epds;
+	}
+
+	if (type->calc_num_ports) {
+		retval = type->calc_num_ports(serial, epds);
+		if (retval < 0)
+			goto err_free_epds;
+		num_ports = retval;
+	}
+
+	if (!num_ports)
+		num_ports = type->num_ports;
+
+	if (num_ports > MAX_NUM_PORTS) {
+		dev_warn(ddev, "too many ports requested: %d\n", num_ports);
+		num_ports = MAX_NUM_PORTS;
+	}
+
+	serial->num_ports = (unsigned char)num_ports;
+	serial->num_bulk_in = epds->num_bulk_in;
+	serial->num_bulk_out = epds->num_bulk_out;
+	serial->num_interrupt_in = epds->num_interrupt_in;
+	serial->num_interrupt_out = epds->num_interrupt_out;
+
+	/* found all that we need */
+	dev_info(ddev, "%s converter detected\n", type->description);
+
+	/* create our ports, we need as many as the max endpoints */
+	/* we don't use num_ports here because some devices have more
+	   endpoint pairs than ports */
+	max_endpoints = max(epds->num_bulk_in, epds->num_bulk_out);
+	max_endpoints = max(max_endpoints, epds->num_interrupt_in);
+	max_endpoints = max(max_endpoints, epds->num_interrupt_out);
+	max_endpoints = max(max_endpoints, serial->num_ports);
+	serial->num_port_pointers = max_endpoints;
+
+	dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints);
+	for (i = 0; i < max_endpoints; ++i) {
+		port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
+		if (!port) {
+			retval = -ENOMEM;
+			goto err_free_epds;
+		}
+		tty_port_init(&port->port);
+		port->port.ops = &serial_port_ops;
+		port->serial = serial;
+		spin_lock_init(&port->lock);
+		/* Keep this for private driver use for the moment but
+		   should probably go away */
+		INIT_WORK(&port->work, usb_serial_port_work);
+		serial->port[i] = port;
+		port->dev.parent = &interface->dev;
+		port->dev.driver = NULL;
+		port->dev.bus = &usb_serial_bus_type;
+		port->dev.release = &usb_serial_port_release;
+		port->dev.groups = usb_serial_port_groups;
+		device_initialize(&port->dev);
+	}
+
+	/* set up the endpoint information */
+	for (i = 0; i < epds->num_bulk_in; ++i) {
+		retval = setup_port_bulk_in(serial->port[i], epds->bulk_in[i]);
+		if (retval)
+			goto err_free_epds;
+	}
+
+	for (i = 0; i < epds->num_bulk_out; ++i) {
+		retval = setup_port_bulk_out(serial->port[i],
+				epds->bulk_out[i]);
+		if (retval)
+			goto err_free_epds;
+	}
+
+	if (serial->type->read_int_callback) {
+		for (i = 0; i < epds->num_interrupt_in; ++i) {
+			retval = setup_port_interrupt_in(serial->port[i],
+					epds->interrupt_in[i]);
+			if (retval)
+				goto err_free_epds;
+		}
+	} else if (epds->num_interrupt_in) {
+		dev_dbg(ddev, "The device claims to support interrupt in transfers, but read_int_callback is not defined\n");
+	}
+
+	if (serial->type->write_int_callback) {
+		for (i = 0; i < epds->num_interrupt_out; ++i) {
+			retval = setup_port_interrupt_out(serial->port[i],
+					epds->interrupt_out[i]);
+			if (retval)
+				goto err_free_epds;
+		}
+	} else if (epds->num_interrupt_out) {
+		dev_dbg(ddev, "The device claims to support interrupt out transfers, but write_int_callback is not defined\n");
+	}
+
+	usb_set_intfdata(interface, serial);
+
+	/* if this device type has an attach function, call it */
+	if (type->attach) {
+		retval = type->attach(serial);
+		if (retval < 0)
+			goto err_free_epds;
+		serial->attached = 1;
+		if (retval > 0) {
+			/* quietly accept this device, but don't bind to a
+			   serial port as it's about to disappear */
+			serial->num_ports = 0;
+			goto exit;
+		}
+	} else {
+		serial->attached = 1;
+	}
+
+	retval = allocate_minors(serial, num_ports);
+	if (retval) {
+		dev_err(ddev, "No more free serial minor numbers\n");
+		goto err_free_epds;
+	}
+
+	/* register all of the individual ports with the driver core */
+	for (i = 0; i < num_ports; ++i) {
+		port = serial->port[i];
+		dev_set_name(&port->dev, "ttyUSB%d", port->minor);
+		dev_dbg(ddev, "registering %s\n", dev_name(&port->dev));
+		device_enable_async_suspend(&port->dev);
+
+		retval = device_add(&port->dev);
+		if (retval)
+			dev_err(ddev, "Error registering port device, continuing\n");
+	}
+
+	if (num_ports > 0)
+		usb_serial_console_init(serial->port[0]->minor);
+exit:
+	kfree(epds);
+	module_put(type->driver.owner);
+	return 0;
+
+err_free_epds:
+	kfree(epds);
+err_put_serial:
+	usb_serial_put(serial);
+err_put_module:
+	module_put(type->driver.owner);
+
+	return retval;
+}
+
+static void usb_serial_disconnect(struct usb_interface *interface)
+{
+	int i;
+	struct usb_serial *serial = usb_get_intfdata(interface);
+	struct device *dev = &interface->dev;
+	struct usb_serial_port *port;
+	struct tty_struct *tty;
+
+	usb_serial_console_disconnect(serial);
+
+	mutex_lock(&serial->disc_mutex);
+	/* must set a flag, to signal subdrivers */
+	serial->disconnected = 1;
+	mutex_unlock(&serial->disc_mutex);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		tty = tty_port_tty_get(&port->port);
+		if (tty) {
+			tty_vhangup(tty);
+			tty_kref_put(tty);
+		}
+		usb_serial_port_poison_urbs(port);
+		wake_up_interruptible(&port->port.delta_msr_wait);
+		cancel_work_sync(&port->work);
+		if (device_is_registered(&port->dev))
+			device_del(&port->dev);
+	}
+	if (serial->type->disconnect)
+		serial->type->disconnect(serial);
+
+	/* let the last holder of this object cause it to be cleaned up */
+	usb_serial_put(serial);
+	dev_info(dev, "device disconnected\n");
+}
+
+int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	int i, r = 0;
+
+	serial->suspending = 1;
+
+	/*
+	 * serial->type->suspend() MUST return 0 in system sleep context,
+	 * otherwise, the resume callback has to recover device from
+	 * previous suspend failure.
+	 */
+	if (serial->type->suspend) {
+		r = serial->type->suspend(serial, message);
+		if (r < 0) {
+			serial->suspending = 0;
+			goto err_out;
+		}
+	}
+
+	for (i = 0; i < serial->num_ports; ++i)
+		usb_serial_port_poison_urbs(serial->port[i]);
+err_out:
+	return r;
+}
+EXPORT_SYMBOL(usb_serial_suspend);
+
+static void usb_serial_unpoison_port_urbs(struct usb_serial *serial)
+{
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i)
+		usb_serial_port_unpoison_urbs(serial->port[i]);
+}
+
+int usb_serial_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	int rv;
+
+	usb_serial_unpoison_port_urbs(serial);
+
+	serial->suspending = 0;
+	if (serial->type->resume)
+		rv = serial->type->resume(serial);
+	else
+		rv = usb_serial_generic_resume(serial);
+
+	return rv;
+}
+EXPORT_SYMBOL(usb_serial_resume);
+
+static int usb_serial_reset_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	int rv;
+
+	usb_serial_unpoison_port_urbs(serial);
+
+	serial->suspending = 0;
+	if (serial->type->reset_resume) {
+		rv = serial->type->reset_resume(serial);
+	} else {
+		rv = -EOPNOTSUPP;
+		intf->needs_binding = 1;
+	}
+
+	return rv;
+}
+
+static const struct tty_operations serial_ops = {
+	.open =			serial_open,
+	.close =		serial_close,
+	.write =		serial_write,
+	.hangup =		serial_hangup,
+	.write_room =		serial_write_room,
+	.ioctl =		serial_ioctl,
+	.set_termios =		serial_set_termios,
+	.throttle =		serial_throttle,
+	.unthrottle =		serial_unthrottle,
+	.break_ctl =		serial_break,
+	.chars_in_buffer =	serial_chars_in_buffer,
+	.wait_until_sent =	serial_wait_until_sent,
+	.tiocmget =		serial_tiocmget,
+	.tiocmset =		serial_tiocmset,
+	.get_icount =		serial_get_icount,
+	.cleanup =		serial_cleanup,
+	.install =		serial_install,
+	.proc_show =		serial_proc_show,
+};
+
+
+struct tty_driver *usb_serial_tty_driver;
+
+static int __init usb_serial_init(void)
+{
+	int result;
+
+	usb_serial_tty_driver = alloc_tty_driver(USB_SERIAL_TTY_MINORS);
+	if (!usb_serial_tty_driver)
+		return -ENOMEM;
+
+	/* Initialize our global data */
+	result = bus_register(&usb_serial_bus_type);
+	if (result) {
+		pr_err("%s - registering bus driver failed\n", __func__);
+		goto exit_bus;
+	}
+
+	usb_serial_tty_driver->driver_name = "usbserial";
+	usb_serial_tty_driver->name = "ttyUSB";
+	usb_serial_tty_driver->major = USB_SERIAL_TTY_MAJOR;
+	usb_serial_tty_driver->minor_start = 0;
+	usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
+						TTY_DRIVER_DYNAMIC_DEV;
+	usb_serial_tty_driver->init_termios = tty_std_termios;
+	usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
+							| HUPCL | CLOCAL;
+	usb_serial_tty_driver->init_termios.c_ispeed = 9600;
+	usb_serial_tty_driver->init_termios.c_ospeed = 9600;
+	tty_set_operations(usb_serial_tty_driver, &serial_ops);
+	result = tty_register_driver(usb_serial_tty_driver);
+	if (result) {
+		pr_err("%s - tty_register_driver failed\n", __func__);
+		goto exit_reg_driver;
+	}
+
+	/* register the generic driver, if we should */
+	result = usb_serial_generic_register();
+	if (result < 0) {
+		pr_err("%s - registering generic driver failed\n", __func__);
+		goto exit_generic;
+	}
+
+	return result;
+
+exit_generic:
+	tty_unregister_driver(usb_serial_tty_driver);
+
+exit_reg_driver:
+	bus_unregister(&usb_serial_bus_type);
+
+exit_bus:
+	pr_err("%s - returning with error %d\n", __func__, result);
+	put_tty_driver(usb_serial_tty_driver);
+	return result;
+}
+
+
+static void __exit usb_serial_exit(void)
+{
+	usb_serial_console_exit();
+
+	usb_serial_generic_deregister();
+
+	tty_unregister_driver(usb_serial_tty_driver);
+	put_tty_driver(usb_serial_tty_driver);
+	bus_unregister(&usb_serial_bus_type);
+	idr_destroy(&serial_minors);
+}
+
+
+module_init(usb_serial_init);
+module_exit(usb_serial_exit);
+
+#define set_to_generic_if_null(type, function)				\
+	do {								\
+		if (!type->function) {					\
+			type->function = usb_serial_generic_##function;	\
+			pr_debug("%s: using generic " #function	"\n",	\
+						type->driver.name);	\
+		}							\
+	} while (0)
+
+static void usb_serial_operations_init(struct usb_serial_driver *device)
+{
+	set_to_generic_if_null(device, open);
+	set_to_generic_if_null(device, write);
+	set_to_generic_if_null(device, close);
+	set_to_generic_if_null(device, write_room);
+	set_to_generic_if_null(device, chars_in_buffer);
+	if (device->tx_empty)
+		set_to_generic_if_null(device, wait_until_sent);
+	set_to_generic_if_null(device, read_bulk_callback);
+	set_to_generic_if_null(device, write_bulk_callback);
+	set_to_generic_if_null(device, process_read_urb);
+	set_to_generic_if_null(device, prepare_write_buffer);
+}
+
+static int usb_serial_register(struct usb_serial_driver *driver)
+{
+	int retval;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (!driver->description)
+		driver->description = driver->driver.name;
+	if (!driver->usb_driver) {
+		WARN(1, "Serial driver %s has no usb_driver\n",
+				driver->description);
+		return -EINVAL;
+	}
+
+	usb_serial_operations_init(driver);
+
+	/* Add this device to our list of devices */
+	mutex_lock(&table_lock);
+	list_add(&driver->driver_list, &usb_serial_driver_list);
+
+	retval = usb_serial_bus_register(driver);
+	if (retval) {
+		pr_err("problem %d when registering driver %s\n", retval, driver->description);
+		list_del(&driver->driver_list);
+	} else {
+		pr_info("USB Serial support registered for %s\n", driver->description);
+	}
+	mutex_unlock(&table_lock);
+	return retval;
+}
+
+static void usb_serial_deregister(struct usb_serial_driver *device)
+{
+	pr_info("USB Serial deregistering driver %s\n", device->description);
+
+	mutex_lock(&table_lock);
+	list_del(&device->driver_list);
+	mutex_unlock(&table_lock);
+
+	usb_serial_bus_deregister(device);
+}
+
+/**
+ * usb_serial_register_drivers - register drivers for a usb-serial module
+ * @serial_drivers: NULL-terminated array of pointers to drivers to be registered
+ * @name: name of the usb_driver for this set of @serial_drivers
+ * @id_table: list of all devices this @serial_drivers set binds to
+ *
+ * Registers all the drivers in the @serial_drivers array, and dynamically
+ * creates a struct usb_driver with the name @name and id_table of @id_table.
+ */
+int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
+				const char *name,
+				const struct usb_device_id *id_table)
+{
+	int rc;
+	struct usb_driver *udriver;
+	struct usb_serial_driver * const *sd;
+
+	/*
+	 * udriver must be registered before any of the serial drivers,
+	 * because the store_new_id() routine for the serial drivers (in
+	 * bus.c) probes udriver.
+	 *
+	 * Performance hack: We don't want udriver to be probed until
+	 * the serial drivers are registered, because the probe would
+	 * simply fail for lack of a matching serial driver.
+	 * So we leave udriver's id_table set to NULL until we are all set.
+	 *
+	 * Suspend/resume support is implemented in the usb-serial core,
+	 * so fill in the PM-related fields in udriver.
+	 */
+	udriver = kzalloc(sizeof(*udriver), GFP_KERNEL);
+	if (!udriver)
+		return -ENOMEM;
+
+	udriver->name = name;
+	udriver->no_dynamic_id = 1;
+	udriver->supports_autosuspend = 1;
+	udriver->suspend = usb_serial_suspend;
+	udriver->resume = usb_serial_resume;
+	udriver->probe = usb_serial_probe;
+	udriver->disconnect = usb_serial_disconnect;
+
+	/* we only set the reset_resume field if the serial_driver has one */
+	for (sd = serial_drivers; *sd; ++sd) {
+		if ((*sd)->reset_resume) {
+			udriver->reset_resume = usb_serial_reset_resume;
+			break;
+		}
+	}
+
+	rc = usb_register(udriver);
+	if (rc)
+		goto failed_usb_register;
+
+	for (sd = serial_drivers; *sd; ++sd) {
+		(*sd)->usb_driver = udriver;
+		rc = usb_serial_register(*sd);
+		if (rc)
+			goto failed;
+	}
+
+	/* Now set udriver's id_table and look for matches */
+	udriver->id_table = id_table;
+	rc = driver_attach(&udriver->drvwrap.driver);
+	return 0;
+
+ failed:
+	while (sd-- > serial_drivers)
+		usb_serial_deregister(*sd);
+	usb_deregister(udriver);
+failed_usb_register:
+	kfree(udriver);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(usb_serial_register_drivers);
+
+/**
+ * usb_serial_deregister_drivers - deregister drivers for a usb-serial module
+ * @serial_drivers: NULL-terminated array of pointers to drivers to be deregistered
+ *
+ * Deregisters all the drivers in the @serial_drivers array and deregisters and
+ * frees the struct usb_driver that was created by the call to
+ * usb_serial_register_drivers().
+ */
+void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[])
+{
+	struct usb_driver *udriver = (*serial_drivers)->usb_driver;
+
+	for (; *serial_drivers; ++serial_drivers)
+		usb_serial_deregister(*serial_drivers);
+	usb_deregister(udriver);
+	kfree(udriver);
+}
+EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
new file mode 100644
index 0000000..d28dab4
--- /dev/null
+++ b/drivers/usb/serial/usb-wwan.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Definitions for USB serial mobile broadband cards
+ */
+
+#ifndef __LINUX_USB_USB_WWAN
+#define __LINUX_USB_USB_WWAN
+
+extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
+extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
+extern void usb_wwan_close(struct usb_serial_port *port);
+extern int usb_wwan_port_probe(struct usb_serial_port *port);
+extern int usb_wwan_port_remove(struct usb_serial_port *port);
+extern int usb_wwan_write_room(struct tty_struct *tty);
+extern int usb_wwan_tiocmget(struct tty_struct *tty);
+extern int usb_wwan_tiocmset(struct tty_struct *tty,
+			     unsigned int set, unsigned int clear);
+extern int usb_wwan_ioctl(struct tty_struct *tty,
+			  unsigned int cmd, unsigned long arg);
+extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
+			  const unsigned char *buf, int count);
+extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
+#ifdef CONFIG_PM
+extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
+extern int usb_wwan_resume(struct usb_serial *serial);
+#endif
+
+/* per port private data */
+
+#define N_IN_URB 4
+#define N_OUT_URB 4
+#define IN_BUFLEN 4096
+#define OUT_BUFLEN 4096
+
+struct usb_wwan_intf_private {
+	spinlock_t susp_lock;
+	unsigned int suspended:1;
+	unsigned int use_send_setup:1;
+	int in_flight;
+	unsigned int open_ports;
+	void *private;
+};
+
+struct usb_wwan_port_private {
+	/* Input endpoints and buffer for this port */
+	struct urb *in_urbs[N_IN_URB];
+	u8 *in_buffer[N_IN_URB];
+	/* Output endpoints and buffer for this port */
+	struct urb *out_urbs[N_OUT_URB];
+	u8 *out_buffer[N_OUT_URB];
+	unsigned long out_busy;	/* Bit vector of URBs in use */
+	struct usb_anchor delayed;
+
+	/* Settings for the port */
+	int rts_state;		/* Handshaking pins (outputs) */
+	int dtr_state;
+	int cts_state;		/* Handshaking pins (inputs) */
+	int dsr_state;
+	int dcd_state;
+	int ri_state;
+
+	unsigned long tx_start_time[N_OUT_URB];
+};
+
+#endif /* __LINUX_USB_USB_WWAN */
diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c
new file mode 100644
index 0000000..aaf4813
--- /dev/null
+++ b/drivers/usb/serial/usb_debug.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Debug cable driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <greg@kroah.com>
+ */
+
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define USB_DEBUG_MAX_PACKET_SIZE	8
+#define USB_DEBUG_BRK_SIZE		8
+static const char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = {
+	0x00,
+	0xff,
+	0x01,
+	0xfe,
+	0x00,
+	0xfe,
+	0x01,
+	0xff,
+};
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ },
+};
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0010) },
+	{ USB_DEVICE(0x1d6b, 0x0011) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0010) },
+	{ USB_DEVICE(0x1d6b, 0x0011) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+/* This HW really does not support a serial break, so one will be
+ * emulated when ever the break state is set to true.
+ */
+static void usb_debug_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	if (!break_state)
+		return;
+	usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
+}
+
+static void usb_debug_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+
+	if (urb->actual_length == USB_DEBUG_BRK_SIZE &&
+		memcmp(urb->transfer_buffer, USB_DEBUG_BRK,
+						USB_DEBUG_BRK_SIZE) == 0) {
+		usb_serial_handle_break(port);
+		return;
+	}
+
+	usb_serial_generic_process_read_urb(urb);
+}
+
+static struct usb_serial_driver debug_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"debug",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.bulk_out_size =	USB_DEBUG_MAX_PACKET_SIZE,
+	.break_ctl =		usb_debug_break_ctl,
+	.process_read_urb =	usb_debug_process_read_urb,
+};
+
+static struct usb_serial_driver dbc_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"xhci_dbc",
+	},
+	.id_table =		dbc_id_table,
+	.num_ports =		1,
+	.break_ctl =		usb_debug_break_ctl,
+	.process_read_urb =	usb_debug_process_read_urb,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&debug_device, &dbc_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
new file mode 100644
index 0000000..912472f
--- /dev/null
+++ b/drivers/usb/serial/usb_wwan.c
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+  USB Driver layer for GSM modems
+
+  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
+
+  Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
+
+  History: see the git log.
+
+  Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
+
+  This driver exists because the "normal" serial driver doesn't work too well
+  with GSM modems. Issues:
+  - data loss -- one single Receive URB is not nearly enough
+  - controlling the baud rate doesn't make sense
+*/
+
+#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
+#define DRIVER_DESC "USB Driver for GSM modems"
+
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include "usb-wwan.h"
+
+/*
+ * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request
+ * in CDC ACM.
+ */
+static int usb_wwan_send_setup(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct usb_wwan_port_private *portdata;
+	int val = 0;
+	int ifnum;
+	int res;
+
+	portdata = usb_get_serial_port_data(port);
+
+	if (portdata->dtr_state)
+		val |= 0x01;
+	if (portdata->rts_state)
+		val |= 0x02;
+
+	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+
+	res = usb_autopm_get_interface(serial->interface);
+	if (res)
+		return res;
+
+	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				0x22, 0x21, val, ifnum, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+
+	usb_autopm_put_interface(port->serial->interface);
+
+	return res;
+}
+
+void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata;
+
+	intfdata = usb_get_serial_data(port->serial);
+
+	if (!intfdata->use_send_setup)
+		return;
+
+	portdata = usb_get_serial_port_data(port);
+	/* FIXME: locking */
+	portdata->rts_state = on;
+	portdata->dtr_state = on;
+
+	usb_wwan_send_setup(port);
+}
+EXPORT_SYMBOL(usb_wwan_dtr_rts);
+
+int usb_wwan_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int value;
+	struct usb_wwan_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+
+	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+	    ((portdata->dtr_state) ? TIOCM_DTR : 0) |
+	    ((portdata->cts_state) ? TIOCM_CTS : 0) |
+	    ((portdata->dsr_state) ? TIOCM_DSR : 0) |
+	    ((portdata->dcd_state) ? TIOCM_CAR : 0) |
+	    ((portdata->ri_state) ? TIOCM_RNG : 0);
+
+	return value;
+}
+EXPORT_SYMBOL(usb_wwan_tiocmget);
+
+int usb_wwan_tiocmset(struct tty_struct *tty,
+		      unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata;
+
+	portdata = usb_get_serial_port_data(port);
+	intfdata = usb_get_serial_data(port->serial);
+
+	if (!intfdata->use_send_setup)
+		return -EINVAL;
+
+	/* FIXME: what locks portdata fields ? */
+	if (set & TIOCM_RTS)
+		portdata->rts_state = 1;
+	if (set & TIOCM_DTR)
+		portdata->dtr_state = 1;
+
+	if (clear & TIOCM_RTS)
+		portdata->rts_state = 0;
+	if (clear & TIOCM_DTR)
+		portdata->dtr_state = 0;
+	return usb_wwan_send_setup(port);
+}
+EXPORT_SYMBOL(usb_wwan_tiocmset);
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line            = port->minor;
+	tmp.port            = port->port_number;
+	tmp.baud_base       = tty_get_baud_rate(port->port.tty);
+	tmp.close_delay	    = port->port.close_delay / 10;
+	tmp.closing_wait    = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+				 ASYNC_CLOSING_WAIT_NONE :
+				 port->port.closing_wait / 10;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *newinfo)
+{
+	struct serial_struct new_serial;
+	unsigned int closing_wait, close_delay;
+	int retval = 0;
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+		return -EFAULT;
+
+	close_delay = new_serial.close_delay * 10;
+	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+			ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+
+	mutex_lock(&port->port.mutex);
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((close_delay != port->port.close_delay) ||
+		    (closing_wait != port->port.closing_wait))
+			retval = -EPERM;
+		else
+			retval = -EOPNOTSUPP;
+	} else {
+		port->port.close_delay  = close_delay;
+		port->port.closing_wait = closing_wait;
+	}
+
+	mutex_unlock(&port->port.mutex);
+	return retval;
+}
+
+int usb_wwan_ioctl(struct tty_struct *tty,
+		   unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	case TIOCSSERIAL:
+		return set_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	default:
+		break;
+	}
+
+	dev_dbg(&port->dev, "%s arg not supported\n", __func__);
+
+	return -ENOIOCTLCMD;
+}
+EXPORT_SYMBOL(usb_wwan_ioctl);
+
+int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
+		   const unsigned char *buf, int count)
+{
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata;
+	int i;
+	int left, todo;
+	struct urb *this_urb = NULL;	/* spurious */
+	int err;
+	unsigned long flags;
+
+	portdata = usb_get_serial_port_data(port);
+	intfdata = usb_get_serial_data(port->serial);
+
+	dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
+
+	i = 0;
+	left = count;
+	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
+		todo = left;
+		if (todo > OUT_BUFLEN)
+			todo = OUT_BUFLEN;
+
+		this_urb = portdata->out_urbs[i];
+		if (test_and_set_bit(i, &portdata->out_busy)) {
+			if (time_before(jiffies,
+					portdata->tx_start_time[i] + 10 * HZ))
+				continue;
+			usb_unlink_urb(this_urb);
+			continue;
+		}
+		dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
+			usb_pipeendpoint(this_urb->pipe), i);
+
+		err = usb_autopm_get_interface_async(port->serial->interface);
+		if (err < 0) {
+			clear_bit(i, &portdata->out_busy);
+			break;
+		}
+
+		/* send the data */
+		memcpy(this_urb->transfer_buffer, buf, todo);
+		this_urb->transfer_buffer_length = todo;
+
+		spin_lock_irqsave(&intfdata->susp_lock, flags);
+		if (intfdata->suspended) {
+			usb_anchor_urb(this_urb, &portdata->delayed);
+			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+		} else {
+			intfdata->in_flight++;
+			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+			err = usb_submit_urb(this_urb, GFP_ATOMIC);
+			if (err) {
+				dev_err(&port->dev,
+					"%s: submit urb %d failed: %d\n",
+					__func__, i, err);
+				clear_bit(i, &portdata->out_busy);
+				spin_lock_irqsave(&intfdata->susp_lock, flags);
+				intfdata->in_flight--;
+				spin_unlock_irqrestore(&intfdata->susp_lock,
+						       flags);
+				usb_autopm_put_interface_async(port->serial->interface);
+				break;
+			}
+		}
+
+		portdata->tx_start_time[i] = jiffies;
+		buf += todo;
+		left -= todo;
+	}
+
+	count -= left;
+	dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
+	return count;
+}
+EXPORT_SYMBOL(usb_wwan_write);
+
+static void usb_wwan_indat_callback(struct urb *urb)
+{
+	int err;
+	int endpoint;
+	struct usb_serial_port *port;
+	struct device *dev;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+	port = urb->context;
+	dev = &port->dev;
+
+	if (status) {
+		dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
+			__func__, status, endpoint);
+	} else {
+		if (urb->actual_length) {
+			tty_insert_flip_string(&port->port, data,
+					urb->actual_length);
+			tty_flip_buffer_push(&port->port);
+		} else
+			dev_dbg(dev, "%s: empty read urb received\n", __func__);
+	}
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		if (err != -EPERM && err != -ENODEV) {
+			dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
+				__func__, err);
+			/* busy also in error unless we are killed */
+			usb_mark_last_busy(port->serial->dev);
+		}
+	} else {
+		usb_mark_last_busy(port->serial->dev);
+	}
+}
+
+static void usb_wwan_outdat_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata;
+	unsigned long flags;
+	int i;
+
+	port = urb->context;
+	intfdata = usb_get_serial_data(port->serial);
+
+	usb_serial_port_softint(port);
+	usb_autopm_put_interface_async(port->serial->interface);
+	portdata = usb_get_serial_port_data(port);
+	spin_lock_irqsave(&intfdata->susp_lock, flags);
+	intfdata->in_flight--;
+	spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+
+	for (i = 0; i < N_OUT_URB; ++i) {
+		if (portdata->out_urbs[i] == urb) {
+			smp_mb__before_atomic();
+			clear_bit(i, &portdata->out_busy);
+			break;
+		}
+	}
+}
+
+int usb_wwan_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_wwan_port_private *portdata;
+	int i;
+	int data_len = 0;
+	struct urb *this_urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	for (i = 0; i < N_OUT_URB; i++) {
+		this_urb = portdata->out_urbs[i];
+		if (this_urb && !test_bit(i, &portdata->out_busy))
+			data_len += OUT_BUFLEN;
+	}
+
+	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
+	return data_len;
+}
+EXPORT_SYMBOL(usb_wwan_write_room);
+
+int usb_wwan_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_wwan_port_private *portdata;
+	int i;
+	int data_len = 0;
+	struct urb *this_urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	for (i = 0; i < N_OUT_URB; i++) {
+		this_urb = portdata->out_urbs[i];
+		/* FIXME: This locking is insufficient as this_urb may
+		   go unused during the test */
+		if (this_urb && test_bit(i, &portdata->out_busy))
+			data_len += this_urb->transfer_buffer_length;
+	}
+	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
+	return data_len;
+}
+EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
+
+int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata;
+	struct usb_serial *serial = port->serial;
+	int i, err;
+	struct urb *urb;
+
+	portdata = usb_get_serial_port_data(port);
+	intfdata = usb_get_serial_data(serial);
+
+	if (port->interrupt_in_urb) {
+		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (err) {
+			dev_err(&port->dev, "%s: submit int urb failed: %d\n",
+				__func__, err);
+		}
+	}
+
+	/* Start reading from the IN endpoint */
+	for (i = 0; i < N_IN_URB; i++) {
+		urb = portdata->in_urbs[i];
+		if (!urb)
+			continue;
+		err = usb_submit_urb(urb, GFP_KERNEL);
+		if (err) {
+			dev_err(&port->dev,
+				"%s: submit read urb %d failed: %d\n",
+				__func__, i, err);
+		}
+	}
+
+	spin_lock_irq(&intfdata->susp_lock);
+	if (++intfdata->open_ports == 1)
+		serial->interface->needs_remote_wakeup = 1;
+	spin_unlock_irq(&intfdata->susp_lock);
+	/* this balances a get in the generic USB serial code */
+	usb_autopm_put_interface(serial->interface);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_wwan_open);
+
+static void unbusy_queued_urb(struct urb *urb,
+					struct usb_wwan_port_private *portdata)
+{
+	int i;
+
+	for (i = 0; i < N_OUT_URB; i++) {
+		if (urb == portdata->out_urbs[i]) {
+			clear_bit(i, &portdata->out_busy);
+			break;
+		}
+	}
+}
+
+void usb_wwan_close(struct usb_serial_port *port)
+{
+	int i;
+	struct usb_serial *serial = port->serial;
+	struct usb_wwan_port_private *portdata;
+	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+	struct urb *urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	/*
+	 * Need to take susp_lock to make sure port is not already being
+	 * resumed, but no need to hold it due to initialized
+	 */
+	spin_lock_irq(&intfdata->susp_lock);
+	if (--intfdata->open_ports == 0)
+		serial->interface->needs_remote_wakeup = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	for (;;) {
+		urb = usb_get_from_anchor(&portdata->delayed);
+		if (!urb)
+			break;
+		unbusy_queued_urb(urb, portdata);
+		usb_autopm_put_interface_async(serial->interface);
+	}
+
+	for (i = 0; i < N_IN_URB; i++)
+		usb_kill_urb(portdata->in_urbs[i]);
+	for (i = 0; i < N_OUT_URB; i++)
+		usb_kill_urb(portdata->out_urbs[i]);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	usb_autopm_get_interface_no_resume(serial->interface);
+}
+EXPORT_SYMBOL(usb_wwan_close);
+
+static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
+				      int endpoint,
+				      int dir, void *ctx, char *buf, int len,
+				      void (*callback) (struct urb *))
+{
+	struct usb_serial *serial = port->serial;
+	struct urb *urb;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
+	if (!urb)
+		return NULL;
+
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
+			  buf, len, callback, ctx);
+
+	return urb;
+}
+
+int usb_wwan_port_probe(struct usb_serial_port *port)
+{
+	struct usb_wwan_port_private *portdata;
+	struct urb *urb;
+	u8 *buffer;
+	int i;
+
+	if (!port->bulk_in_size || !port->bulk_out_size)
+		return -ENODEV;
+
+	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+	if (!portdata)
+		return -ENOMEM;
+
+	init_usb_anchor(&portdata->delayed);
+
+	for (i = 0; i < N_IN_URB; i++) {
+		buffer = (u8 *)__get_free_page(GFP_KERNEL);
+		if (!buffer)
+			goto bail_out_error;
+		portdata->in_buffer[i] = buffer;
+
+		urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
+						USB_DIR_IN, port,
+						buffer, IN_BUFLEN,
+						usb_wwan_indat_callback);
+		portdata->in_urbs[i] = urb;
+	}
+
+	for (i = 0; i < N_OUT_URB; i++) {
+		buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+		if (!buffer)
+			goto bail_out_error2;
+		portdata->out_buffer[i] = buffer;
+
+		urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
+						USB_DIR_OUT, port,
+						buffer, OUT_BUFLEN,
+						usb_wwan_outdat_callback);
+		portdata->out_urbs[i] = urb;
+	}
+
+	usb_set_serial_port_data(port, portdata);
+
+	return 0;
+
+bail_out_error2:
+	for (i = 0; i < N_OUT_URB; i++) {
+		usb_free_urb(portdata->out_urbs[i]);
+		kfree(portdata->out_buffer[i]);
+	}
+bail_out_error:
+	for (i = 0; i < N_IN_URB; i++) {
+		usb_free_urb(portdata->in_urbs[i]);
+		free_page((unsigned long)portdata->in_buffer[i]);
+	}
+	kfree(portdata);
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
+
+int usb_wwan_port_remove(struct usb_serial_port *port)
+{
+	int i;
+	struct usb_wwan_port_private *portdata;
+
+	portdata = usb_get_serial_port_data(port);
+	usb_set_serial_port_data(port, NULL);
+
+	for (i = 0; i < N_IN_URB; i++) {
+		usb_free_urb(portdata->in_urbs[i]);
+		free_page((unsigned long)portdata->in_buffer[i]);
+	}
+	for (i = 0; i < N_OUT_URB; i++) {
+		usb_free_urb(portdata->out_urbs[i]);
+		kfree(portdata->out_buffer[i]);
+	}
+
+	kfree(portdata);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_wwan_port_remove);
+
+#ifdef CONFIG_PM
+static void stop_urbs(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_port_private *portdata;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+		for (j = 0; j < N_IN_URB; j++)
+			usb_kill_urb(portdata->in_urbs[j]);
+		for (j = 0; j < N_OUT_URB; j++)
+			usb_kill_urb(portdata->out_urbs[j]);
+		usb_kill_urb(port->interrupt_in_urb);
+	}
+}
+
+int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
+{
+	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+
+	spin_lock_irq(&intfdata->susp_lock);
+	if (PMSG_IS_AUTO(message)) {
+		if (intfdata->in_flight) {
+			spin_unlock_irq(&intfdata->susp_lock);
+			return -EBUSY;
+		}
+	}
+	intfdata->suspended = 1;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	stop_urbs(serial);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_wwan_suspend);
+
+/* Caller must hold susp_lock. */
+static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
+	struct usb_wwan_port_private *portdata;
+	struct urb *urb;
+	int err_count = 0;
+	int err;
+
+	portdata = usb_get_serial_port_data(port);
+
+	for (;;) {
+		urb = usb_get_from_anchor(&portdata->delayed);
+		if (!urb)
+			break;
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err) {
+			dev_err(&port->dev, "%s: submit urb failed: %d\n",
+					__func__, err);
+			err_count++;
+			unbusy_queued_urb(urb, portdata);
+			usb_autopm_put_interface_async(serial->interface);
+			continue;
+		}
+		data->in_flight++;
+	}
+
+	if (err_count)
+		return -EIO;
+
+	return 0;
+}
+
+int usb_wwan_resume(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+	struct usb_wwan_port_private *portdata;
+	struct urb *urb;
+	int err;
+	int err_count = 0;
+
+	spin_lock_irq(&intfdata->susp_lock);
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+
+		if (!tty_port_initialized(&port->port))
+			continue;
+
+		portdata = usb_get_serial_port_data(port);
+
+		if (port->interrupt_in_urb) {
+			err = usb_submit_urb(port->interrupt_in_urb,
+					GFP_ATOMIC);
+			if (err) {
+				dev_err(&port->dev,
+					"%s: submit int urb failed: %d\n",
+					__func__, err);
+				err_count++;
+			}
+		}
+
+		err = usb_wwan_submit_delayed_urbs(port);
+		if (err)
+			err_count++;
+
+		for (j = 0; j < N_IN_URB; j++) {
+			urb = portdata->in_urbs[j];
+			err = usb_submit_urb(urb, GFP_ATOMIC);
+			if (err < 0) {
+				dev_err(&port->dev,
+					"%s: submit read urb %d failed: %d\n",
+					__func__, i, err);
+				err_count++;
+			}
+		}
+	}
+	intfdata->suspended = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	if (err_count)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_wwan_resume);
+#endif
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
new file mode 100644
index 0000000..8ddbecc
--- /dev/null
+++ b/drivers/usb/serial/visor.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB HandSpring Visor, Palm m50x, and Sony Clie driver
+ * (supports all of the Palm OS USB devices)
+ *
+ *	Copyright (C) 1999 - 2004
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/usb/cdc.h>
+#include "visor.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC "USB HandSpring Visor / Palm OS driver"
+
+/* function prototypes for a handspring visor */
+static int  visor_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void visor_close(struct usb_serial_port *port);
+static int  visor_probe(struct usb_serial *serial,
+					const struct usb_device_id *id);
+static int  visor_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds);
+static int  clie_5_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds);
+static void visor_read_int_callback(struct urb *urb);
+static int  clie_3_5_startup(struct usb_serial *serial);
+static int palm_os_3_probe(struct usb_serial *serial,
+					const struct usb_device_id *id);
+static int palm_os_4_probe(struct usb_serial *serial,
+					const struct usb_device_id *id);
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_3_probe },
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE_INTERFACE_CLASS(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID, 0xff),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ }					/* Terminating entry */
+};
+
+static const struct usb_device_id clie_id_5_table[] = {
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID),
+		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
+	{ }					/* Terminating entry */
+};
+
+static const struct usb_device_id clie_id_3_5_table[] = {
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
+	{ }					/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
+	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) },
+	{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
+	{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) },
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) },
+	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
+	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) },
+	{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID) },
+	{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) },
+	{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) },
+	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) },
+	{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+/* All of the device info needed for the Handspring Visor,
+   and Palm 4.0 devices */
+static struct usb_serial_driver handspring_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"visor",
+	},
+	.description =		"Handspring Visor / Palm OS",
+	.id_table =		id_table,
+	.num_ports =		2,
+	.bulk_out_size =	256,
+	.open =			visor_open,
+	.close =		visor_close,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.probe =		visor_probe,
+	.calc_num_ports =	visor_calc_num_ports,
+	.read_int_callback =	visor_read_int_callback,
+};
+
+/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
+static struct usb_serial_driver clie_5_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"clie_5",
+	},
+	.description =		"Sony Clie 5.0",
+	.id_table =		clie_id_5_table,
+	.num_ports =		2,
+	.num_bulk_out =		2,
+	.bulk_out_size =	256,
+	.open =			visor_open,
+	.close =		visor_close,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.probe =		visor_probe,
+	.calc_num_ports =	clie_5_calc_num_ports,
+	.read_int_callback =	visor_read_int_callback,
+};
+
+/* device info for the Sony Clie OS version 3.5 */
+static struct usb_serial_driver clie_3_5_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"clie_3.5",
+	},
+	.description =		"Sony Clie 3.5",
+	.id_table =		clie_id_3_5_table,
+	.num_ports =		1,
+	.bulk_out_size =	256,
+	.open =			visor_open,
+	.close =		visor_close,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.attach =		clie_3_5_startup,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&handspring_device, &clie_5_device, &clie_3_5_device, NULL
+};
+
+/******************************************************************************
+ * Handspring Visor specific driver functions
+ ******************************************************************************/
+static int visor_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result = 0;
+
+	if (!port->read_urb) {
+		/* this is needed for some brain dead Sony devices */
+		dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n");
+		return -ENODEV;
+	}
+
+	/* Start reading from the device */
+	result = usb_serial_generic_open(tty, port);
+	if (result)
+		goto exit;
+
+	if (port->interrupt_in_urb) {
+		dev_dbg(&port->dev, "adding interrupt input for treo\n");
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (result)
+			dev_err(&port->dev,
+			    "%s - failed submitting interrupt urb, error %d\n",
+							__func__, result);
+	}
+exit:
+	return result;
+}
+
+
+static void visor_close(struct usb_serial_port *port)
+{
+	unsigned char *transfer_buffer;
+
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	transfer_buffer = kmalloc(0x12, GFP_KERNEL);
+	if (!transfer_buffer)
+		return;
+	usb_control_msg(port->serial->dev,
+					 usb_rcvctrlpipe(port->serial->dev, 0),
+					 VISOR_CLOSE_NOTIFICATION, 0xc2,
+					 0x0000, 0x0000,
+					 transfer_buffer, 0x12, 300);
+	kfree(transfer_buffer);
+}
+
+static void visor_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
+	int result;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	/*
+	 * This information is still unknown what it can be used for.
+	 * If anyone has an idea, please let the author know...
+	 *
+	 * Rumor has it this endpoint is used to notify when data
+	 * is ready to be read from the bulk ones.
+	 */
+	usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
+			      urb->transfer_buffer);
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->dev,
+				"%s - Error %d submitting interrupt urb\n",
+							__func__, result);
+}
+
+static int palm_os_3_probe(struct usb_serial *serial,
+						const struct usb_device_id *id)
+{
+	struct device *dev = &serial->dev->dev;
+	struct visor_connection_info *connection_info;
+	unsigned char *transfer_buffer;
+	char *string;
+	int retval = 0;
+	int i;
+	int num_ports = 0;
+
+	transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	/* send a get connection info request */
+	retval = usb_control_msg(serial->dev,
+				  usb_rcvctrlpipe(serial->dev, 0),
+				  VISOR_GET_CONNECTION_INFORMATION,
+				  0xc2, 0x0000, 0x0000, transfer_buffer,
+				  sizeof(*connection_info), 300);
+	if (retval < 0) {
+		dev_err(dev, "%s - error %d getting connection information\n",
+			__func__, retval);
+		goto exit;
+	}
+
+	if (retval != sizeof(*connection_info)) {
+		dev_err(dev, "Invalid connection information received from device\n");
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	connection_info = (struct visor_connection_info *)transfer_buffer;
+
+	num_ports = le16_to_cpu(connection_info->num_ports);
+
+	/* Handle devices that report invalid stuff here. */
+	if (num_ports == 0 || num_ports > 2) {
+		dev_warn(dev, "%s: No valid connect info available\n",
+			serial->type->description);
+		num_ports = 2;
+	}
+
+	for (i = 0; i < num_ports; ++i) {
+		switch (connection_info->connections[i].port_function_id) {
+		case VISOR_FUNCTION_GENERIC:
+			string = "Generic";
+			break;
+		case VISOR_FUNCTION_DEBUGGER:
+			string = "Debugger";
+			break;
+		case VISOR_FUNCTION_HOTSYNC:
+			string = "HotSync";
+			break;
+		case VISOR_FUNCTION_CONSOLE:
+			string = "Console";
+			break;
+		case VISOR_FUNCTION_REMOTE_FILE_SYS:
+			string = "Remote File System";
+			break;
+		default:
+			string = "unknown";
+			break;
+		}
+		dev_info(dev, "%s: port %d, is for %s use\n",
+			serial->type->description,
+			connection_info->connections[i].port, string);
+	}
+	dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
+		num_ports);
+
+	/*
+	 * save off our num_ports info so that we can use it in the
+	 * calc_num_ports callback
+	 */
+	usb_set_serial_data(serial, (void *)(long)num_ports);
+
+	/* ask for the number of bytes available, but ignore the
+	   response as it is broken */
+	retval = usb_control_msg(serial->dev,
+				  usb_rcvctrlpipe(serial->dev, 0),
+				  VISOR_REQUEST_BYTES_AVAILABLE,
+				  0xc2, 0x0000, 0x0005, transfer_buffer,
+				  0x02, 300);
+	if (retval < 0)
+		dev_err(dev, "%s - error %d getting bytes available request\n",
+			__func__, retval);
+	retval = 0;
+
+exit:
+	kfree(transfer_buffer);
+
+	return retval;
+}
+
+static int palm_os_4_probe(struct usb_serial *serial,
+						const struct usb_device_id *id)
+{
+	struct device *dev = &serial->dev->dev;
+	struct palm_ext_connection_info *connection_info;
+	unsigned char *transfer_buffer;
+	int retval;
+
+	transfer_buffer =  kmalloc(sizeof(*connection_info), GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	retval = usb_control_msg(serial->dev,
+				  usb_rcvctrlpipe(serial->dev, 0),
+				  PALM_GET_EXT_CONNECTION_INFORMATION,
+				  0xc2, 0x0000, 0x0000, transfer_buffer,
+				  sizeof(*connection_info), 300);
+	if (retval < 0)
+		dev_err(dev, "%s - error %d getting connection info\n",
+			__func__, retval);
+	else
+		usb_serial_debug_data(dev, __func__, retval, transfer_buffer);
+
+	kfree(transfer_buffer);
+	return 0;
+}
+
+
+static int visor_probe(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	int retval = 0;
+	int (*startup)(struct usb_serial *serial,
+					const struct usb_device_id *id);
+
+	/*
+	 * some Samsung Android phones in modem mode have the same ID
+	 * as SPH-I500, but they are ACM devices, so dont bind to them
+	 */
+	if (id->idVendor == SAMSUNG_VENDOR_ID &&
+		id->idProduct == SAMSUNG_SPH_I500_ID &&
+		serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM &&
+		serial->dev->descriptor.bDeviceSubClass ==
+			USB_CDC_SUBCLASS_ACM)
+		return -ENODEV;
+
+	if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
+		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
+			serial->dev->actconfig->desc.bConfigurationValue);
+		return -ENODEV;
+	}
+
+	if (id->driver_info) {
+		startup = (void *)id->driver_info;
+		retval = startup(serial, id);
+	}
+
+	return retval;
+}
+
+static int visor_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	unsigned int vid = le16_to_cpu(serial->dev->descriptor.idVendor);
+	int num_ports = (int)(long)(usb_get_serial_data(serial));
+
+	if (num_ports)
+		usb_set_serial_data(serial, NULL);
+
+	/*
+	 * Only swap the bulk endpoints for the Handspring devices with
+	 * interrupt in endpoints, which for now are the Treo devices.
+	 */
+	if (!(vid == HANDSPRING_VENDOR_ID || vid == KYOCERA_VENDOR_ID) ||
+			epds->num_interrupt_in == 0)
+		goto out;
+
+	if (epds->num_bulk_in < 2 || epds->num_interrupt_in < 2) {
+		dev_err(&serial->interface->dev, "missing endpoints\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * It appears that Treos and Kyoceras want to use the
+	 * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint,
+	 * so let's swap the 1st and 2nd bulk in and interrupt endpoints.
+	 * Note that swapping the bulk out endpoints would break lots of
+	 * apps that want to communicate on the second port.
+	 */
+	swap(epds->bulk_in[0], epds->bulk_in[1]);
+	swap(epds->interrupt_in[0], epds->interrupt_in[1]);
+out:
+	return num_ports;
+}
+
+static int clie_5_calc_num_ports(struct usb_serial *serial,
+					struct usb_serial_endpoints *epds)
+{
+	/*
+	 * TH55 registers 2 ports.
+	 * Communication in from the UX50/TH55 uses the first bulk-in
+	 * endpoint, while communication out to the UX50/TH55 uses the second
+	 * bulk-out endpoint.
+	 */
+
+	/*
+	 * FIXME: Should we swap the descriptors instead of using the same
+	 *        bulk-out endpoint for both ports?
+	 */
+	epds->bulk_out[0] = epds->bulk_out[1];
+
+	return serial->type->num_ports;
+}
+
+static int clie_3_5_startup(struct usb_serial *serial)
+{
+	struct device *dev = &serial->dev->dev;
+	int result;
+	u8 *data;
+
+	data = kmalloc(1, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/*
+	 * Note that PEG-300 series devices expect the following two calls.
+	 */
+
+	/* get the config number */
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
+				  0, 0, data, 1, 3000);
+	if (result < 0) {
+		dev_err(dev, "%s: get config number failed: %d\n",
+							__func__, result);
+		goto out;
+	}
+	if (result != 1) {
+		dev_err(dev, "%s: get config number bad return length: %d\n",
+							__func__, result);
+		result = -EIO;
+		goto out;
+	}
+
+	/* get the interface number */
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_INTERFACE,
+				  USB_DIR_IN | USB_RECIP_INTERFACE,
+				  0, 0, data, 1, 3000);
+	if (result < 0) {
+		dev_err(dev, "%s: get interface number failed: %d\n",
+							__func__, result);
+		goto out;
+	}
+	if (result != 1) {
+		dev_err(dev,
+			"%s: get interface number bad return length: %d\n",
+							__func__, result);
+		result = -EIO;
+		goto out;
+	}
+
+	result = 0;
+out:
+	kfree(data);
+
+	return result;
+}
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h
new file mode 100644
index 0000000..fe29024
--- /dev/null
+++ b/drivers/usb/serial/visor.h
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB HandSpring Visor driver
+ *
+ *	Copyright (C) 1999 - 2003
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver.
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_VISOR_H
+#define __LINUX_USB_SERIAL_VISOR_H
+
+
+#define HANDSPRING_VENDOR_ID		0x082d
+#define HANDSPRING_VISOR_ID		0x0100
+#define HANDSPRING_TREO_ID		0x0200
+#define HANDSPRING_TREO600_ID		0x0300
+
+#define PALM_VENDOR_ID			0x0830
+#define PALM_M500_ID			0x0001
+#define PALM_M505_ID			0x0002
+#define PALM_M515_ID			0x0003
+#define PALM_I705_ID			0x0020
+#define PALM_M125_ID			0x0040
+#define PALM_M130_ID			0x0050
+#define PALM_TUNGSTEN_T_ID		0x0060
+#define PALM_TREO_650			0x0061
+#define PALM_TUNGSTEN_Z_ID		0x0031
+#define PALM_ZIRE_ID			0x0070
+#define PALM_M100_ID			0x0080
+
+#define GSPDA_VENDOR_ID		0x115e
+#define GSPDA_XPLORE_M68_ID		0xf100
+
+#define SONY_VENDOR_ID			0x054C
+#define SONY_CLIE_3_5_ID		0x0038
+#define SONY_CLIE_4_0_ID		0x0066
+#define SONY_CLIE_S360_ID		0x0095
+#define SONY_CLIE_4_1_ID		0x009A
+#define SONY_CLIE_NX60_ID		0x00DA
+#define SONY_CLIE_NZ90V_ID		0x00E9
+#define SONY_CLIE_UX50_ID		0x0144
+#define SONY_CLIE_TJ25_ID		0x0169
+
+#define ACER_VENDOR_ID			0x0502
+#define ACER_S10_ID			0x0001
+
+#define SAMSUNG_VENDOR_ID		0x04E8
+#define SAMSUNG_SCH_I330_ID		0x8001
+#define SAMSUNG_SPH_I500_ID		0x6601
+
+#define TAPWAVE_VENDOR_ID		0x12EF
+#define TAPWAVE_ZODIAC_ID		0x0100
+
+#define GARMIN_VENDOR_ID		0x091E
+#define GARMIN_IQUE_3600_ID		0x0004
+
+#define ACEECA_VENDOR_ID		0x4766
+#define ACEECA_MEZ1000_ID		0x0001
+
+#define KYOCERA_VENDOR_ID		0x0C88
+#define KYOCERA_7135_ID			0x0021
+
+#define FOSSIL_VENDOR_ID		0x0E67
+#define FOSSIL_ABACUS_ID		0x0002
+
+/****************************************************************************
+ * Handspring Visor Vendor specific request codes (bRequest values)
+ * A big thank you to Handspring for providing the following information.
+ * If anyone wants the original file where these values and structures came
+ * from, send email to <greg@kroah.com>.
+ ****************************************************************************/
+
+/****************************************************************************
+ * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transferred to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ ****************************************************************************/
+#define VISOR_REQUEST_BYTES_AVAILABLE		0x01
+
+/****************************************************************************
+ * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ ****************************************************************************/
+#define VISOR_CLOSE_NOTIFICATION		0x02
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ ****************************************************************************/
+#define VISOR_GET_CONNECTION_INFORMATION	0x03
+
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ ****************************************************************************/
+struct visor_connection_info {
+	__le16	num_ports;
+	struct {
+		__u8	port_function_id;
+		__u8	port;
+	} connections[2];
+};
+
+
+/* struct visor_connection_info.connection[x].port defines: */
+#define VISOR_ENDPOINT_1		0x01
+#define VISOR_ENDPOINT_2		0x02
+
+/* struct visor_connection_info.connection[x].port_function_id defines: */
+#define VISOR_FUNCTION_GENERIC		0x00
+#define VISOR_FUNCTION_DEBUGGER		0x01
+#define VISOR_FUNCTION_HOTSYNC		0x02
+#define VISOR_FUNCTION_CONSOLE		0x03
+#define VISOR_FUNCTION_REMOTE_FILE_SYS	0x04
+
+
+/****************************************************************************
+ * PALM_GET_SOME_UNKNOWN_INFORMATION is sent by the host during enumeration to
+ * get some information from the M series devices, that is currently unknown.
+ ****************************************************************************/
+#define PALM_GET_EXT_CONNECTION_INFORMATION	0x04
+
+/**
+ * struct palm_ext_connection_info - return data from a PALM_GET_EXT_CONNECTION_INFORMATION request
+ * @num_ports: maximum number of functions/connections in use
+ * @endpoint_numbers_different: will be 1 if in and out endpoints numbers are
+ *	different, otherwise it is 0.  If value is 1, then
+ *	connections.end_point_info is non-zero.  If value is 0, then
+ *	connections.port contains the endpoint number, which is the same for in
+ *	and out.
+ * @port_function_id: contains the creator id of the application that opened
+ *	this connection.
+ * @port: contains the in/out endpoint number.  Is 0 if in and out endpoint
+ *	numbers are different.
+ * @end_point_info: high nubbe is in endpoint and low nibble will indicate out
+ *	endpoint.  Is 0 if in and out endpoints are the same.
+ *
+ * The maximum number of connections currently supported is 2
+ */
+struct palm_ext_connection_info {
+	__u8 num_ports;
+	__u8 endpoint_numbers_different;
+	__le16 reserved1;
+	struct {
+		__u32 port_function_id;
+		__u8 port;
+		__u8 end_point_info;
+		__le16 reserved;
+	} connections[2];
+};
+
+#endif
+
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
new file mode 100644
index 0000000..1c7b46a
--- /dev/null
+++ b/drivers/usb/serial/whiteheat.c
@@ -0,0 +1,838 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *	Copyright (C) 2002
+ *	    Connect Tech Inc.
+ *
+ *	Copyright (C) 1999 - 2001
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/termbits.h>
+#include <linux/usb.h>
+#include <linux/serial_reg.h>
+#include <linux/serial.h>
+#include <linux/usb/serial.h>
+#include <linux/usb/ezusb.h>
+#include "whiteheat.h"			/* WhiteHEAT specific commands */
+
+#ifndef CMSPAR
+#define CMSPAR 0
+#endif
+
+/*
+ * Version Information
+ */
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
+#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
+
+#define CONNECT_TECH_VENDOR_ID		0x0710
+#define CONNECT_TECH_FAKE_WHITE_HEAT_ID	0x0001
+#define CONNECT_TECH_WHITE_HEAT_ID	0x8001
+
+/*
+   ID tables for whiteheat are unusual, because we want to different
+   things for different versions of the device.  Eventually, this
+   will be doable from a single table.  But, for now, we define two
+   separate ID tables, and then a third table that combines them
+   just for the purpose of exporting the autoloading information.
+*/
+static const struct usb_device_id id_table_std[] = {
+	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_prerenumeration[] = {
+	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
+	{ }						/* Terminating entry */
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
+	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+
+/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
+static int  whiteheat_firmware_download(struct usb_serial *serial,
+					const struct usb_device_id *id);
+static int  whiteheat_firmware_attach(struct usb_serial *serial);
+
+/* function prototypes for the Connect Tech WhiteHEAT serial converter */
+static int  whiteheat_attach(struct usb_serial *serial);
+static void whiteheat_release(struct usb_serial *serial);
+static int  whiteheat_port_probe(struct usb_serial_port *port);
+static int  whiteheat_port_remove(struct usb_serial_port *port);
+static int  whiteheat_open(struct tty_struct *tty,
+			struct usb_serial_port *port);
+static void whiteheat_close(struct usb_serial_port *port);
+static int  whiteheat_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+static void whiteheat_set_termios(struct tty_struct *tty,
+			struct usb_serial_port *port, struct ktermios *old);
+static int  whiteheat_tiocmget(struct tty_struct *tty);
+static int  whiteheat_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear);
+static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
+
+static struct usb_serial_driver whiteheat_fake_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"whiteheatnofirm",
+	},
+	.description =		"Connect Tech - WhiteHEAT - (prerenumeration)",
+	.id_table =		id_table_prerenumeration,
+	.num_ports =		1,
+	.probe =		whiteheat_firmware_download,
+	.attach =		whiteheat_firmware_attach,
+};
+
+static struct usb_serial_driver whiteheat_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"whiteheat",
+	},
+	.description =		"Connect Tech - WhiteHEAT",
+	.id_table =		id_table_std,
+	.num_ports =		4,
+	.num_bulk_in =		5,
+	.num_bulk_out =		5,
+	.attach =		whiteheat_attach,
+	.release =		whiteheat_release,
+	.port_probe =		whiteheat_port_probe,
+	.port_remove =		whiteheat_port_remove,
+	.open =			whiteheat_open,
+	.close =		whiteheat_close,
+	.ioctl =		whiteheat_ioctl,
+	.set_termios =		whiteheat_set_termios,
+	.break_ctl =		whiteheat_break_ctl,
+	.tiocmget =		whiteheat_tiocmget,
+	.tiocmset =		whiteheat_tiocmset,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&whiteheat_fake_device, &whiteheat_device, NULL
+};
+
+struct whiteheat_command_private {
+	struct mutex		mutex;
+	__u8			port_running;
+	__u8			command_finished;
+	wait_queue_head_t	wait_command; /* for handling sleeping whilst
+						 waiting for a command to
+						 finish */
+	__u8			result_buffer[64];
+};
+
+struct whiteheat_private {
+	__u8			mcr;		/* FIXME: no locking on mcr */
+};
+
+
+/* local function prototypes */
+static int start_command_port(struct usb_serial *serial);
+static void stop_command_port(struct usb_serial *serial);
+static void command_port_write_callback(struct urb *urb);
+static void command_port_read_callback(struct urb *urb);
+
+static int firm_send_command(struct usb_serial_port *port, __u8 command,
+						__u8 *data, __u8 datasize);
+static int firm_open(struct usb_serial_port *port);
+static int firm_close(struct usb_serial_port *port);
+static void firm_setup_port(struct tty_struct *tty);
+static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
+static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
+static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
+static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
+static int firm_get_dtr_rts(struct usb_serial_port *port);
+static int firm_report_tx_done(struct usb_serial_port *port);
+
+
+#define COMMAND_PORT		4
+#define COMMAND_TIMEOUT		(2*HZ)	/* 2 second timeout for a command */
+#define	COMMAND_TIMEOUT_MS	2000
+#define CLOSING_DELAY		(30 * HZ)
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat prerenumeration driver functions
+ *****************************************************************************/
+
+/* steps to download the firmware to the WhiteHEAT device:
+ - hold the reset (by writing to the reset bit of the CPUCS register)
+ - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
+ - release the reset (by writing to the CPUCS register)
+ - download the WH.HEX file for all addresses greater than 0x1b3f using
+   VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
+ - hold the reset
+ - download the WH.HEX file for all addresses less than 0x1b40 using
+   VENDOR_REQUEST_ANCHOR_LOAD
+ - release the reset
+ - device renumerated itself and comes up as new device id with all
+   firmware download completed.
+*/
+static int whiteheat_firmware_download(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	int response;
+
+	response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw");
+	if (response >= 0) {
+		response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw");
+		if (response >= 0)
+			return 0;
+	}
+	return -ENOENT;
+}
+
+
+static int whiteheat_firmware_attach(struct usb_serial *serial)
+{
+	/* We want this device to fail to have a driver assigned to it */
+	return 1;
+}
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat serial driver functions
+ *****************************************************************************/
+
+static int whiteheat_attach(struct usb_serial *serial)
+{
+	struct usb_serial_port *command_port;
+	struct whiteheat_command_private *command_info;
+	struct whiteheat_hw_info *hw_info;
+	int pipe;
+	int ret;
+	int alen;
+	__u8 *command;
+	__u8 *result;
+
+	command_port = serial->port[COMMAND_PORT];
+
+	pipe = usb_sndbulkpipe(serial->dev,
+			command_port->bulk_out_endpointAddress);
+	command = kmalloc(2, GFP_KERNEL);
+	if (!command)
+		goto no_command_buffer;
+	command[0] = WHITEHEAT_GET_HW_INFO;
+	command[1] = 0;
+
+	result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
+	if (!result)
+		goto no_result_buffer;
+	/*
+	 * When the module is reloaded the firmware is still there and
+	 * the endpoints are still in the usb core unchanged. This is the
+	 * unlinking bug in disguise. Same for the call below.
+	 */
+	usb_clear_halt(serial->dev, pipe);
+	ret = usb_bulk_msg(serial->dev, pipe, command, 2,
+						&alen, COMMAND_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n",
+			serial->type->description, ret);
+		goto no_firmware;
+	} else if (alen != 2) {
+		dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n",
+			serial->type->description, alen);
+		goto no_firmware;
+	}
+
+	pipe = usb_rcvbulkpipe(serial->dev,
+				command_port->bulk_in_endpointAddress);
+	/* See the comment on the usb_clear_halt() above */
+	usb_clear_halt(serial->dev, pipe);
+	ret = usb_bulk_msg(serial->dev, pipe, result,
+			sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n",
+			serial->type->description, ret);
+		goto no_firmware;
+	} else if (alen != sizeof(*hw_info) + 1) {
+		dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n",
+			serial->type->description, alen);
+		goto no_firmware;
+	} else if (result[0] != command[0]) {
+		dev_err(&serial->dev->dev, "%s: Command failed [%d]\n",
+			serial->type->description, result[0]);
+		goto no_firmware;
+	}
+
+	hw_info = (struct whiteheat_hw_info *)&result[1];
+
+	dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n",
+		 serial->type->description,
+		 hw_info->sw_major_rev, hw_info->sw_minor_rev);
+
+	command_info = kmalloc(sizeof(struct whiteheat_command_private),
+								GFP_KERNEL);
+	if (!command_info)
+		goto no_command_private;
+
+	mutex_init(&command_info->mutex);
+	command_info->port_running = 0;
+	init_waitqueue_head(&command_info->wait_command);
+	usb_set_serial_port_data(command_port, command_info);
+	command_port->write_urb->complete = command_port_write_callback;
+	command_port->read_urb->complete = command_port_read_callback;
+	kfree(result);
+	kfree(command);
+
+	return 0;
+
+no_firmware:
+	/* Firmware likely not running */
+	dev_err(&serial->dev->dev,
+		"%s: Unable to retrieve firmware version, try replugging\n",
+		serial->type->description);
+	dev_err(&serial->dev->dev,
+		"%s: If the firmware is not running (status led not blinking)\n",
+		serial->type->description);
+	dev_err(&serial->dev->dev,
+		"%s: please contact support@connecttech.com\n",
+		serial->type->description);
+	kfree(result);
+	kfree(command);
+	return -ENODEV;
+
+no_command_private:
+	kfree(result);
+no_result_buffer:
+	kfree(command);
+no_command_buffer:
+	return -ENOMEM;
+}
+
+static void whiteheat_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *command_port;
+
+	/* free up our private data for our command port */
+	command_port = serial->port[COMMAND_PORT];
+	kfree(usb_get_serial_port_data(command_port));
+}
+
+static int whiteheat_port_probe(struct usb_serial_port *port)
+{
+	struct whiteheat_private *info;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	usb_set_serial_port_data(port, info);
+
+	return 0;
+}
+
+static int whiteheat_port_remove(struct usb_serial_port *port)
+{
+	struct whiteheat_private *info;
+
+	info = usb_get_serial_port_data(port);
+	kfree(info);
+
+	return 0;
+}
+
+static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int retval;
+
+	retval = start_command_port(port->serial);
+	if (retval)
+		goto exit;
+
+	/* send an open port command */
+	retval = firm_open(port);
+	if (retval) {
+		stop_command_port(port->serial);
+		goto exit;
+	}
+
+	retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
+	if (retval) {
+		firm_close(port);
+		stop_command_port(port->serial);
+		goto exit;
+	}
+
+	if (tty)
+		firm_setup_port(tty);
+
+	/* Work around HCD bugs */
+	usb_clear_halt(port->serial->dev, port->read_urb->pipe);
+	usb_clear_halt(port->serial->dev, port->write_urb->pipe);
+
+	retval = usb_serial_generic_open(tty, port);
+	if (retval) {
+		firm_close(port);
+		stop_command_port(port->serial);
+		goto exit;
+	}
+exit:
+	return retval;
+}
+
+
+static void whiteheat_close(struct usb_serial_port *port)
+{
+	firm_report_tx_done(port);
+	firm_close(port);
+
+	usb_serial_generic_close(port);
+
+	stop_command_port(port->serial);
+}
+
+static int whiteheat_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	unsigned int modem_signals = 0;
+
+	firm_get_dtr_rts(port);
+	if (info->mcr & UART_MCR_DTR)
+		modem_signals |= TIOCM_DTR;
+	if (info->mcr & UART_MCR_RTS)
+		modem_signals |= TIOCM_RTS;
+
+	return modem_signals;
+}
+
+static int whiteheat_tiocmset(struct tty_struct *tty,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+
+	if (set & TIOCM_RTS)
+		info->mcr |= UART_MCR_RTS;
+	if (set & TIOCM_DTR)
+		info->mcr |= UART_MCR_DTR;
+
+	if (clear & TIOCM_RTS)
+		info->mcr &= ~UART_MCR_RTS;
+	if (clear & TIOCM_DTR)
+		info->mcr &= ~UART_MCR_DTR;
+
+	firm_set_dtr(port, info->mcr & UART_MCR_DTR);
+	firm_set_rts(port, info->mcr & UART_MCR_RTS);
+	return 0;
+}
+
+
+static int whiteheat_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct serial_struct serstruct;
+	void __user *user_arg = (void __user *)arg;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		memset(&serstruct, 0, sizeof(serstruct));
+		serstruct.type = PORT_16654;
+		serstruct.line = port->minor;
+		serstruct.port = port->port_number;
+		serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
+		serstruct.custom_divisor = 0;
+		serstruct.baud_base = 460800;
+		serstruct.close_delay = CLOSING_DELAY;
+		serstruct.closing_wait = CLOSING_DELAY;
+
+		if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
+			return -EFAULT;
+		break;
+	default:
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+
+static void whiteheat_set_termios(struct tty_struct *tty,
+	struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	firm_setup_port(tty);
+}
+
+static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	firm_set_break(port, break_state);
+}
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat callback routines
+ *****************************************************************************/
+static void command_port_write_callback(struct urb *urb)
+{
+	int status = urb->status;
+
+	if (status) {
+		dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status);
+		return;
+	}
+}
+
+
+static void command_port_read_callback(struct urb *urb)
+{
+	struct usb_serial_port *command_port = urb->context;
+	struct whiteheat_command_private *command_info;
+	int status = urb->status;
+	unsigned char *data = urb->transfer_buffer;
+	int result;
+
+	command_info = usb_get_serial_port_data(command_port);
+	if (!command_info) {
+		dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
+		return;
+	}
+	if (!urb->actual_length) {
+		dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__);
+		return;
+	}
+	if (status) {
+		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
+		if (status != -ENOENT)
+			command_info->command_finished = WHITEHEAT_CMD_FAILURE;
+		wake_up(&command_info->wait_command);
+		return;
+	}
+
+	usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data);
+
+	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
+		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
+		wake_up(&command_info->wait_command);
+	} else if (data[0] == WHITEHEAT_CMD_FAILURE) {
+		command_info->command_finished = WHITEHEAT_CMD_FAILURE;
+		wake_up(&command_info->wait_command);
+	} else if (data[0] == WHITEHEAT_EVENT) {
+		/* These are unsolicited reports from the firmware, hence no
+		   waiting command to wakeup */
+		dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
+	} else if ((data[0] == WHITEHEAT_GET_DTR_RTS) &&
+		(urb->actual_length - 1 <= sizeof(command_info->result_buffer))) {
+		memcpy(command_info->result_buffer, &data[1],
+						urb->actual_length - 1);
+		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
+		wake_up(&command_info->wait_command);
+	} else
+		dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__);
+
+	/* Continue trying to always read */
+	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
+	if (result)
+		dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n",
+			__func__, result);
+}
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat firmware interface
+ *****************************************************************************/
+static int firm_send_command(struct usb_serial_port *port, __u8 command,
+						__u8 *data, __u8 datasize)
+{
+	struct usb_serial_port *command_port;
+	struct whiteheat_command_private *command_info;
+	struct whiteheat_private *info;
+	struct device *dev = &port->dev;
+	__u8 *transfer_buffer;
+	int retval = 0;
+	int t;
+
+	dev_dbg(dev, "%s - command %d\n", __func__, command);
+
+	command_port = port->serial->port[COMMAND_PORT];
+	command_info = usb_get_serial_port_data(command_port);
+	mutex_lock(&command_info->mutex);
+	command_info->command_finished = false;
+
+	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
+	transfer_buffer[0] = command;
+	memcpy(&transfer_buffer[1], data, datasize);
+	command_port->write_urb->transfer_buffer_length = datasize + 1;
+	retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
+	if (retval) {
+		dev_dbg(dev, "%s - submit urb failed\n", __func__);
+		goto exit;
+	}
+
+	/* wait for the command to complete */
+	t = wait_event_timeout(command_info->wait_command,
+		(bool)command_info->command_finished, COMMAND_TIMEOUT);
+	if (!t)
+		usb_kill_urb(command_port->write_urb);
+
+	if (command_info->command_finished == false) {
+		dev_dbg(dev, "%s - command timed out.\n", __func__);
+		retval = -ETIMEDOUT;
+		goto exit;
+	}
+
+	if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
+		dev_dbg(dev, "%s - command failed.\n", __func__);
+		retval = -EIO;
+		goto exit;
+	}
+
+	if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
+		dev_dbg(dev, "%s - command completed.\n", __func__);
+		switch (command) {
+		case WHITEHEAT_GET_DTR_RTS:
+			info = usb_get_serial_port_data(port);
+			memcpy(&info->mcr, command_info->result_buffer,
+					sizeof(struct whiteheat_dr_info));
+				break;
+		}
+	}
+exit:
+	mutex_unlock(&command_info->mutex);
+	return retval;
+}
+
+
+static int firm_open(struct usb_serial_port *port)
+{
+	struct whiteheat_simple open_command;
+
+	open_command.port = port->port_number + 1;
+	return firm_send_command(port, WHITEHEAT_OPEN,
+		(__u8 *)&open_command, sizeof(open_command));
+}
+
+
+static int firm_close(struct usb_serial_port *port)
+{
+	struct whiteheat_simple close_command;
+
+	close_command.port = port->port_number + 1;
+	return firm_send_command(port, WHITEHEAT_CLOSE,
+			(__u8 *)&close_command, sizeof(close_command));
+}
+
+
+static void firm_setup_port(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct device *dev = &port->dev;
+	struct whiteheat_port_settings port_settings;
+	unsigned int cflag = tty->termios.c_cflag;
+
+	port_settings.port = port->port_number + 1;
+
+	/* get the byte size */
+	switch (cflag & CSIZE) {
+	case CS5:	port_settings.bits = 5;   break;
+	case CS6:	port_settings.bits = 6;   break;
+	case CS7:	port_settings.bits = 7;   break;
+	default:
+	case CS8:	port_settings.bits = 8;   break;
+	}
+	dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits);
+
+	/* determine the parity */
+	if (cflag & PARENB)
+		if (cflag & CMSPAR)
+			if (cflag & PARODD)
+				port_settings.parity = WHITEHEAT_PAR_MARK;
+			else
+				port_settings.parity = WHITEHEAT_PAR_SPACE;
+		else
+			if (cflag & PARODD)
+				port_settings.parity = WHITEHEAT_PAR_ODD;
+			else
+				port_settings.parity = WHITEHEAT_PAR_EVEN;
+	else
+		port_settings.parity = WHITEHEAT_PAR_NONE;
+	dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity);
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		port_settings.stop = 2;
+	else
+		port_settings.stop = 1;
+	dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop);
+
+	/* figure out the flow control settings */
+	if (cflag & CRTSCTS)
+		port_settings.hflow = (WHITEHEAT_HFLOW_CTS |
+						WHITEHEAT_HFLOW_RTS);
+	else
+		port_settings.hflow = WHITEHEAT_HFLOW_NONE;
+	dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__,
+	    (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
+	    (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
+	    (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
+	    (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
+
+	/* determine software flow control */
+	if (I_IXOFF(tty))
+		port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
+	else
+		port_settings.sflow = WHITEHEAT_SFLOW_NONE;
+	dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow);
+
+	port_settings.xon = START_CHAR(tty);
+	port_settings.xoff = STOP_CHAR(tty);
+	dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
+
+	/* get the baud rate wanted */
+	port_settings.baud = tty_get_baud_rate(tty);
+	dev_dbg(dev, "%s - baud rate = %d\n", __func__, port_settings.baud);
+
+	/* fixme: should set validated settings */
+	tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
+	/* handle any settings that aren't specified in the tty structure */
+	port_settings.lloop = 0;
+
+	/* now send the message to the device */
+	firm_send_command(port, WHITEHEAT_SETUP_PORT,
+			(__u8 *)&port_settings, sizeof(port_settings));
+}
+
+
+static int firm_set_rts(struct usb_serial_port *port, __u8 onoff)
+{
+	struct whiteheat_set_rdb rts_command;
+
+	rts_command.port = port->port_number + 1;
+	rts_command.state = onoff;
+	return firm_send_command(port, WHITEHEAT_SET_RTS,
+			(__u8 *)&rts_command, sizeof(rts_command));
+}
+
+
+static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff)
+{
+	struct whiteheat_set_rdb dtr_command;
+
+	dtr_command.port = port->port_number + 1;
+	dtr_command.state = onoff;
+	return firm_send_command(port, WHITEHEAT_SET_DTR,
+			(__u8 *)&dtr_command, sizeof(dtr_command));
+}
+
+
+static int firm_set_break(struct usb_serial_port *port, __u8 onoff)
+{
+	struct whiteheat_set_rdb break_command;
+
+	break_command.port = port->port_number + 1;
+	break_command.state = onoff;
+	return firm_send_command(port, WHITEHEAT_SET_BREAK,
+			(__u8 *)&break_command, sizeof(break_command));
+}
+
+
+static int firm_purge(struct usb_serial_port *port, __u8 rxtx)
+{
+	struct whiteheat_purge purge_command;
+
+	purge_command.port = port->port_number + 1;
+	purge_command.what = rxtx;
+	return firm_send_command(port, WHITEHEAT_PURGE,
+			(__u8 *)&purge_command, sizeof(purge_command));
+}
+
+
+static int firm_get_dtr_rts(struct usb_serial_port *port)
+{
+	struct whiteheat_simple get_dr_command;
+
+	get_dr_command.port = port->port_number + 1;
+	return firm_send_command(port, WHITEHEAT_GET_DTR_RTS,
+			(__u8 *)&get_dr_command, sizeof(get_dr_command));
+}
+
+
+static int firm_report_tx_done(struct usb_serial_port *port)
+{
+	struct whiteheat_simple close_command;
+
+	close_command.port = port->port_number + 1;
+	return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE,
+			(__u8 *)&close_command, sizeof(close_command));
+}
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat utility functions
+ *****************************************************************************/
+static int start_command_port(struct usb_serial *serial)
+{
+	struct usb_serial_port *command_port;
+	struct whiteheat_command_private *command_info;
+	int retval = 0;
+
+	command_port = serial->port[COMMAND_PORT];
+	command_info = usb_get_serial_port_data(command_port);
+	mutex_lock(&command_info->mutex);
+	if (!command_info->port_running) {
+		/* Work around HCD bugs */
+		usb_clear_halt(serial->dev, command_port->read_urb->pipe);
+
+		retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
+		if (retval) {
+			dev_err(&serial->dev->dev,
+				"%s - failed submitting read urb, error %d\n",
+				__func__, retval);
+			goto exit;
+		}
+	}
+	command_info->port_running++;
+
+exit:
+	mutex_unlock(&command_info->mutex);
+	return retval;
+}
+
+
+static void stop_command_port(struct usb_serial *serial)
+{
+	struct usb_serial_port *command_port;
+	struct whiteheat_command_private *command_info;
+
+	command_port = serial->port[COMMAND_PORT];
+	command_info = usb_get_serial_port_data(command_port);
+	mutex_lock(&command_info->mutex);
+	command_info->port_running--;
+	if (!command_info->port_running)
+		usb_kill_urb(command_port->read_urb);
+	mutex_unlock(&command_info->mutex);
+}
+
+module_usb_serial_driver(serial_drivers, id_table_combined);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("whiteheat.fw");
+MODULE_FIRMWARE("whiteheat_loader.fw");
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
new file mode 100644
index 0000000..72c1b0c
--- /dev/null
+++ b/drivers/usb/serial/whiteheat.h
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *      Copyright (C) 2002
+ *          Connect Tech Inc.
+ *
+ *      Copyright (C) 1999, 2000
+ *          Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_WHITEHEAT_H
+#define __LINUX_USB_SERIAL_WHITEHEAT_H
+
+
+/* WhiteHEAT commands */
+#define WHITEHEAT_OPEN			1	/* open the port */
+#define WHITEHEAT_CLOSE			2	/* close the port */
+#define WHITEHEAT_SETUP_PORT		3	/* change port settings */
+#define WHITEHEAT_SET_RTS		4	/* turn RTS on or off */
+#define WHITEHEAT_SET_DTR		5	/* turn DTR on or off */
+#define WHITEHEAT_SET_BREAK		6	/* turn BREAK on or off */
+#define WHITEHEAT_DUMP			7	/* dump memory */
+#define WHITEHEAT_STATUS		8	/* get status */
+#define WHITEHEAT_PURGE			9	/* clear the UART fifos */
+#define WHITEHEAT_GET_DTR_RTS		10	/* get the state of DTR and RTS
+							for a port */
+#define WHITEHEAT_GET_HW_INFO		11	/* get EEPROM info and
+							hardware ID */
+#define WHITEHEAT_REPORT_TX_DONE	12	/* get the next TX done */
+#define WHITEHEAT_EVENT			13	/* unsolicited status events */
+#define WHITEHEAT_ECHO			14	/* send data to the indicated
+						   IN endpoint */
+#define WHITEHEAT_DO_TEST		15	/* perform specified test */
+#define WHITEHEAT_CMD_COMPLETE		16	/* reply for some commands */
+#define WHITEHEAT_CMD_FAILURE		17	/* reply for failed commands */
+
+
+/*
+ * Commands to the firmware
+ */
+
+
+/*
+ * WHITEHEAT_OPEN
+ * WHITEHEAT_CLOSE
+ * WHITEHEAT_STATUS
+ * WHITEHEAT_GET_DTR_RTS
+ * WHITEHEAT_REPORT_TX_DONE
+*/
+struct whiteheat_simple {
+	__u8	port;	/* port number (1 to N) */
+};
+
+
+/*
+ * WHITEHEAT_SETUP_PORT
+ */
+#define WHITEHEAT_PAR_NONE	'n'	/* no parity */
+#define WHITEHEAT_PAR_EVEN	'e'	/* even parity */
+#define WHITEHEAT_PAR_ODD	'o'	/* odd parity */
+#define WHITEHEAT_PAR_SPACE	'0'	/* space (force 0) parity */
+#define WHITEHEAT_PAR_MARK	'1'	/* mark (force 1) parity */
+
+#define WHITEHEAT_SFLOW_NONE	'n'	/* no software flow control */
+#define WHITEHEAT_SFLOW_RX	'r'	/* XOFF/ON is sent when RX
+					   fills/empties */
+#define WHITEHEAT_SFLOW_TX	't'	/* when received XOFF/ON will
+					   stop/start TX */
+#define WHITEHEAT_SFLOW_RXTX	'b'	/* both SFLOW_RX and SFLOW_TX */
+
+#define WHITEHEAT_HFLOW_NONE		0x00	/* no hardware flow control */
+#define WHITEHEAT_HFLOW_RTS_TOGGLE	0x01	/* RTS is on during transmit,
+						   off otherwise */
+#define WHITEHEAT_HFLOW_DTR		0x02	/* DTR is off/on when RX
+						   fills/empties */
+#define WHITEHEAT_HFLOW_CTS		0x08	/* when received CTS off/on
+						   will stop/start TX */
+#define WHITEHEAT_HFLOW_DSR		0x10	/* when received DSR off/on
+						   will stop/start TX */
+#define WHITEHEAT_HFLOW_RTS		0x80	/* RTS is off/on when RX
+						   fills/empties */
+
+struct whiteheat_port_settings {
+	__u8	port;		/* port number (1 to N) */
+	__u32	baud;		/* any value 7 - 460800, firmware calculates
+				   best fit; arrives little endian */
+	__u8	bits;		/* 5, 6, 7, or 8 */
+	__u8	stop;		/* 1 or 2, default 1 (2 = 1.5 if bits = 5) */
+	__u8	parity;		/* see WHITEHEAT_PAR_* above */
+	__u8	sflow;		/* see WHITEHEAT_SFLOW_* above */
+	__u8	xoff;		/* XOFF byte value */
+	__u8	xon;		/* XON byte value */
+	__u8	hflow;		/* see WHITEHEAT_HFLOW_* above */
+	__u8	lloop;		/* 0/1 turns local loopback mode off/on */
+} __attribute__ ((packed));
+
+
+/*
+ * WHITEHEAT_SET_RTS
+ * WHITEHEAT_SET_DTR
+ * WHITEHEAT_SET_BREAK
+ */
+#define WHITEHEAT_RTS_OFF	0x00
+#define WHITEHEAT_RTS_ON	0x01
+#define WHITEHEAT_DTR_OFF	0x00
+#define WHITEHEAT_DTR_ON	0x01
+#define WHITEHEAT_BREAK_OFF	0x00
+#define WHITEHEAT_BREAK_ON	0x01
+
+struct whiteheat_set_rdb {
+	__u8	port;		/* port number (1 to N) */
+	__u8	state;		/* 0/1 turns signal off/on */
+};
+
+
+/*
+ * WHITEHEAT_DUMP
+ */
+#define WHITEHEAT_DUMP_MEM_DATA		'd'  /* data */
+#define WHITEHEAT_DUMP_MEM_IDATA	'i'  /* idata */
+#define WHITEHEAT_DUMP_MEM_BDATA	'b'  /* bdata */
+#define WHITEHEAT_DUMP_MEM_XDATA	'x'  /* xdata */
+
+/*
+ * Allowable address ranges (firmware checks address):
+ * Type DATA:  0x00 - 0xff
+ * Type IDATA: 0x80 - 0xff
+ * Type BDATA: 0x20 - 0x2f
+ * Type XDATA: 0x0000 - 0xffff
+ *
+ * B/I/DATA all read the local memory space
+ * XDATA reads the external memory space
+ * BDATA returns bits as bytes
+ *
+ * NOTE: 0x80 - 0xff (local space) are the Special Function Registers
+ *       of the 8051, and some have on-read side-effects.
+ */
+
+struct whiteheat_dump {
+	__u8	mem_type;	/* see WHITEHEAT_DUMP_* above */
+	__u16	addr;		/* address, see restrictions above */
+	__u16	length;		/* number of bytes to dump, max 63 bytes */
+};
+
+
+/*
+ * WHITEHEAT_PURGE
+ */
+#define WHITEHEAT_PURGE_RX	0x01	/* purge rx fifos */
+#define WHITEHEAT_PURGE_TX	0x02	/* purge tx fifos */
+
+struct whiteheat_purge {
+	__u8	port;		/* port number (1 to N) */
+	__u8	what;		/* bit pattern of what to purge */
+};
+
+
+/*
+ * WHITEHEAT_ECHO
+ */
+struct whiteheat_echo {
+	__u8	port;		/* port number (1 to N) */
+	__u8	length;		/* length of message to echo, max 61 bytes */
+	__u8	echo_data[61];	/* data to echo */
+};
+
+
+/*
+ * WHITEHEAT_DO_TEST
+ */
+#define WHITEHEAT_TEST_UART_RW		0x01  /* read/write uart registers */
+#define WHITEHEAT_TEST_UART_INTR	0x02  /* uart interrupt */
+#define WHITEHEAT_TEST_SETUP_CONT	0x03  /* setup for
+						PORT_CONT/PORT_DISCONT */
+#define WHITEHEAT_TEST_PORT_CONT	0x04  /* port connect */
+#define WHITEHEAT_TEST_PORT_DISCONT	0x05  /* port disconnect */
+#define WHITEHEAT_TEST_UART_CLK_START	0x06  /* uart clock test start */
+#define WHITEHEAT_TEST_UART_CLK_STOP	0x07  /* uart clock test stop */
+#define WHITEHEAT_TEST_MODEM_FT		0x08  /* modem signals, requires a
+						loopback cable/connector */
+#define WHITEHEAT_TEST_ERASE_EEPROM	0x09  /* erase eeprom */
+#define WHITEHEAT_TEST_READ_EEPROM	0x0a  /* read eeprom */
+#define WHITEHEAT_TEST_PROGRAM_EEPROM	0x0b  /* program eeprom */
+
+struct whiteheat_test {
+	__u8	port;		/* port number (1 to n) */
+	__u8	test;		/* see WHITEHEAT_TEST_* above*/
+	__u8	info[32];	/* additional info */
+};
+
+
+/*
+ * Replies from the firmware
+ */
+
+
+/*
+ * WHITEHEAT_STATUS
+ */
+#define WHITEHEAT_EVENT_MODEM		0x01	/* modem field is valid */
+#define WHITEHEAT_EVENT_ERROR		0x02	/* error field is valid */
+#define WHITEHEAT_EVENT_FLOW		0x04	/* flow field is valid */
+#define WHITEHEAT_EVENT_CONNECT		0x08	/* connect field is valid */
+
+#define WHITEHEAT_FLOW_NONE		0x00	/* no flow control active */
+#define WHITEHEAT_FLOW_HARD_OUT		0x01	/* TX is stopped by CTS
+						  (waiting for CTS to go on) */
+#define WHITEHEAT_FLOW_HARD_IN		0x02	/* remote TX is stopped
+						  by RTS */
+#define WHITEHEAT_FLOW_SOFT_OUT		0x04	/* TX is stopped by XOFF
+						  received (waiting for XON) */
+#define WHITEHEAT_FLOW_SOFT_IN		0x08	/* remote TX is stopped by XOFF
+						  transmitted */
+#define WHITEHEAT_FLOW_TX_DONE		0x80	/* TX has completed */
+
+struct whiteheat_status_info {
+	__u8	port;		/* port number (1 to N) */
+	__u8	event;		/* indicates what the current event is,
+					see WHITEHEAT_EVENT_* above */
+	__u8	modem;		/* modem signal status (copy of uart's
+					MSR register) */
+	__u8	error;		/* line status (copy of uart's LSR register) */
+	__u8	flow;		/* flow control state, see WHITEHEAT_FLOW_*
+					above */
+	__u8	connect;	/* 0 means not connected, non-zero means
+					connected */
+};
+
+
+/*
+ * WHITEHEAT_GET_DTR_RTS
+ */
+struct whiteheat_dr_info {
+	__u8	mcr;		/* copy of uart's MCR register */
+};
+
+
+/*
+ * WHITEHEAT_GET_HW_INFO
+ */
+struct whiteheat_hw_info {
+	__u8	hw_id;		/* hardware id number, WhiteHEAT = 0 */
+	__u8	sw_major_rev;	/* major version number */
+	__u8	sw_minor_rev;	/* minor version number */
+	struct whiteheat_hw_eeprom_info {
+		__u8	b0;			/* B0 */
+		__u8	vendor_id_low;		/* vendor id (low byte) */
+		__u8	vendor_id_high;		/* vendor id (high byte) */
+		__u8	product_id_low;		/* product id (low byte) */
+		__u8	product_id_high;	/* product id (high byte) */
+		__u8	device_id_low;		/* device id (low byte) */
+		__u8	device_id_high;		/* device id (high byte) */
+		__u8	not_used_1;
+		__u8	serial_number_0;	/* serial number (low byte) */
+		__u8	serial_number_1;	/* serial number */
+		__u8	serial_number_2;	/* serial number */
+		__u8	serial_number_3;	/* serial number (high byte) */
+		__u8	not_used_2;
+		__u8	not_used_3;
+		__u8	checksum_low;		/* checksum (low byte) */
+		__u8	checksum_high;		/* checksum (high byte */
+	} hw_eeprom_info;	/* EEPROM contents */
+};
+
+
+/*
+ * WHITEHEAT_EVENT
+ */
+struct whiteheat_event_info {
+	__u8	port;		/* port number (1 to N) */
+	__u8	event;		/* see whiteheat_status_info.event */
+	__u8	info;		/* see whiteheat_status_info.modem, .error,
+					.flow, .connect */
+};
+
+
+/*
+ * WHITEHEAT_DO_TEST
+ */
+#define WHITEHEAT_TEST_FAIL	0x00  /* test failed */
+#define WHITEHEAT_TEST_UNKNOWN	0x01  /* unknown test requested */
+#define WHITEHEAT_TEST_PASS	0xff  /* test passed */
+
+struct whiteheat_test_info {
+	__u8	port;		/* port number (1 to N) */
+	__u8	test;		/* indicates which test this is a response for,
+				   see WHITEHEAT_DO_TEST above */
+	__u8	status;		/* see WHITEHEAT_TEST_* above */
+	__u8	results[32];	/* test-dependent results */
+};
+
+
+#endif
diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c
new file mode 100644
index 0000000..ff4092f
--- /dev/null
+++ b/drivers/usb/serial/wishbone-serial.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Wishbone-Serial adapter driver
+ *
+ * Copyright (C) 2013 Wesley W. Terpstra <w.terpstra@gsi.de>
+ * Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define GSI_VENDOR_OPENCLOSE 0xB0
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1D50, 0x6062, 0xFF, 0xFF, 0xFF) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/*
+ * Etherbone must be told that a new stream has begun before data arrives.
+ * This is necessary to restart the negotiation of Wishbone bus parameters.
+ * Similarly, when the stream ends, Etherbone must be told so that the cycle
+ * line can be driven low in the case that userspace failed to do so.
+ */
+static int usb_gsi_openclose(struct usb_serial_port *port, int value)
+{
+	struct usb_device *dev = port->serial->dev;
+
+	return usb_control_msg(
+		dev,
+		usb_sndctrlpipe(dev, 0), /* Send to EP0OUT */
+		GSI_VENDOR_OPENCLOSE,
+		USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+		value, /* wValue = device is open(1) or closed(0) */
+		port->serial->interface->cur_altsetting->desc.bInterfaceNumber,
+		NULL, 0,  /* There is no data stage */
+		5000); /* Timeout till operation fails */
+}
+
+static int wishbone_serial_open(struct tty_struct *tty,
+				struct usb_serial_port *port)
+{
+	int retval;
+
+	retval = usb_gsi_openclose(port, 1);
+	if (retval) {
+		dev_err(&port->serial->dev->dev,
+		       "Could not mark device as open (%d)\n",
+		       retval);
+		return retval;
+	}
+
+	retval = usb_serial_generic_open(tty, port);
+	if (retval)
+		usb_gsi_openclose(port, 0);
+
+	return retval;
+}
+
+static void wishbone_serial_close(struct usb_serial_port *port)
+{
+	usb_serial_generic_close(port);
+	usb_gsi_openclose(port, 0);
+}
+
+static struct usb_serial_driver wishbone_serial_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"wishbone_serial",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.open =			&wishbone_serial_open,
+	.close =		&wishbone_serial_close,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&wishbone_serial_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR("Wesley W. Terpstra <w.terpstra@gsi.de>");
+MODULE_DESCRIPTION("USB Wishbone-Serial adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c
new file mode 100644
index 0000000..cf262c9
--- /dev/null
+++ b/drivers/usb/serial/xsens_mt.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xsens MT USB driver
+ *
+ * Copyright (C) 2013 Xsens <info@xsens.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define XSENS_VID 0x2639
+
+#define MTi_10_IMU_PID		0x0001
+#define MTi_20_VRU_PID		0x0002
+#define MTi_30_AHRS_PID		0x0003
+
+#define MTi_100_IMU_PID		0x0011
+#define MTi_200_VRU_PID		0x0012
+#define MTi_300_AHRS_PID	0x0013
+
+#define MTi_G_700_GPS_INS_PID	0x0017
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(XSENS_VID, MTi_10_IMU_PID) },
+	{ USB_DEVICE(XSENS_VID, MTi_20_VRU_PID) },
+	{ USB_DEVICE(XSENS_VID, MTi_30_AHRS_PID) },
+
+	{ USB_DEVICE(XSENS_VID, MTi_100_IMU_PID) },
+	{ USB_DEVICE(XSENS_VID, MTi_200_VRU_PID) },
+	{ USB_DEVICE(XSENS_VID, MTi_300_AHRS_PID) },
+
+	{ USB_DEVICE(XSENS_VID, MTi_G_700_GPS_INS_PID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int xsens_mt_probe(struct usb_serial *serial,
+					const struct usb_device_id *id)
+{
+	if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1)
+		return 0;
+
+	return -ENODEV;
+}
+
+static struct usb_serial_driver xsens_mt_device = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "xsens_mt",
+	},
+	.id_table = id_table,
+	.num_ports = 1,
+
+	.probe = xsens_mt_probe,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&xsens_mt_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_AUTHOR("Frans Klaver <frans.klaver@xsens.com>");
+MODULE_DESCRIPTION("USB-serial driver for Xsens motion trackers");
+MODULE_LICENSE("GPL v2");