zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/Kconfig b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Kconfig
new file mode 100644
index 0000000..7141d65
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Kconfig
@@ -0,0 +1,682 @@
+#
+# USB Serial device configuration
+#
+
+menuconfig USB_SERIAL
+	tristate "USB Serial Converter support"
+	depends on USB
+	---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_EZUSB
+	bool "Functions for loading firmware on EZUSB chips"
+	help
+	    Say Y here if you need EZUSB device support.
+
+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_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
+	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 be 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_FUNSOFT
+	tristate "USB Fundamental Software Dongle Driver"
+	---help---
+	  Say Y here if you want to use the Fundamental Software dongle.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called funsoft.
+
+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_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
+	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
+	---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_KEYSPAN_MPR
+	bool "USB Keyspan MPR Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the Keyspan MPR converter.
+
+config USB_SERIAL_KEYSPAN_USA28
+	bool "USB Keyspan USA-28 Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-28 converter.
+
+config USB_SERIAL_KEYSPAN_USA28X
+	bool "USB Keyspan USA-28X Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-28X converter.
+	  Be sure you have a USA-28X, there are also 28XA and 28XB
+	  models, the label underneath has the actual part number.
+
+config USB_SERIAL_KEYSPAN_USA28XA
+	bool "USB Keyspan USA-28XA Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-28XA converter.
+	  Be sure you have a USA-28XA, there are also 28X and 28XB
+	  models, the label underneath has the actual part number.
+
+config USB_SERIAL_KEYSPAN_USA28XB
+	bool "USB Keyspan USA-28XB Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-28XB converter.
+	  Be sure you have a USA-28XB, there are also 28X and 28XA
+	  models, the label underneath has the actual part number.
+
+config USB_SERIAL_KEYSPAN_USA19
+	bool "USB Keyspan USA-19 Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-19 converter.
+
+config USB_SERIAL_KEYSPAN_USA18X
+	bool "USB Keyspan USA-18X Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-18X converter.
+
+config USB_SERIAL_KEYSPAN_USA19W
+	bool "USB Keyspan USA-19W Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-19W converter.
+
+config USB_SERIAL_KEYSPAN_USA19QW
+	bool "USB Keyspan USA-19QW Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-19QW converter.
+
+config USB_SERIAL_KEYSPAN_USA19QI
+	bool "USB Keyspan USA-19QI Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-19QI converter.
+
+config USB_SERIAL_KEYSPAN_USA49W
+	bool "USB Keyspan USA-49W Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-49W converter.
+
+config USB_SERIAL_KEYSPAN_USA49WLC
+	bool "USB Keyspan USA-49WLC Firmware"
+	depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
+	help
+	  Say Y here to include firmware for the USA-49WLC converter.
+
+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_MOTOROLA
+	tristate "USB Motorola Phone modem driver"
+	---help---
+	  Say Y here if you want to use a Motorola phone with a USB
+	  connector as a modem link.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called moto_modem.  If unsure, choose N.
+
+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_HP4X
+        tristate "USB HP4x Calculators support"
+        help
+          Say Y here if you want to use an Hewlett-Packard 4x Calculator.
+
+          To compile this driver as a module, choose M here: the
+          module will be called hp4x.
+
+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_SIEMENS_MPI
+	tristate "USB Siemens MPI driver"
+	help
+	  Say M here if you want to use a Siemens USB/MPI adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called siemens_mpi.
+
+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 / Entregra Single Port Serial Driver"
+	select USB_EZUSB
+	help
+	  Say Y here if you want to use a Xircom or Entregra 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_VIVOPAY_SERIAL
+        tristate "USB ViVOpay serial interface driver"
+        help
+          Say Y here if you want to use a ViVOtech ViVOpay USB device.
+
+          To compile this driver as a module, choose M here: the
+          module will be called vivopay-serial.
+
+config USB_SERIAL_ZIO
+	tristate "ZIO Motherboard USB serial interface driver"
+	help
+	  Say Y here if you want to use ZIO Motherboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zio.
+
+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_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/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile
new file mode 100644
index 0000000..07f198e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile
@@ -0,0 +1,64 @@
+#
+# 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
+usbserial-$(CONFIG_USB_EZUSB)		+= ezusb.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_FTDI_SIO)		+= ftdi_sio.o
+obj-$(CONFIG_USB_SERIAL_FUNSOFT)		+= funsoft.o
+obj-$(CONFIG_USB_SERIAL_GARMIN)			+= garmin_gps.o
+obj-$(CONFIG_USB_SERIAL_HP4X)			+= hp4x.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_MOTOROLA)		+= moto_modem.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_SAFE)			+= safe_serial.o
+obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI)		+= siemens_mpi.o
+obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS)		+= sierra.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_VISOR)			+= visor.o
+obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
+obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
+obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL)		+= vivopay-serial.o
+obj-$(CONFIG_USB_SERIAL_ZIO)			+= zio.o
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile-keyspan_pda_fw b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile-keyspan_pda_fw
new file mode 100644
index 0000000..c20baf7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/Makefile-keyspan_pda_fw
@@ -0,0 +1,16 @@
+
+# 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/aircable.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/aircable.c
new file mode 100644
index 0000000..eec4fb9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/aircable.c
@@ -0,0 +1,207 @@
+/*
+ * AIRcable USB Bluetooth Dongle Driver.
+ *
+ * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * 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 posibilities 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 sended.
+ * 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.
+ *
+ * The driver registers himself with the USB-serial core and the USB Core. I had
+ * to implement a probe function against USB-serial, because other way, the
+ * driver was attaching himself to both interfaces. I have tryed with different
+ * configurations of usb_serial_driver with out exit, only the probe function
+ * could handle this correctly.
+ *
+ * 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 knowladge in drivers development
+ * and made it all avaible 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>
+
+static bool debug;
+
+/* 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
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.0"
+#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_probe(struct usb_serial *serial,
+			  const struct usb_device_id *id)
+{
+	struct usb_host_interface *iface_desc = serial->interface->
+								cur_altsetting;
+	struct usb_endpoint_descriptor *endpoint;
+	int num_bulk_out = 0;
+	int i;
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_out(endpoint)) {
+			dbg("found bulk out on endpoint %d", i);
+			++num_bulk_out;
+		}
+	}
+
+	if (num_bulk_out == 0) {
+		dbg("Invalid interface, discarding");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int aircable_process_packet(struct tty_struct *tty,
+			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) {
+		dbg("%s - malformed packet", __func__);
+		return 0;
+	}
+
+	tty_insert_flip_string(tty, 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;
+	struct tty_struct *tty;
+	int has_headers;
+	int count;
+	int len;
+	int i;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	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(tty, port, has_headers,
+								&data[i], len);
+	}
+
+	if (count)
+		tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static struct usb_driver aircable_driver = {
+	.name =		"aircable",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver aircable_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"aircable",
+	},
+	.id_table = 		id_table,
+	.num_ports =		1,
+	.bulk_out_size =	HCI_COMPLETE_FRAME,
+	.probe =		aircable_probe,
+	.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(aircable_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ark3116.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ark3116.c
new file mode 100644
index 0000000..9282703
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ark3116.c
@@ -0,0 +1,861 @@
+/*
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.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>
+
+static bool debug;
+/*
+ * Version information
+ */
+
+#define DRIVER_VERSION "v0.7"
+#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 {
+	struct async_icount	icount;
+	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 contol 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);
+	return result;
+}
+
+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 < 0)
+		return result;
+	else
+		return buf[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_attach(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct ark3116_private *priv;
+
+	/* make sure we have our end-points */
+	if ((serial->num_bulk_in == 0) ||
+	    (serial->num_bulk_out == 0) ||
+	    (serial->num_interrupt_in == 0)) {
+		dev_err(&serial->dev->dev,
+			"%s - missing endpoint - "
+			"bulk in: %d, bulk out: %d, int in %d\n",
+			KBUILD_MODNAME,
+			serial->num_bulk_in,
+			serial->num_bulk_out,
+			serial->num_interrupt_in);
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(struct ark3116_private),
+		       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(&serial->dev->dev,
+		"%s using %s mode\n",
+		KBUILD_MODNAME,
+		priv->irda ? "IrDA" : "RS232");
+	return 0;
+}
+
+static void ark3116_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+	/* device is closed, so URBs and DMA should be down */
+
+	usb_set_serial_port_data(port, NULL);
+
+	mutex_destroy(&priv->hw_lock);
+
+	kfree(priv);
+}
+
+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 */
+	dbg("%s - setting bps to %d", __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);
+
+	dbg("%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d",
+	    __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(&serial->dev->dev,
+			 "%s: don't know how to do software flow control\n",
+			 KBUILD_MODNAME);
+	}
+
+	/* 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;
+
+	if (serial->dev) {
+		/* disable DMA */
+		ark3116_write_reg(serial, UART_FCR, 0);
+
+		/* deactivate interrupts */
+		ark3116_write_reg(serial, UART_IER, 0);
+
+		usb_serial_generic_close(port);
+		if (serial->num_interrupt_in)
+			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) {
+		dbg("%s - usb_serial_generic_open failed: %d",
+		    __func__, result);
+		goto err_out;
+	}
+
+	/* remove any data still left: also clears error state */
+	ark3116_read_reg(serial, UART_RX, buf);
+
+	/* read modem status */
+	priv->msr = ark3116_read_reg(serial, UART_MSR, buf);
+	/* read line status */
+	priv->lsr = ark3116_read_reg(serial, UART_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);
+		ark3116_close(port);
+		goto err_out;
+	}
+
+	/* 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);
+
+err_out:
+	kfree(buf);
+	return result;
+}
+
+static int ark3116_get_icount(struct tty_struct *tty,
+					struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	struct async_icount cnow = priv->icount;
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+	return 0;
+}
+
+static int ark3116_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ark3116_private *priv = usb_get_serial_port_data(port);
+	struct serial_struct serstruct;
+	void __user *user_arg = (void __user *)arg;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		/* XXX: Some of these values are probably wrong. */
+		memset(&serstruct, 0, sizeof(serstruct));
+		serstruct.type = PORT_16654;
+		serstruct.line = port->serial->minor;
+		serstruct.port = port->number;
+		serstruct.custom_divisor = 0;
+		serstruct.baud_base = 460800;
+
+		if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
+			return -EFAULT;
+
+		return 0;
+	case TIOCSSERIAL:
+		if (copy_from_user(&serstruct, user_arg, sizeof(serstruct)))
+			return -EFAULT;
+		return 0;
+	case TIOCMIWAIT:
+		for (;;) {
+			struct async_icount prev = priv->icount;
+			interruptible_sleep_on(&port->delta_msr_wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			if ((prev.rng == priv->icount.rng) &&
+			    (prev.dsr == priv->icount.dsr) &&
+			    (prev.dcd == priv->icount.dcd) &&
+			    (prev.cts == priv->icount.cts))
+				return -EIO;
+			if ((arg & TIOCM_RNG &&
+			     (prev.rng != priv->icount.rng)) ||
+			    (arg & TIOCM_DSR &&
+			     (prev.dsr != priv->icount.dsr)) ||
+			    (arg & TIOCM_CD  &&
+			     (prev.dcd != priv->icount.dcd)) ||
+			    (arg & TIOCM_CTS &&
+			     (prev.cts != priv->icount.cts)))
+				return 0;
+		}
+		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)
+			priv->icount.cts++;
+		if (msr & UART_MSR_DDSR)
+			priv->icount.dsr++;
+		if (msr & UART_MSR_DDCD)
+			priv->icount.dcd++;
+		if (msr & UART_MSR_TERI)
+			priv->icount.rng++;
+		wake_up_interruptible(&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)
+			priv->icount.brk++;
+		if (lsr & UART_LSR_FE)
+			priv->icount.frame++;
+		if (lsr & UART_LSR_PE)
+			priv->icount.parity++;
+		if (lsr & UART_LSR_OE)
+			priv->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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __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;
+			dbg("%s: iir=%02x", __func__, data[1]);
+			if (id == UART_IIR_MSI) {
+				dbg("%s: msr=%02x", __func__, data[3]);
+				ark3116_update_msr(port, data[3]);
+				break;
+			} else if (id == UART_IIR_RLSI) {
+				dbg("%s: lsr=%02x", __func__, data[2]);
+				ark3116_update_lsr(port, data[2]);
+				break;
+			}
+		}
+		/*
+		 * Not sure what this data meant...
+		 */
+		usb_serial_debug_data(debug, &port->dev,
+				      __func__,
+				      urb->actual_length,
+				      urb->transfer_buffer);
+		break;
+	}
+
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting interrupt urb\n",
+			__func__, result);
+}
+
+
+/* Data comes in via the bulk (data) URB, erors/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);
+	struct tty_struct *tty;
+	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;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		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(tty, 0, TTY_OVERRUN);
+	}
+	tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+							urb->actual_length);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static struct usb_driver ark3116_driver = {
+	.name =		"ark3116",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver ark3116_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"ark3116",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.attach =		ark3116_attach,
+	.release =		ark3116_release,
+	.set_termios =		ark3116_set_termios,
+	.init_termios =		ark3116_init_termios,
+	.ioctl =		ark3116_ioctl,
+	.tiocmget =		ark3116_tiocmget,
+	.tiocmset =		ark3116_tiocmset,
+	.get_icount =		ark3116_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(ark3116_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug");
+
+/*
+ * 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.c
new file mode 100644
index 0000000..a52e0d2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.c
@@ -0,0 +1,535 @@
+/*
+ * 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.
+ *
+ * 	This program is free software; you can redistribute it and/or modify
+ * 	it under the terms of the GNU General Public License as published by
+ * 	the Free Software Foundation; either version 2 of the License, or
+ * 	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ *
+ * TODO:
+ * -- Add true modem contol 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/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 "belkin_sa.h"
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.3"
+#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_startup(struct usb_serial *serial);
+static void belkin_sa_release(struct usb_serial *serial);
+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_combined[] = {
+	{ 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_combined);
+
+static struct usb_driver belkin_driver = {
+	.name =		"belkin",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table_combined,
+};
+
+/* 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_combined,
+	.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,
+	.attach =		belkin_sa_startup,
+	.release =		belkin_sa_release,
+};
+
+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)
+
+/* do some startup allocations not currently performed by usb_serial_probe() */
+static int belkin_sa_startup(struct usb_serial *serial)
+{
+	struct usb_device *dev = serial->dev;
+	struct belkin_sa_private *priv;
+
+	/* allocate the private data structure */
+	priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL);
+	if (!priv)
+		return -1; /* error */
+	/* set initial values for control structures */
+	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);
+
+	init_waitqueue_head(&serial->port[0]->write_wait);
+	usb_set_serial_port_data(serial->port[0], priv);
+
+	return 0;
+}
+
+static void belkin_sa_release(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+static int belkin_sa_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	int retval;
+
+	dbg("%s port %d", __func__, port->number);
+
+	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)
+{
+	dbg("%s port %d", __func__, port->number);
+
+	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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &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);
+	struct tty_struct *tty;
+	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;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		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(tty, 0, TTY_OVERRUN);
+	}
+
+	tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+							urb->actual_length);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+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: dbg("CSIZE was not CS5-CS8, using default of 8");
+			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;
+
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%s", __func__);
+
+	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(belkin_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.h
new file mode 100644
index 0000000..c74b58a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/belkin_sa.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/bus.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/bus.c
new file mode 100644
index 0000000..358d2d6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/bus.c
@@ -0,0 +1,188 @@
+/*
+ * USB Serial Converter Bus specific functions
+ *
+ * Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#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 ssize_t show_port_number(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+
+	return sprintf(buf, "%d\n", port->number - port->serial->minor);
+}
+
+static DEVICE_ATTR(port_number, S_IRUGO, show_port_number, NULL);
+
+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) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	driver = port->serial->type;
+	if (driver->port_probe) {
+		retval = driver->port_probe(port);
+		if (retval)
+			goto exit;
+	}
+
+	retval = device_create_file(dev, &dev_attr_port_number);
+	if (retval) {
+		if (driver->port_remove)
+			driver->port_remove(port);
+		goto exit;
+	}
+
+	minor = port->number;
+	tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev);
+	if (IS_ERR(tty_dev)) {
+		retval = PTR_ERR(tty_dev);
+		device_remove_file(dev, &dev_attr_port_number);
+		if (driver->port_remove)
+			driver->port_remove(port);
+		goto exit;
+	}
+
+	dev_info(&port->serial->dev->dev,
+		 "%s converter now attached to ttyUSB%d\n",
+		 driver->description, minor);
+
+exit:
+	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;
+
+	port = to_usb_serial_port(dev);
+	if (!port)
+		return -ENODEV;
+
+	device_remove_file(&port->dev, &dev_attr_port_number);
+
+	driver = port->serial->type;
+	if (driver->port_remove)
+		retval = driver->port_remove(port);
+
+	minor = port->number;
+	tty_unregister_device(usb_serial_tty_driver, minor);
+	dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
+		 driver->description, minor);
+
+	return retval;
+}
+
+#ifdef CONFIG_HOTPLUG
+static ssize_t store_new_id(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, 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->drvwrap.driver,
+					  buf, count);
+	return retval;
+}
+
+static struct driver_attribute drv_attrs[] = {
+	__ATTR(new_id, S_IWUSR, NULL, store_new_id),
+	__ATTR_NULL,
+};
+
+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);
+}
+
+#else
+static struct driver_attribute drv_attrs[] = {
+	__ATTR_NULL,
+};
+static inline void free_dynids(struct usb_serial_driver *drv)
+{
+}
+#endif
+
+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_attrs = 	drv_attrs,
+};
+
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/ch341.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ch341.c
new file mode 100644
index 0000000..a45594a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ch341.c
@@ -0,0 +1,661 @@
+/*
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.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_WRITE_REG    0x9A
+#define CH341_REQ_READ_REG     0x95
+#define CH341_REG_BREAK1       0x05
+#define CH341_REG_BREAK2       0x18
+#define CH341_NBREAK_BITS_REG1 0x01
+#define CH341_NBREAK_BITS_REG2 0x40
+
+
+static bool debug;
+
+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 line_control; /* set line control value RTS/DTR */
+	u8 line_status; /* active status of modem control inputs */
+	u8 multi_status_change; /* status changed multiple since last call */
+};
+
+static int ch341_control_out(struct usb_device *dev, u8 request,
+			     u16 value, u16 index)
+{
+	int r;
+	dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
+		(int)request, (int)value, (int)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);
+
+	return r;
+}
+
+static int ch341_control_in(struct usb_device *dev,
+			    u8 request, u16 value, u16 index,
+			    char *buf, unsigned bufsize)
+{
+	int r;
+	dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
+		(int)request, (int)value, (int)index, buf, (int)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);
+	return r;
+}
+
+static int ch341_set_baudrate(struct usb_device *dev,
+			      struct ch341_private *priv)
+{
+	short a, b;
+	int r;
+	unsigned long factor;
+	short divisor;
+
+	dbg("ch341_set_baudrate(%d)", priv->baud_rate);
+
+	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;
+	b = factor & 0xff;
+
+	r = ch341_control_out(dev, 0x9a, 0x1312, a);
+	if (!r)
+		r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
+
+	return r;
+}
+
+static int ch341_set_handshake(struct usb_device *dev, u8 control)
+{
+	dbg("ch341_set_handshake(0x%02x)", control);
+	return ch341_control_out(dev, 0xa4, ~control, 0);
+}
+
+static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
+{
+	char *buffer;
+	int r;
+	const unsigned size = 8;
+	unsigned long flags;
+
+	dbg("ch341_get_status()");
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
+	if (r < 0)
+		goto out;
+
+	/* setup the private status if available */
+	if (r == 2) {
+		r = 0;
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+		priv->multi_status_change = 0;
+		spin_unlock_irqrestore(&priv->lock, flags);
+	} else
+		r = -EPROTO;
+
+out:	kfree(buffer);
+	return r;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
+{
+	char *buffer;
+	int r;
+	const unsigned size = 8;
+
+	dbg("ch341_configure()");
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* expect two bytes 0x27 0x00 */
+	r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
+	if (r < 0)
+		goto out;
+
+	r = ch341_control_out(dev, 0xa1, 0, 0);
+	if (r < 0)
+		goto out;
+
+	r = ch341_set_baudrate(dev, priv);
+	if (r < 0)
+		goto out;
+
+	/* expect two bytes 0x56 0x00 */
+	r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
+	if (r < 0)
+		goto out;
+
+	r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
+	if (r < 0)
+		goto out;
+
+	/* expect 0xff 0xee */
+	r = ch341_get_status(dev, priv);
+	if (r < 0)
+		goto out;
+
+	r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
+	if (r < 0)
+		goto out;
+
+	r = ch341_set_baudrate(dev, priv);
+	if (r < 0)
+		goto out;
+
+	r = ch341_set_handshake(dev, priv->line_control);
+	if (r < 0)
+		goto out;
+
+	/* expect 0x9f 0xee */
+	r = ch341_get_status(dev, priv);
+
+out:	kfree(buffer);
+	return r;
+}
+
+/* allocate private data */
+static int ch341_attach(struct usb_serial *serial)
+{
+	struct ch341_private *priv;
+	int r;
+
+	dbg("ch341_attach()");
+
+	/* private data */
+	priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->baud_rate = DEFAULT_BAUD_RATE;
+	priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+
+	r = ch341_configure(serial->dev, priv);
+	if (r < 0)
+		goto error;
+
+	usb_set_serial_port_data(serial->port[0], priv);
+	return 0;
+
+error:	kfree(priv);
+	return r;
+}
+
+static int ch341_carrier_raised(struct usb_serial_port *port)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	if (priv->line_status & 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;
+
+	dbg("%s - port %d", __func__, port->number);
+	/* drop DTR and RTS */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (on)
+		priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
+	else
+		priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	ch341_set_handshake(port->serial->dev, priv->line_control);
+	wake_up_interruptible(&port->delta_msr_wait);
+}
+
+static void ch341_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	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 usb_serial *serial = port->serial;
+	struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
+	int r;
+
+	dbg("ch341_open()");
+
+	priv->baud_rate = DEFAULT_BAUD_RATE;
+
+	r = ch341_configure(serial->dev, priv);
+	if (r)
+		goto out;
+
+	r = ch341_set_handshake(serial->dev, priv->line_control);
+	if (r)
+		goto out;
+
+	r = ch341_set_baudrate(serial->dev, priv);
+	if (r)
+		goto out;
+
+	dbg("%s - submitting interrupt urb", __func__);
+	r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (r) {
+		dev_err(&port->dev, "%s - failed submitting interrupt urb,"
+			" error %d\n", __func__, r);
+		ch341_close(port);
+		goto out;
+	}
+
+	r = usb_serial_generic_open(tty, port);
+
+out:	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;
+
+	dbg("ch341_set_termios()");
+
+	baud_rate = tty_get_baud_rate(tty);
+
+	priv->baud_rate = baud_rate;
+
+	if (baud_rate) {
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		ch341_set_baudrate(port->serial->dev, priv);
+	} else {
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	ch341_set_handshake(port->serial->dev, priv->line_control);
+
+	/* Unimplemented:
+	 * (cflag & CSIZE) : data bits [5, 8]
+	 * (cflag & PARENB) : parity {NONE, EVEN, ODD}
+	 * (cflag & CSTOPB) : stop bits [1, 2]
+	 */
+}
+
+static void ch341_break_ctl(struct tty_struct *tty, int break_state)
+{
+	const uint16_t ch341_break_reg =
+		CH341_REG_BREAK1 | ((uint16_t) CH341_REG_BREAK2 << 8);
+	struct usb_serial_port *port = tty->driver_data;
+	int r;
+	uint16_t reg_contents;
+	uint8_t *break_reg;
+
+	dbg("%s()", __func__);
+
+	break_reg = kmalloc(2, GFP_KERNEL);
+	if (!break_reg) {
+		dev_err(&port->dev, "%s - kmalloc failed\n", __func__);
+		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;
+	}
+	dbg("%s - initial ch341 break register contents - reg1: %x, reg2: %x",
+			__func__, break_reg[0], break_reg[1]);
+	if (break_state != 0) {
+		dbg("%s - Enter break state requested", __func__);
+		break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
+		break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
+	} else {
+		dbg("%s - Leave break state requested", __func__);
+		break_reg[0] |= CH341_NBREAK_BITS_REG1;
+		break_reg[1] |= CH341_NBREAK_BITS_REG2;
+	}
+	dbg("%s - New ch341 break register contents - reg1: %x, reg2: %x",
+			__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->line_control |= CH341_BIT_RTS;
+	if (set & TIOCM_DTR)
+		priv->line_control |= CH341_BIT_DTR;
+	if (clear & TIOCM_RTS)
+		priv->line_control &= ~CH341_BIT_RTS;
+	if (clear & TIOCM_DTR)
+		priv->line_control &= ~CH341_BIT_DTR;
+	control = priv->line_control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return ch341_set_handshake(port->serial->dev, control);
+}
+
+static void ch341_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int actual_length = urb->actual_length;
+	int status;
+
+	dbg("%s (%d)", __func__, port->number);
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __func__,
+		    urb->status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+			      urb->actual_length, urb->transfer_buffer);
+
+	if (actual_length >= 4) {
+		struct ch341_private *priv = usb_get_serial_port_data(port);
+		unsigned long flags;
+		u8 prev_line_status = priv->line_status;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->line_status = (~(data[2])) & CH341_BITS_MODEM_STAT;
+		if ((data[1] & CH341_MULT_STAT))
+			priv->multi_status_change = 1;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		if ((priv->line_status ^ prev_line_status) & CH341_BIT_DCD) {
+			struct tty_struct *tty = tty_port_tty_get(&port->port);
+			if (tty)
+				usb_serial_handle_dcd_change(port, tty,
+					    priv->line_status & CH341_BIT_DCD);
+			tty_kref_put(tty);
+		}
+
+		wake_up_interruptible(&port->delta_msr_wait);
+	}
+
+exit:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(&urb->dev->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, status);
+}
+
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 prevstatus;
+	u8 status;
+	u8 changed;
+	u8 multi_change = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prevstatus = priv->line_status;
+	priv->multi_status_change = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (!multi_change) {
+		interruptible_sleep_on(&port->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		if (port->serial->disconnected)
+			return -EIO;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->line_status;
+		multi_change = priv->multi_status_change;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prevstatus ^ status;
+
+		if (((arg & TIOCM_RNG) && (changed & CH341_BIT_RI)) ||
+		    ((arg & TIOCM_DSR) && (changed & CH341_BIT_DSR)) ||
+		    ((arg & TIOCM_CD)  && (changed & CH341_BIT_DCD)) ||
+		    ((arg & TIOCM_CTS) && (changed & CH341_BIT_CTS))) {
+			return 0;
+		}
+		prevstatus = status;
+	}
+
+	return 0;
+}
+
+static int ch341_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		return wait_modem_info(port, arg);
+
+	default:
+		dbg("%s not supported = 0x%04x", __func__, cmd);
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+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;
+
+	dbg("%s (%d)", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	mcr = priv->line_control;
+	status = priv->line_status;
+	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);
+
+	dbg("%s - result = %x", __func__, result);
+
+	return result;
+}
+
+
+static int ch341_reset_resume(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_serial *serial = NULL;
+	struct ch341_private *priv;
+
+	serial = usb_get_intfdata(intf);
+	priv = usb_get_serial_port_data(serial->port[0]);
+
+	/*reconfigure ch341 serial port after bus-reset*/
+	ch341_configure(dev, priv);
+
+	usb_serial_resume(intf);
+
+	return 0;
+}
+
+static struct usb_driver ch341_driver = {
+	.name		= "ch341",
+	.probe		= usb_serial_probe,
+	.disconnect	= usb_serial_disconnect,
+	.suspend	= usb_serial_suspend,
+	.resume		= usb_serial_resume,
+	.reset_resume	= ch341_reset_resume,
+	.id_table	= id_table,
+	.supports_autosuspend =	1,
+};
+
+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,
+	.ioctl             = ch341_ioctl,
+	.set_termios       = ch341_set_termios,
+	.break_ctl         = ch341_break_ctl,
+	.tiocmget          = ch341_tiocmget,
+	.tiocmset          = ch341_tiocmset,
+	.read_int_callback = ch341_read_int_callback,
+	.attach            = ch341_attach,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ch341_device, NULL
+};
+
+module_usb_serial_driver(ch341_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/console.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/console.c
new file mode 100644
index 0000000..87302dd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/console.c
@@ -0,0 +1,322 @@
+/*
+ * USB Serial Console driver
+ *
+ * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * Thanks to Randy Dunlap for the original version of this code.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.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>
+
+static int debug;
+
+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;
+
+	dbg("%s", __func__);
+
+	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;
+	}
+	co->cflag = cflag;
+
+	/*
+	 * no need to check the index here: if the index is wrong, console
+	 * code won't call us
+	 */
+	serial = usb_serial_get_by_index(co->index);
+	if (serial == NULL) {
+		/* no device is connected yet, sorry :( */
+		err("No USB device connected to ttyUSB%i", co->index);
+		return -ENODEV;
+	}
+
+	retval = usb_autopm_get_interface(serial->interface);
+	if (retval)
+		goto error_get_interface;
+
+	port = serial->port[co->index - serial->minor];
+	tty_port_tty_set(&port->port, NULL);
+
+	info->port = port;
+
+	++port->port.count;
+	if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
+		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;
+				err("no more memory");
+				goto reset_open_count;
+			}
+			kref_init(&tty->kref);
+			tty->driver = usb_serial_tty_driver;
+			tty->index = co->index;
+			INIT_LIST_HEAD(&tty->tty_files);
+			kref_get(&tty->driver->kref);
+			tty->ops = &usb_console_fake_tty_ops;
+			if (tty_init_termios(tty)) {
+				retval = -ENOMEM;
+				err("no more memory");
+				goto put_tty;
+			}
+			tty_port_tty_set(&port->port, tty);
+		}
+
+		/* only call the device specific open if this
+		 * is the first time the port is opened */
+		if (serial->type->open)
+			retval = serial->type->open(NULL, port);
+		else
+			retval = usb_serial_generic_open(NULL, port);
+
+		if (retval) {
+			err("could not open USB console port");
+			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_kref_put(tty);
+		}
+		set_bit(ASYNCB_INITIALIZED, &port->port.flags);
+	}
+	/* 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);
+ put_tty:
+	tty_kref_put(tty);
+ reset_open_count:
+	port->port.count = 0;
+	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;
+
+	dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
+
+	if (!port->port.console) {
+		dbg("%s - port not opened", __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 */
+		if (serial->type->write)
+			retval = serial->type->write(NULL, port, buf, i);
+		else
+			retval = usb_serial_generic_write(NULL, port, buf, i);
+		dbg("%s - return value : %d", __func__, retval);
+		if (lf) {
+			/* append CR after LF */
+			unsigned char cr = 13;
+			if (serial->type->write)
+				retval = serial->type->write(NULL,
+								port, &cr, 1);
+			else
+				retval = usb_serial_generic_write(NULL,
+								port, &cr, 1);
+			dbg("%s - return value : %d", __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 && serial->port && serial->port[0]
+				&& serial->port[0] == usbcons_info.port) {
+		usb_serial_console_exit();
+		usb_serial_put(serial);
+	}
+}
+
+void usb_serial_console_init(int serial_debug, int minor)
+{
+	debug = serial_debug;
+
+	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.
+		 */
+		dbg("registering the USB serial console.");
+		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/ap/os/linux/linux-3.4.x/drivers/usb/serial/cp210x.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cp210x.c
new file mode 100644
index 0000000..29bf383
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cp210x.c
@@ -0,0 +1,951 @@
+/*
+ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * 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>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.09"
+#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 *port);
+static void cp210x_get_termios_port(struct usb_serial_port *port,
+	unsigned int *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 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_startup(struct usb_serial *);
+static void cp210x_release(struct usb_serial *);
+static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
+
+static bool debug;
+
+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(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, 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, 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, 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, 0x8477) }, /* Balluff RFID */
+	{ 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, 0x8946) }, /* Ketra N1 Wireless Interface */
+	{ USB_DEVICE(0x10C4, 0x8977) },	/* CEL MeshWorks DevKit Device */
+	{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
+	{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
+	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
+	{ 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(0x13AD, 0x9999) }, /* Baltech card reader */
+	{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+	{ 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(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(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
+	{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
+	{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
+	{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
+	{ } /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct cp210x_port_private {
+	__u8			bInterfaceNumber;
+};
+
+static struct usb_driver cp210x_driver = {
+	.name		= "cp210x",
+	.probe		= usb_serial_probe,
+	.disconnect	= usb_serial_disconnect,
+	.id_table	= id_table,
+};
+
+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,
+	.tiocmget 		= cp210x_tiocmget,
+	.tiocmset		= cp210x_tiocmset,
+	.attach			= cp210x_startup,
+	.release		= cp210x_release,
+	.dtr_rts		= cp210x_dtr_rts
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&cp210x_device, NULL
+};
+
+/* Config request types */
+#define REQTYPE_HOST_TO_DEVICE	0x41
+#define REQTYPE_DEVICE_TO_HOST	0xc1
+
+/* 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
+
+/* 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_get_config
+ * Reads from the CP210x configuration registers
+ * 'size' is specified in bytes.
+ * 'data' is a pointer to a pre-allocated array of integers large
+ * enough to hold 'size' bytes (with 4 bytes to each integer)
+ */
+static int cp210x_get_config(struct usb_serial_port *port, u8 request,
+		unsigned int *data, int size)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	__le32 *buf;
+	int result, i, length;
+
+	/* Number of integers required to contain the array */
+	length = (((size - 1) | 3) + 1)/4;
+
+	buf = kcalloc(length, sizeof(__le32), GFP_KERNEL);
+	if (!buf) {
+		dev_err(&port->dev, "%s - out of memory.\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Issue the request, attempting to read 'size' bytes */
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				request, REQTYPE_DEVICE_TO_HOST, 0x0000,
+				port_priv->bInterfaceNumber, buf, size,
+				USB_CTRL_GET_TIMEOUT);
+
+	/* Convert data into an array of integers */
+	for (i = 0; i < length; i++)
+		data[i] = le32_to_cpu(buf[i]);
+
+	kfree(buf);
+
+	if (result != size) {
+		dbg("%s - Unable to send config request, "
+				"request=0x%x size=%d result=%d",
+				__func__, request, size, result);
+		if (result > 0)
+			result = -EPROTO;
+
+		return result;
+	}
+
+	return 0;
+}
+
+/*
+ * cp210x_set_config
+ * Writes to the CP210x configuration registers
+ * Values less than 16 bits wide are sent directly
+ * 'size' is specified in bytes.
+ */
+static int cp210x_set_config(struct usb_serial_port *port, u8 request,
+		unsigned int *data, int size)
+{
+	struct usb_serial *serial = port->serial;
+	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+	__le32 *buf;
+	int result, i, length;
+
+	/* Number of integers required to contain the array */
+	length = (((size - 1) | 3) + 1)/4;
+
+	buf = kmalloc(length * sizeof(__le32), GFP_KERNEL);
+	if (!buf) {
+		dev_err(&port->dev, "%s - out of memory.\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	/* Array of integers into bytes */
+	for (i = 0; i < length; i++)
+		buf[i] = cpu_to_le32(data[i]);
+
+	if (size > 2) {
+		result = usb_control_msg(serial->dev,
+				usb_sndctrlpipe(serial->dev, 0),
+				request, REQTYPE_HOST_TO_DEVICE, 0x0000,
+				port_priv->bInterfaceNumber, buf, size,
+				USB_CTRL_SET_TIMEOUT);
+	} else {
+		result = usb_control_msg(serial->dev,
+				usb_sndctrlpipe(serial->dev, 0),
+				request, REQTYPE_HOST_TO_DEVICE, data[0],
+				port_priv->bInterfaceNumber, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	}
+
+	kfree(buf);
+
+	if ((size > 2 && result != size) || result < 0) {
+		dbg("%s - Unable to send request, "
+				"request=0x%x size=%d result=%d",
+				__func__, request, size, result);
+		if (result > 0)
+			result = -EPROTO;
+
+		return result;
+	}
+
+	return 0;
+}
+
+/*
+ * cp210x_set_config_single
+ * Convenience function for calling cp210x_set_config on single data values
+ * without requiring an integer pointer
+ */
+static inline int cp210x_set_config_single(struct usb_serial_port *port,
+		u8 request, unsigned int data)
+{
+	return cp210x_set_config(port, request, &data, 2);
+}
+
+/*
+ * cp210x_quantise_baudrate
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static unsigned int cp210x_quantise_baudrate(unsigned int baud) {
+	if (baud <= 300)
+		baud = 300;
+	else if (baud <= 600)      baud = 600;
+	else if (baud <= 1200)     baud = 1200;
+	else if (baud <= 1800)     baud = 1800;
+	else if (baud <= 2400)     baud = 2400;
+	else if (baud <= 4000)     baud = 4000;
+	else if (baud <= 4803)     baud = 4800;
+	else if (baud <= 7207)     baud = 7200;
+	else if (baud <= 9612)     baud = 9600;
+	else if (baud <= 14428)    baud = 14400;
+	else if (baud <= 16062)    baud = 16000;
+	else if (baud <= 19250)    baud = 19200;
+	else if (baud <= 28912)    baud = 28800;
+	else if (baud <= 38601)    baud = 38400;
+	else if (baud <= 51558)    baud = 51200;
+	else if (baud <= 56280)    baud = 56000;
+	else if (baud <= 58053)    baud = 57600;
+	else if (baud <= 64111)    baud = 64000;
+	else if (baud <= 77608)    baud = 76800;
+	else if (baud <= 117028)   baud = 115200;
+	else if (baud <= 129347)   baud = 128000;
+	else if (baud <= 156868)   baud = 153600;
+	else if (baud <= 237832)   baud = 230400;
+	else if (baud <= 254234)   baud = 250000;
+	else if (baud <= 273066)   baud = 256000;
+	else if (baud <= 491520)   baud = 460800;
+	else if (baud <= 567138)   baud = 500000;
+	else if (baud <= 670254)   baud = 576000;
+	else if (baud < 1000000)
+		baud = 921600;
+	else if (baud > 2000000)
+		baud = 2000000;
+	return baud;
+}
+
+static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	result = cp210x_set_config_single(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)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	usb_serial_generic_close(port);
+
+	mutex_lock(&port->serial->disc_mutex);
+	if (!port->serial->disconnected)
+		cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
+	mutex_unlock(&port->serial->disc_mutex);
+}
+
+/*
+ * 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 {
+		unsigned int 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,
+	unsigned int *cflagp, unsigned int *baudp)
+{
+	unsigned int cflag, modem_ctl[4];
+	unsigned int baud;
+	unsigned int bits;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4);
+
+	dbg("%s - baud rate = %d", __func__, baud);
+	*baudp = baud;
+
+	cflag = *cflagp;
+
+	cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+	cflag &= ~CSIZE;
+	switch (bits & BITS_DATA_MASK) {
+	case BITS_DATA_5:
+		dbg("%s - data bits = 5", __func__);
+		cflag |= CS5;
+		break;
+	case BITS_DATA_6:
+		dbg("%s - data bits = 6", __func__);
+		cflag |= CS6;
+		break;
+	case BITS_DATA_7:
+		dbg("%s - data bits = 7", __func__);
+		cflag |= CS7;
+		break;
+	case BITS_DATA_8:
+		dbg("%s - data bits = 8", __func__);
+		cflag |= CS8;
+		break;
+	case BITS_DATA_9:
+		dbg("%s - data bits = 9 (not supported, using 8 data bits)",
+								__func__);
+		cflag |= CS8;
+		bits &= ~BITS_DATA_MASK;
+		bits |= BITS_DATA_8;
+		cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+		break;
+	default:
+		dbg("%s - Unknown number of data bits, using 8", __func__);
+		cflag |= CS8;
+		bits &= ~BITS_DATA_MASK;
+		bits |= BITS_DATA_8;
+		cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+		break;
+	}
+
+	switch (bits & BITS_PARITY_MASK) {
+	case BITS_PARITY_NONE:
+		dbg("%s - parity = NONE", __func__);
+		cflag &= ~PARENB;
+		break;
+	case BITS_PARITY_ODD:
+		dbg("%s - parity = ODD", __func__);
+		cflag |= (PARENB|PARODD);
+		break;
+	case BITS_PARITY_EVEN:
+		dbg("%s - parity = EVEN", __func__);
+		cflag &= ~PARODD;
+		cflag |= PARENB;
+		break;
+	case BITS_PARITY_MARK:
+		dbg("%s - parity = MARK", __func__);
+		cflag |= (PARENB|PARODD|CMSPAR);
+		break;
+	case BITS_PARITY_SPACE:
+		dbg("%s - parity = SPACE", __func__);
+		cflag &= ~PARODD;
+		cflag |= (PARENB|CMSPAR);
+		break;
+	default:
+		dbg("%s - Unknown parity mode, disabling parity", __func__);
+		cflag &= ~PARENB;
+		bits &= ~BITS_PARITY_MASK;
+		cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+		break;
+	}
+
+	cflag &= ~CSTOPB;
+	switch (bits & BITS_STOP_MASK) {
+	case BITS_STOP_1:
+		dbg("%s - stop bits = 1", __func__);
+		break;
+	case BITS_STOP_1_5:
+		dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)",
+								__func__);
+		bits &= ~BITS_STOP_MASK;
+		cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+		break;
+	case BITS_STOP_2:
+		dbg("%s - stop bits = 2", __func__);
+		cflag |= CSTOPB;
+		break;
+	default:
+		dbg("%s - Unknown number of stop bits, using 1 stop bit",
+								__func__);
+		bits &= ~BITS_STOP_MASK;
+		cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+		break;
+	}
+
+	cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
+	if (modem_ctl[0] & 0x0008) {
+		dbg("%s - flow control = CRTSCTS", __func__);
+		cflag |= CRTSCTS;
+	} else {
+		dbg("%s - flow control = NONE", __func__);
+		cflag &= ~CRTSCTS;
+	}
+
+	*cflagp = cflag;
+}
+
+/*
+ * 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)
+{
+	u32 baud;
+
+	baud = tty->termios->c_ospeed;
+
+	/* This maps the requested rate to a rate valid on cp2102 or cp2103,
+	 * or to an arbitrary rate in [1M,2M].
+	 *
+	 * NOTE: B0 is not implemented.
+	 */
+	baud = cp210x_quantise_baudrate(baud);
+
+	dbg("%s - setting baud rate to %u", __func__, baud);
+	if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud,
+							sizeof(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)
+{
+	unsigned int cflag, old_cflag;
+	unsigned int bits;
+	unsigned int modem_ctl[4];
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (!tty)
+		return;
+
+	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_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+		bits &= ~BITS_DATA_MASK;
+		switch (cflag & CSIZE) {
+		case CS5:
+			bits |= BITS_DATA_5;
+			dbg("%s - data bits = 5", __func__);
+			break;
+		case CS6:
+			bits |= BITS_DATA_6;
+			dbg("%s - data bits = 6", __func__);
+			break;
+		case CS7:
+			bits |= BITS_DATA_7;
+			dbg("%s - data bits = 7", __func__);
+			break;
+		case CS8:
+			bits |= BITS_DATA_8;
+			dbg("%s - data bits = 8", __func__);
+			break;
+		/*case CS9:
+			bits |= BITS_DATA_9;
+			dbg("%s - data bits = 9", __func__);
+			break;*/
+		default:
+			dbg("cp210x driver does not "
+					"support the number of bits requested,"
+					" using 8 bit mode");
+				bits |= BITS_DATA_8;
+				break;
+		}
+		if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+			dbg("Number of data bits requested "
+					"not supported by device");
+	}
+
+	if ((cflag     & (PARENB|PARODD|CMSPAR)) !=
+	    (old_cflag & (PARENB|PARODD|CMSPAR))) {
+		cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+		bits &= ~BITS_PARITY_MASK;
+		if (cflag & PARENB) {
+			if (cflag & CMSPAR) {
+			    if (cflag & PARODD) {
+				    bits |= BITS_PARITY_MARK;
+				    dbg("%s - parity = MARK", __func__);
+			    } else {
+				    bits |= BITS_PARITY_SPACE;
+				    dbg("%s - parity = SPACE", __func__);
+			    }
+			} else {
+			    if (cflag & PARODD) {
+				    bits |= BITS_PARITY_ODD;
+				    dbg("%s - parity = ODD", __func__);
+			    } else {
+				    bits |= BITS_PARITY_EVEN;
+				    dbg("%s - parity = EVEN", __func__);
+			    }
+			}
+		}
+		if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+			dbg("Parity mode not supported by device");
+	}
+
+	if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+		cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+		bits &= ~BITS_STOP_MASK;
+		if (cflag & CSTOPB) {
+			bits |= BITS_STOP_2;
+			dbg("%s - stop bits = 2", __func__);
+		} else {
+			bits |= BITS_STOP_1;
+			dbg("%s - stop bits = 1", __func__);
+		}
+		if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+			dbg("Number of stop bits requested "
+					"not supported by device");
+	}
+
+	if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+		cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
+		dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
+				__func__, modem_ctl[0], modem_ctl[1],
+				modem_ctl[2], modem_ctl[3]);
+
+		if (cflag & CRTSCTS) {
+			modem_ctl[0] &= ~0x7B;
+			modem_ctl[0] |= 0x09;
+			modem_ctl[1] = 0x80;
+			dbg("%s - flow control = CRTSCTS", __func__);
+		} else {
+			modem_ctl[0] &= ~0x7B;
+			modem_ctl[0] |= 0x01;
+			modem_ctl[1] |= 0x40;
+			dbg("%s - flow control = NONE", __func__);
+		}
+
+		dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
+				__func__, modem_ctl[0], modem_ctl[1],
+				modem_ctl[2], modem_ctl[3]);
+		cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
+	}
+
+}
+
+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)
+{
+	unsigned int control = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+	}
+
+	dbg("%s - control = 0x%.4x", __func__, control);
+
+	return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
+}
+
+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;
+	unsigned int control;
+	int result;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+
+	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);
+
+	dbg("%s - control = 0x%.2x", __func__, control);
+
+	return result;
+}
+
+static void cp210x_break_ctl (struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int state;
+
+	dbg("%s - port %d", __func__, port->number);
+	if (break_state == 0)
+		state = BREAK_OFF;
+	else
+		state = BREAK_ON;
+	dbg("%s - turning break %s", __func__,
+			state == BREAK_OFF ? "off" : "on");
+	cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
+}
+
+static int cp210x_startup(struct usb_serial *serial)
+{
+	struct cp210x_port_private *port_priv;
+	int i;
+
+	/* cp210x buffers behave strangely unless device is reset */
+	usb_reset_device(serial->dev);
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+		if (!port_priv)
+			return -ENOMEM;
+
+		memset(port_priv, 0x00, sizeof(*port_priv));
+		port_priv->bInterfaceNumber =
+		    serial->interface->cur_altsetting->desc.bInterfaceNumber;
+
+		usb_set_serial_port_data(serial->port[i], port_priv);
+	}
+
+	return 0;
+}
+
+static void cp210x_release(struct usb_serial *serial)
+{
+	struct cp210x_port_private *port_priv;
+	int i;
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port_priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(port_priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+}
+
+module_usb_serial_driver(cp210x_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/cyberjack.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cyberjack.c
new file mode 100644
index 0000000..d39b941
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cyberjack.c
@@ -0,0 +1,486 @@
+/*
+ *  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.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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/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>
+
+#define CYBERJACK_LOCAL_BUF_SIZE 32
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.01"
+#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_startup(struct usb_serial *serial);
+static void cyberjack_disconnect(struct usb_serial *serial);
+static void cyberjack_release(struct usb_serial *serial);
+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_driver cyberjack_driver = {
+	.name =		"cyberjack",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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,
+	.attach =		cyberjack_startup,
+	.disconnect =		cyberjack_disconnect,
+	.release =		cyberjack_release,
+	.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 */
+};
+
+/* do some startup allocations not currently performed by usb_serial_probe() */
+static int cyberjack_startup(struct usb_serial *serial)
+{
+	struct cyberjack_private *priv;
+	int i;
+
+	dbg("%s", __func__);
+
+	/* allocate the private data structure */
+	priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* set initial values */
+	spin_lock_init(&priv->lock);
+	priv->rdtodo = 0;
+	priv->wrfilled = 0;
+	priv->wrsent = 0;
+	usb_set_serial_port_data(serial->port[0], priv);
+
+	init_waitqueue_head(&serial->port[0]->write_wait);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		int result;
+		result = usb_submit_urb(serial->port[i]->interrupt_in_urb,
+					GFP_KERNEL);
+		if (result)
+			dev_err(&serial->dev->dev,
+				"usb_submit_urb(read int) failed\n");
+		dbg("%s - usb_submit_urb(int urb)", __func__);
+	}
+
+	return 0;
+}
+
+static void cyberjack_disconnect(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		usb_kill_urb(serial->port[i]->interrupt_in_urb);
+}
+
+static void cyberjack_release(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		/* My special items, the standard routines free my urbs */
+		kfree(usb_get_serial_port_data(serial->port[i]));
+	}
+}
+
+static int  cyberjack_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	struct cyberjack_private *priv;
+	unsigned long flags;
+	int result = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	dbg("%s - usb_clear_halt", __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 result;
+}
+
+static void cyberjack_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	if (port->serial->dev) {
+		/* shutdown any bulk reads that might be going on */
+		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 cyberjack_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int result;
+	int wrexpected;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __func__);
+		return 0;
+	}
+
+	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
+		dbg("%s - already writing", __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(debug, &port->dev, __func__, count,
+		priv->wrbuf + priv->wrfilled);
+	priv->wrfilled += count;
+
+	if (priv->wrfilled >= 3) {
+		wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
+		dbg("%s - expected data: %d", __func__, wrexpected);
+	} else
+		wrexpected = sizeof(priv->wrbuf);
+
+	if (priv->wrfilled >= wrexpected) {
+		/* We have enough data to begin transmission */
+		int length;
+
+		dbg("%s - transmitting data (frame 1)", __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",
+				__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;
+		}
+
+		dbg("%s - priv->wrsent=%d", __func__, priv->wrsent);
+		dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled);
+
+		if (priv->wrsent >= priv->wrfilled) {
+			dbg("%s - buffer cleaned", __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);
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	int result;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* the urb might have been killed. */
+	if (status)
+		return;
+
+	usb_serial_debug_data(debug, &port->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(&priv->lock);
+
+		old_rdtodo = priv->rdtodo;
+
+		if (old_rdtodo + size < old_rdtodo) {
+			dbg("To many bulk_in urbs to do.");
+			spin_unlock(&priv->lock);
+			goto resubmit;
+		}
+
+		/* "+=" is probably more fault tollerant than "=" */
+		priv->rdtodo += size;
+
+		dbg("%s - rdtodo: %d", __func__, priv->rdtodo);
+
+		spin_unlock(&priv->lock);
+
+		if (!old_rdtodo) {
+			result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (result)
+				dev_err(&port->dev, "%s - failed resubmitting "
+					"read urb, error %d\n",
+					__func__, result);
+			dbg("%s - usb_submit_urb(read urb)", __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");
+	dbg("%s - usb_submit_urb(int urb)", __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 tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	short todo;
+	int result;
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+						urb->actual_length, data);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __func__, status);
+		return;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty) {
+		dbg("%s - ignoring since device not open", __func__);
+		return;
+	}
+	if (urb->actual_length) {
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	spin_lock(&priv->lock);
+
+	/* 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(&priv->lock);
+
+	dbg("%s - rdtodo: %d", __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(&port->dev, "%s - failed resubmitting read "
+				"urb, error %d\n", __func__, result);
+		dbg("%s - usb_submit_urb(read urb)", __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);
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	set_bit(0, &port->write_urbs_free);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __func__, status);
+		return;
+	}
+
+	spin_lock(&priv->lock);
+
+	/* only do something if we have more data to send */
+	if (priv->wrfilled) {
+		int length, blksize, result;
+
+		dbg("%s - transmitting data (frame 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(&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;
+			goto exit;
+		}
+
+		dbg("%s - priv->wrsent=%d", __func__, priv->wrsent);
+		dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled);
+
+		blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
+
+		if (priv->wrsent >= priv->wrfilled ||
+					priv->wrsent >= blksize) {
+			dbg("%s - buffer cleaned", __func__);
+			memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
+			priv->wrfilled = 0;
+			priv->wrsent = 0;
+		}
+	}
+
+exit:
+	spin_unlock(&priv->lock);
+	usb_serial_port_softint(port);
+}
+
+module_usb_serial_driver(cyberjack_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.c
new file mode 100644
index 0000000..b1b8467
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.c
@@ -0,0 +1,1381 @@
+/*
+ * USB Cypress M8 driver
+ *
+ * 	Copyright (C) 2004
+ * 	    Lonnie Mendez (dignome@gmail.com)
+ *	Copyright (C) 2003,2004
+ *	    Neil Whelchel (koyama@firstlight.net)
+ *
+ * 	This program is free software; you can redistribute it and/or modify
+ * 	it under the terms of the GNU General Public License as published by
+ * 	the Free Software Foundation; either version 2 of the License, or
+ * 	(at your option) any later version.
+ *
+ * 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/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/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 debug;
+static bool stats;
+static int interval;
+static bool unstable_bauds;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.10"
+#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);
+
+static struct usb_driver cypress_driver = {
+	.name =		"cypress",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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, diff_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_startup(struct usb_serial *serial);
+static int  cypress_hidcom_startup(struct usb_serial *serial);
+static int  cypress_ca42v2_startup(struct usb_serial *serial);
+static void cypress_release(struct usb_serial *serial);
+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 int  cypress_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+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,
+	.attach =			cypress_earthmate_startup,
+	.release =			cypress_release,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.ioctl =			cypress_ioctl,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.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,
+	.attach =			cypress_hidcom_startup,
+	.release =			cypress_release,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.ioctl =			cypress_ioctl,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.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,
+	.attach =			cypress_ca42v2_startup,
+	.release =			cypress_release,
+	.open =				cypress_open,
+	.close =			cypress_close,
+	.dtr_rts =			cypress_dtr_rts,
+	.write =			cypress_write,
+	.write_room =			cypress_write_room,
+	.ioctl =			cypress_ioctl,
+	.set_termios =			cypress_set_termios,
+	.tiocmget =			cypress_tiocmget,
+	.tiocmset =			cypress_tiocmset,
+	.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) {
+			dbg("%s - failed setting baud rate, device incapable "
+			    "speed %d", __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 */
+			dbg("%s - failed setting baud rate, unsupported speed "
+			    "of %d on Earthmate GPS", __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;
+	u8 *feature_buffer;
+	const unsigned int feature_len = 5;
+	unsigned long flags;
+
+	dbg("%s", __func__);
+
+	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) {
+			dbg("%s - baud rate is changing", __func__);
+			retval = analyze_baud_rate(port, baud_rate);
+			if (retval >= 0) {
+				new_baudrate = retval;
+				dbg("%s - New baud rate set to %d",
+				    __func__, new_baudrate);
+			}
+		}
+		dbg("%s - baud rate is being sent as %d",
+					__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 */
+
+		dbg("%s - device is being sent this feature report:",
+								__func__);
+		dbg("%s - %02X - %02X - %02X - %02X - %02X", __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(&port->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;
+		}
+		dbg("%s - retreiving serial line settings", __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(&port->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->number);
+}
+
+
+/*****************************************************************************
+ * Cypress serial driver functions
+ *****************************************************************************/
+
+
+static int generic_startup(struct usb_serial *serial)
+{
+	struct cypress_private *priv;
+	struct usb_serial_port *port = serial->port[0];
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+		dbg("%s - port %d read & write intervals forced to %d",
+		    __func__, port->number, interval);
+	} else {
+		priv->write_urb_interval = port->interrupt_out_urb->interval;
+		priv->read_urb_interval = port->interrupt_in_urb->interval;
+		dbg("%s - port %d intervals: read=%d write=%d",
+		    __func__, port->number,
+		    priv->read_urb_interval, priv->write_urb_interval);
+	}
+	usb_set_serial_port_data(port, priv);
+
+	return 0;
+}
+
+
+static int cypress_earthmate_startup(struct usb_serial *serial)
+{
+	struct cypress_private *priv;
+	struct usb_serial_port *port = serial->port[0];
+
+	dbg("%s", __func__);
+
+	if (generic_startup(serial)) {
+		dbg("%s - Failed setting up port %d", __func__,
+				port->number);
+		return 1;
+	}
+
+	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 :-( */
+		dbg("%s - Marking this device as unsafe for GET_CONFIG "
+		    "commands", __func__);
+		priv->get_cfg_unsafe = !0;
+	}
+
+	return 0;
+} /* cypress_earthmate_startup */
+
+
+static int cypress_hidcom_startup(struct usb_serial *serial)
+{
+	struct cypress_private *priv;
+
+	dbg("%s", __func__);
+
+	if (generic_startup(serial)) {
+		dbg("%s - Failed setting up port %d", __func__,
+				serial->port[0]->number);
+		return 1;
+	}
+
+	priv = usb_get_serial_port_data(serial->port[0]);
+	priv->chiptype = CT_CYPHIDCOM;
+
+	return 0;
+} /* cypress_hidcom_startup */
+
+
+static int cypress_ca42v2_startup(struct usb_serial *serial)
+{
+	struct cypress_private *priv;
+
+	dbg("%s", __func__);
+
+	if (generic_startup(serial)) {
+		dbg("%s - Failed setting up port %d", __func__,
+				serial->port[0]->number);
+		return 1;
+	}
+
+	priv = usb_get_serial_port_data(serial->port[0]);
+	priv->chiptype = CT_CA42V2;
+
+	return 0;
+} /* cypress_ca42v2_startup */
+
+
+static void cypress_release(struct usb_serial *serial)
+{
+	struct cypress_private *priv;
+
+	dbg("%s - port %d", __func__, serial->port[0]->number);
+
+	/* all open ports are closed at this point */
+
+	priv = usb_get_serial_port_data(serial->port[0]);
+
+	if (priv) {
+		kfifo_free(&priv->write_fifo);
+		kfree(priv);
+	}
+}
+
+
+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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 */
+	if (!port->interrupt_in_urb) {
+		dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n",
+			__func__);
+		return -1;
+	}
+
+	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);
+	}
+	port->port.drain_delay = 256;
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* writing is potentially harmful, lock must be taken */
+	mutex_lock(&port->serial->disc_mutex);
+	if (port->serial->disconnected) {
+		mutex_unlock(&port->serial->disc_mutex);
+		return;
+	}
+	spin_lock_irqsave(&priv->lock, flags);
+	kfifo_reset_out(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s - stopping urbs", __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);
+	mutex_unlock(&port->serial->disc_mutex);
+} /* 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);
+
+	dbg("%s - port %d, %d bytes", __func__, port->number, 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);
+	unsigned long flags;
+
+	if (!priv->comm_is_ok)
+		return;
+
+	dbg("%s - port %d", __func__, port->number);
+	dbg("%s - interrupt out size is %d", __func__,
+						port->interrupt_out_size);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->write_urb_in_use) {
+		dbg("%s - can't write, urb in use", __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++;
+		dbg("%s - line control command being issued", __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;
+	}
+
+	dbg("%s - count is %d", __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(debug, &port->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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	room = kfifo_avail(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+
+	dbg("%s - result = %x", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 int cypress_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+
+	dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	/* This code comes from drivers/char/serial.c and ftdi_sio.c */
+	case TIOCMIWAIT:
+		for (;;) {
+			interruptible_sleep_on(&port->delta_msr_wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			{
+				char diff = priv->diff_status;
+				if (diff == 0)
+					return -EIO; /* no change => error */
+
+				/* consume all events */
+				priv->diff_status = 0;
+
+				/* return 0 if caller wanted to know about
+				   these bits */
+				if (((arg & TIOCM_RNG) && (diff & UART_RI)) ||
+				    ((arg & TIOCM_DSR) && (diff & UART_DSR)) ||
+				    ((arg & TIOCM_CD) && (diff & UART_CD)) ||
+				    ((arg & TIOCM_CTS) && (diff & UART_CTS)))
+					return 0;
+				/* otherwise caller can't care less about what
+				 * happened, and so we continue to wait for
+				 * more events.
+				 */
+			}
+		}
+		return 0;
+	default:
+		break;
+	}
+	dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __func__, cmd);
+	return -ENOIOCTLCMD;
+} /* cypress_ioctl */
+
+
+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);
+	int data_bits, stop_bits, parity_type, parity_enable;
+	unsigned cflag, iflag;
+	unsigned long flags;
+	__u8 oldlines;
+	int linechange = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(&port->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 */
+		dbg("%s - dropping the lines, baud rate 0bps", __func__);
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	} else
+		priv->line_control = (CONTROL_DTR | CONTROL_RTS);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, "
+			"%d data_bits (+5)", __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) {
+		dbg("Using custom termios settings for a baud rate of "
+				"4800bps.");
+		/* 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	chars = kfifo_len(&priv->write_fifo);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s - returns %d", __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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	char tty_flag = TTY_NORMAL;
+	int havedata = 0;
+	int bytes = 0;
+	int result;
+	int i = 0;
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(&urb->dev->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) {
+		dbg("%s - now throttling", __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) {
+		dbg("%s - bad tty pointer - exiting", __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;
+		if (bytes > 2)
+			havedata = 1;
+		break;
+	case packet_format_2:
+		/* This is for the CY7C63743... */
+		priv->current_status = data[0] & 0xF8;
+		bytes = (data[0] & 0x07) + 1;
+		i = 1;
+		if (bytes > 1)
+			havedata = 1;
+		break;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (result < bytes) {
+		dbg("%s - wrong packet size - received %d bytes but packet "
+		    "said %d bytes", __func__, result, bytes);
+		goto continue_read;
+	}
+
+	usb_serial_debug_data(debug, &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) {
+		priv->diff_status |= priv->current_status ^
+			priv->prev_status;
+		wake_up_interruptible(&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 && !(tty->termios->c_cflag & CLOCAL) &&
+			!(priv->current_status & UART_CD)) {
+		dbg("%s - calling hangup", __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;
+		dbg("%s - Parity Error detected", __func__);
+	} else
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* process read if there is data other than line status */
+	if (tty && bytes > i) {
+		tty_insert_flip_string_fixed_flag(tty, data + i,
+				tty_flag, bytes - i);
+		tty_flip_buffer_push(tty);
+	}
+
+	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(&urb->dev->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);
+	int result;
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+						__func__, status);
+		priv->write_urb_in_use = 0;
+		return;
+	case -EPIPE: /* no break needed; clear halt and resubmit */
+		if (!priv->comm_is_ok)
+			break;
+		usb_clear_halt(port->serial->dev, 0x02);
+		/* error in the urb, so we have to resubmit it */
+		dbg("%s - nonzero write bulk status received: %d",
+			__func__, status);
+		port->interrupt_out_urb->transfer_buffer_length = 1;
+		result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
+		if (!result)
+			return;
+		dev_err(&urb->dev->dev,
+			"%s - failed resubmitting write urb, error %d\n",
+							__func__, result);
+		cypress_set_dead(port);
+		break;
+	default:
+		dev_err(&urb->dev->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(cypress_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.h
new file mode 100644
index 0000000..ce13e61
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/cypress_m8.h
@@ -0,0 +1,74 @@
+#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 - flow control - host to device */
+#define UART_DSR	0x20	/* data set ready - flow control - device to host */
+#define CONTROL_RTS	0x10	/* request to send - flow control - host to device */
+#define UART_CTS	0x10	/* clear to send - flow control - device to host */
+#define UART_RI		0x80	/* ring indicator - modem - device to host */
+#define UART_CD		0x40	/* carrier detect - modem - device to host */
+#define CYP_ERROR	0x08	/* received from input report - device to host */
+/* Note - the below has nothing to do with the "feature report" reset */
+#define CONTROL_RESET	0x08	/* sent with output report - host to device */
+
+/* End of RS-232 protocol definitions */
+
+#endif /* CYPRESS_M8_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/digi_acceleport.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/digi_acceleport.c
new file mode 100644
index 0000000..999f91b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/digi_acceleport.c
@@ -0,0 +1,1590 @@
+/*
+*  Digi AccelePort USB-4 and USB-2 Serial Converters
+*
+*  Copyright 2000 by Digi International
+*
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  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/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/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/usb/serial.h>
+
+/* Defines */
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.80.1.2"
+#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;
+	wait_queue_head_t dp_modem_change_wait;
+	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(struct usb_serial_port *port);
+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 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);
+
+
+/* Statics */
+
+static bool debug;
+
+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);
+
+static struct usb_driver digi_driver = {
+	.name =		"digi_acceleport",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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,
+	.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,
+};
+
+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,
+	.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,
+};
+
+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);
+	digi_wakeup_write(port);
+	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+}
+
+static void digi_wakeup_write(struct usb_serial_port *port)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->port);
+	if (tty) {
+		tty_wakeup(tty);
+		tty_kref_put(tty);
+	}
+}
+
+
+/*
+ *  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;
+
+	dbg("digi_write_oob_command: TOP: port=%d, count=%d", 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;
+
+	dbg("digi_write_inb_command: TOP: port=%d, count=%d",
+		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;
+
+
+	dbg("digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
+		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);
+
+
+	dbg("digi_rx_throttle: TOP: port=%d", priv->dp_port_num);
+
+	/* 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);
+
+	dbg("digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num);
+
+	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);
+	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;
+
+	dbg("digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", 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 (!(tty->termios->c_cflag & CRTSCTS) ||
+			    !test_bit(TTY_THROTTLED, &tty->flags))
+				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:
+			dbg("digi_set_termios: can't handle word size %d",
+				(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;
+			tty->hw_stopped = 0;
+		}
+
+		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)
+		dbg("digi_set_termios: write oob failed, ret=%d", 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;
+
+	dbg("%s: TOP: port=%d", __func__, priv->dp_port_num);
+
+	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;
+
+	dbg("%s: TOP: port=%d", __func__, priv->dp_port_num);
+
+	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;
+
+	dbg("digi_write: TOP: port=%d, count=%d, in_interrupt=%ld",
+		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);
+	dbg("digi_write: returning %d", 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;
+	int ret = 0;
+	int status = urb->status;
+
+	dbg("digi_write_bulk_callback: TOP, status=%d", 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) {
+		dbg("digi_write_bulk_callback: oob callback");
+		spin_lock(&priv->dp_port_lock);
+		priv->dp_write_urb_in_use = 0;
+		wake_up_interruptible(&port->write_wait);
+		spin_unlock(&priv->dp_port_lock);
+		return;
+	}
+
+	/* try to send any buffered data on this port */
+	spin_lock(&priv->dp_port_lock);
+	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 */
+	digi_wakeup_write(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(&priv->dp_port_lock);
+	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);
+	dbg("digi_write_room: port=%d, room=%d", 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) {
+		dbg("digi_chars_in_buffer: port=%d, chars=%d",
+			priv->dp_port_num, port->bulk_out_size - 2);
+		/* return(port->bulk_out_size - 2); */
+		return 256;
+	} else {
+		dbg("digi_chars_in_buffer: port=%d, chars=%d",
+			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;
+
+	dbg("digi_open: TOP: port=%d", priv->dp_port_num);
+
+	/* 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)
+		dbg("digi_open: write oob failed, ret=%d", 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);
+
+	dbg("digi_close: TOP: port=%d", priv->dp_port_num);
+
+	mutex_lock(&port->serial->disc_mutex);
+	/* if disconnected, just clear flags */
+	if (port->serial->disconnected)
+		goto exit;
+
+	if (port->serial->dev) {
+		/* 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)
+			dbg("digi_close: write oob failed, ret=%d", 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);
+	dbg("digi_close: done");
+}
+
+
+/*
+ *  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_startup(struct usb_serial *serial)
+{
+
+	int i;
+	struct digi_port *priv;
+	struct digi_serial *serial_priv;
+
+	dbg("digi_startup: TOP");
+
+	/* allocate the private data structures for all ports */
+	/* number of regular ports + 1 for the out-of-band port */
+	for (i = 0; i < serial->type->num_ports + 1; i++) {
+		/* allocate port private structure */
+		priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL);
+		if (priv == NULL) {
+			while (--i >= 0)
+				kfree(usb_get_serial_port_data(serial->port[i]));
+			return 1;			/* error */
+		}
+
+		/* initialize port private structure */
+		spin_lock_init(&priv->dp_port_lock);
+		priv->dp_port_num = i;
+		priv->dp_out_buf_len = 0;
+		priv->dp_write_urb_in_use = 0;
+		priv->dp_modem_signals = 0;
+		init_waitqueue_head(&priv->dp_modem_change_wait);
+		priv->dp_transmit_idle = 0;
+		init_waitqueue_head(&priv->dp_transmit_idle_wait);
+		priv->dp_throttled = 0;
+		priv->dp_throttle_restart = 0;
+		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 = serial->port[i];
+		/* initialize write wait queue for this port */
+		init_waitqueue_head(&serial->port[i]->write_wait);
+
+		usb_set_serial_port_data(serial->port[i], priv);
+	}
+
+	/* allocate serial private structure */
+	serial_priv = kmalloc(sizeof(struct digi_serial), GFP_KERNEL);
+	if (serial_priv == NULL) {
+		for (i = 0; i < serial->type->num_ports + 1; i++)
+			kfree(usb_get_serial_port_data(serial->port[i]));
+		return 1;			/* error */
+	}
+
+	/* initialize serial private structure */
+	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];
+	serial_priv->ds_device_started = 0;
+	usb_set_serial_data(serial, serial_priv);
+
+	return 0;
+}
+
+
+static void digi_disconnect(struct usb_serial *serial)
+{
+	int i;
+	dbg("digi_disconnect: TOP, in_interrupt()=%ld", in_interrupt());
+
+	/* 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)
+{
+	int i;
+	dbg("digi_release: TOP, in_interrupt()=%ld", in_interrupt());
+
+	/* free the private data structures for all ports */
+	/* number of regular ports + 1 for the out-of-band port */
+	for (i = 0; i < serial->type->num_ports + 1; i++)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+	kfree(usb_get_serial_data(serial));
+}
+
+
+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;
+
+	dbg("digi_read_bulk_callback: TOP");
+
+	/* 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 tty_struct *tty;
+	struct digi_port *priv = usb_get_serial_port_data(port);
+	int opcode = ((unsigned char *)urb->transfer_buffer)[0];
+	int len = ((unsigned char *)urb->transfer_buffer)[1];
+	int port_status = ((unsigned char *)urb->transfer_buffer)[2];
+	unsigned char *data = ((unsigned char *)urb->transfer_buffer) + 3;
+	int flag, throttled;
+	int status = urb->status;
+
+	/* do not process callbacks on closed ports */
+	/* but do continue the read chain */
+	if (urb->status == -ENOENT)
+		return 0;
+
+	/* short/multiple packet check */
+	if (urb->actual_length != len + 2) {
+		dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, "
+			"status=%d, port=%d, opcode=%d, len=%d, "
+			"actual_length=%d, status=%d\n", __func__, status,
+			priv->dp_port_num, opcode, len, urb->actual_length,
+			port_status);
+		return -1;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	spin_lock(&priv->dp_port_lock);
+
+	/* 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 (tty && opcode == DIGI_CMD_RECEIVE_DATA) {
+		/* get flag from port_status */
+		flag = 0;
+
+		/* overrun is special, not associated with a char */
+		if (port_status & DIGI_OVERRUN_ERROR)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+		/* break takes precedence over parity, */
+		/* which takes precedence over framing errors */
+		if (port_status & DIGI_BREAK_ERROR)
+			flag = TTY_BREAK;
+		else if (port_status & DIGI_PARITY_ERROR)
+			flag = TTY_PARITY;
+		else if (port_status & DIGI_FRAMING_ERROR)
+			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(tty, data, flag,
+									len);
+			tty_flip_buffer_push(tty);
+		}
+	}
+	spin_unlock(&priv->dp_port_lock);
+	tty_kref_put(tty);
+
+	if (opcode == DIGI_CMD_RECEIVE_DISABLE)
+		dbg("%s: got RECEIVE_DISABLE", __func__);
+	else if (opcode != DIGI_CMD_RECEIVE_DATA)
+		dbg("%s: unknown opcode: %d", __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);
+	int opcode, line, status, val;
+	int i;
+	unsigned int rts;
+
+	dbg("digi_read_oob_callback: port=%d, len=%d",
+			priv->dp_port_num, urb->actual_length);
+
+	/* handle each oob command */
+	for (i = 0; i < urb->actual_length - 3;) {
+		opcode = ((unsigned char *)urb->transfer_buffer)[i++];
+		line = ((unsigned char *)urb->transfer_buffer)[i++];
+		status = ((unsigned char *)urb->transfer_buffer)[i++];
+		val = ((unsigned char *)urb->transfer_buffer)[i++];
+
+		dbg("digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d",
+			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 = tty->termios->c_cflag & CRTSCTS;
+		
+		if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+			spin_lock(&priv->dp_port_lock);
+			/* 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->hw_stopped = 0;
+					digi_wakeup_write(port);
+				}
+			} else {
+				priv->dp_modem_signals &= ~TIOCM_CTS;
+				/* port must be open to use tty struct */
+				if (rts)
+					tty->hw_stopped = 1;
+			}
+			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;
+
+			wake_up_interruptible(&priv->dp_modem_change_wait);
+			spin_unlock(&priv->dp_port_lock);
+		} else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
+			spin_lock(&priv->dp_port_lock);
+			priv->dp_transmit_idle = 1;
+			wake_up_interruptible(&priv->dp_transmit_idle_wait);
+			spin_unlock(&priv->dp_port_lock);
+		} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
+			wake_up_interruptible(&priv->dp_flush_wait);
+		}
+		tty_kref_put(tty);
+	}
+	return 0;
+
+}
+
+module_usb_serial_driver(digi_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/empeg.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/empeg.c
new file mode 100644
index 0000000..5b99fc0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/empeg.c
@@ -0,0 +1,148 @@
+/*
+ * 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)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License, as published by
+ *	the Free Software Foundation, version 2.
+ *
+ * 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>
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.3"
+#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_driver empeg_driver = {
+	.name =		"empeg",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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;
+
+	dbg("%s", __func__);
+
+	if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
+		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
+			serial->dev->actconfig->desc.bConfigurationValue);
+		return -ENODEV;
+	}
+	dbg("%s - reset config", __func__);
+	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(empeg_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb.c
new file mode 100644
index 0000000..3cfc762
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb.c
@@ -0,0 +1,61 @@
+/*
+ * EZ-USB specific functions used by some of the USB to Serial drivers.
+ *
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
+#define CPUCS_REG    0x7F92
+
+int ezusb_writememory(struct usb_serial *serial, int address,
+				unsigned char *data, int length, __u8 request)
+{
+	int result;
+	unsigned char *transfer_buffer;
+
+	/* dbg("ezusb_writememory %x, %d", address, length); */
+	if (!serial->dev) {
+		printk(KERN_ERR "ezusb: %s - no physical device present, "
+		       "failing.\n", __func__);
+		return -ENODEV;
+	}
+
+	transfer_buffer = kmemdup(data, length, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n",
+							__func__, length);
+		return -ENOMEM;
+	}
+	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+		     request, 0x40, address, 0, transfer_buffer, length, 3000);
+	kfree(transfer_buffer);
+	return result;
+}
+EXPORT_SYMBOL_GPL(ezusb_writememory);
+
+int ezusb_set_reset(struct usb_serial *serial, unsigned char reset_bit)
+{
+	int response;
+
+	/* dbg("%s - %d", __func__, reset_bit); */
+	response = ezusb_writememory(serial, CPUCS_REG, &reset_bit, 1, 0xa0);
+	if (response < 0)
+		dev_err(&serial->dev->dev, "%s- %d failed\n",
+						__func__, reset_bit);
+	return response;
+}
+EXPORT_SYMBOL_GPL(ezusb_set_reset);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb_convert.pl b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb_convert.pl
new file mode 100644
index 0000000..13f1146
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ezusb_convert.pl
@@ -0,0 +1,50 @@
+#! /usr/bin/perl -w
+
+
+# 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/f81232.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/f81232.c
new file mode 100644
index 0000000..88c0b19
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/f81232.c
@@ -0,0 +1,405 @@
+/*
+ * Fintek F81232 USB to serial adaptor driver
+ *
+ * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
+ * Copyright (C) 2012 Linux Foundation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#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/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>
+
+static bool debug;
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x1934, 0x0706) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+#define CONTROL_DTR			0x01
+#define CONTROL_RTS			0x02
+
+#define UART_STATE			0x08
+#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
+
+struct f81232_private {
+	spinlock_t lock;
+	wait_queue_head_t delta_msr_wait;
+	u8 line_control;
+	u8 line_status;
+};
+
+static void f81232_update_line_status(struct usb_serial_port *port,
+				      unsigned char *data,
+				      unsigned int actual_length)
+{
+}
+
+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;
+
+	dbg("%s (%d)", __func__, port->number);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __func__,
+		    status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &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;
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	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);
+	wake_up_interruptible(&priv->delta_msr_wait);
+
+	if (!urb->actual_length)
+		return;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		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;
+	dbg("%s - tty_flag = %d", __func__, tty_flag);
+
+	/* overrun is special, not associated with a char */
+	if (line_status & UART_OVERRUN_ERROR)
+		tty_insert_flip_char(tty, 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(tty, data[i], tty_flag);
+	} else {
+		tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+							urb->actual_length);
+	}
+
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static int set_control_lines(struct usb_device *dev, u8 value)
+{
+	/* FIXME - Stubbed out for now */
+	return 0;
+}
+
+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_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	/* FIXME - Stubbed out for now */
+
+	/* Don't change anything if nothing has changed */
+	if (!tty_termios_hw_change(tty->termios, old_termios))
+		return;
+
+	/* Do the real work here... */
+}
+
+static int f81232_tiocmget(struct tty_struct *tty)
+{
+	/* FIXME - Stubbed out for now */
+	return 0;
+}
+
+static int f81232_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	/* FIXME - Stubbed out for now */
+	return 0;
+}
+
+static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ktermios tmp_termios;
+	int result;
+
+	/* Setup termios */
+	if (tty)
+		f81232_set_termios(tty, port, &tmp_termios);
+
+	dbg("%s - submitting interrupt urb", __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;
+	}
+
+	result = usb_serial_generic_open(tty, port);
+	if (result) {
+		usb_kill_urb(port->interrupt_in_urb);
+		return result;
+	}
+
+	port->port.drain_delay = 256;
+	return 0;
+}
+
+static void f81232_close(struct usb_serial_port *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)
+{
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Change DTR and RTS */
+	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);
+	set_control_lines(port->serial->dev, control);
+}
+
+static int f81232_carrier_raised(struct usb_serial_port *port)
+{
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+	if (priv->line_status & UART_DCD)
+		return 1;
+	return 0;
+}
+
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct f81232_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int prevstatus;
+	unsigned int status;
+	unsigned int changed;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prevstatus = priv->line_status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (1) {
+		interruptible_sleep_on(&priv->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->line_status;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prevstatus ^ status;
+
+		if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+		    ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+		    ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
+		    ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
+			return 0;
+		}
+		prevstatus = status;
+	}
+	/* NOTREACHED */
+	return 0;
+}
+
+static int f81232_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	struct serial_struct ser;
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		memset(&ser, 0, sizeof ser);
+		ser.type = PORT_16654;
+		ser.line = port->serial->minor;
+		ser.port = port->number;
+		ser.baud_base = 460800;
+
+		if (copy_to_user((void __user *)arg, &ser, sizeof ser))
+			return -EFAULT;
+
+		return 0;
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		return wait_modem_info(port, arg);
+	default:
+		dbg("%s not supported = 0x%04x", __func__, cmd);
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int f81232_startup(struct usb_serial *serial)
+{
+	struct f81232_private *priv;
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kzalloc(sizeof(struct f81232_private), GFP_KERNEL);
+		if (!priv)
+			goto cleanup;
+		spin_lock_init(&priv->lock);
+		init_waitqueue_head(&priv->delta_msr_wait);
+		usb_set_serial_port_data(serial->port[i], priv);
+	}
+	return 0;
+
+cleanup:
+	for (--i; i >= 0; --i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
+}
+
+static void f81232_release(struct usb_serial *serial)
+{
+	int i;
+	struct f81232_private *priv;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+	}
+}
+
+static struct usb_driver f81232_driver = {
+	.name =		"f81232",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+	.suspend =      usb_serial_suspend,
+	.resume =       usb_serial_resume,
+	.no_dynamic_id = 	1,
+	.supports_autosuspend =	1,
+};
+
+static struct usb_serial_driver f81232_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"f81232",
+	},
+	.id_table =		id_table,
+	.usb_driver = 		&f81232_driver,
+	.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,
+	.process_read_urb =	f81232_process_read_urb,
+	.read_int_callback =	f81232_read_int_callback,
+	.attach =		f81232_startup,
+	.release =		f81232_release,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&f81232_device,
+	NULL,
+};
+
+module_usb_serial_driver(f81232_driver, serial_drivers);
+
+MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
+MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
+MODULE_LICENSE("GPL v2");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.c
new file mode 100644
index 0000000..1e4899c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.c
@@ -0,0 +1,2670 @@
+/*
+ * USB FTDI SIO driver
+ *
+ *	Copyright (C) 2009 - 2010
+ *	    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)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/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/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"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.6.0"
+#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"
+
+static bool debug;
+static __u16 vendor = FTDI_VID;
+static __u16 product;
+
+struct ftdi_private {
+	struct kref kref;
+	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_urb_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 */
+	struct async_icount	icount;
+	char prev_status;        /* Used for TIOCMIWAIT */
+	char transmit_empty;	/* If transmitter is empty or not */
+	struct usb_serial_port *port;
+	__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_mtxorb_hack_setup(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 struct ftdi_sio_quirk ftdi_jtag_quirk = {
+	.probe	= ftdi_jtag_probe,
+};
+
+static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = {
+	.probe  = ftdi_mtxorb_hack_setup,
+};
+
+static struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
+	.probe	= ftdi_NDI_device_setup,
+};
+
+static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
+	.port_probe = ftdi_USB_UIRT_setup,
+};
+
+static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
+	.port_probe = ftdi_HE_TIRA1_setup,
+};
+
+static struct ftdi_sio_quirk ftdi_stmclite_quirk = {
+	.probe	= ftdi_stmclite_probe,
+};
+
+static 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 via module params product/vendor or
+ * /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
+ */
+static 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),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ 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),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ 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),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+	{ 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) },
+	/*
+	 * 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, 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(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(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
+		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ 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(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(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_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) },
+	/* 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) },
+	{ },					/* Optional parameter entry */
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+static struct usb_driver ftdi_driver = {
+	.name =		"ftdi_sio",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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 */
+
+#define FTDI_IMPL_ASYNC_FLAGS = (ASYNC_SPD_HI | ASYNC_SPD_VHI \
+ | ASYNC_SPD_CUST | ASYNC_SPD_SHI | ASYNC_SPD_WARP)
+
+/* 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_close(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_get_icount(struct tty_struct *tty,
+			   struct serial_icounter_struct *icount);
+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 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,
+	.close =		ftdi_close,
+	.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,
+	.get_icount =           ftdi_get_icount,
+	.ioctl =		ftdi_ioctl,
+	.set_termios =		ftdi_set_termios,
+	.break_ctl =		ftdi_break_ctl,
+};
+
+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 */
+
+/* High and low are for DTR, RTS etc etc */
+#define HIGH 1
+#define LOW 0
+
+/*
+ * ***************************************************************************
+ * 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);
+	unsigned urb_value;
+	int rv;
+
+	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+		dbg("%s - DTR|RTS not being set|cleared", __func__);
+		return 0;	/* no change */
+	}
+
+	clear &= ~set;	/* 'set' takes precedence over 'clear' */
+	urb_value = 0;
+	if (clear & TIOCM_DTR)
+		urb_value |= FTDI_SIO_SET_DTR_LOW;
+	if (clear & TIOCM_RTS)
+		urb_value |= FTDI_SIO_SET_RTS_LOW;
+	if (set & TIOCM_DTR)
+		urb_value |= FTDI_SIO_SET_DTR_HIGH;
+	if (set & TIOCM_RTS)
+		urb_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,
+			       urb_value, priv->interface,
+			       NULL, 0, WDR_TIMEOUT);
+	if (rv < 0) {
+		dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",
+				__func__,
+				(set & TIOCM_DTR) ? "HIGH" :
+				(clear & TIOCM_DTR) ? "LOW" : "unchanged",
+				(set & TIOCM_RTS) ? "HIGH" :
+				(clear & TIOCM_RTS) ? "LOW" : "unchanged");
+	} else {
+		dbg("%s - DTR %s, RTS %s", __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);
+	__u32 div_value = 0;
+	int div_okay = 1;
+	int baud;
+
+	/*
+	 * The logic involved in setting the baudrate can be cleanly split into
+	 * 3 steps.
+	 * 1. Standard baud rates are set in tty->termios->c_cflag
+	 * 2. If these are not enough, you can set any speed using alt_speed as
+	 * follows:
+	 *    - set tty->termios->c_cflag speed to B38400
+	 *    - set your real speed in tty->alt_speed; it gets ignored when
+	 *      alt_speed==0, (or)
+	 *    - call TIOCSSERIAL ioctl with (struct serial_struct) set as
+	 *	follows:
+	 *      flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP],
+	 *	this just sets alt_speed to (HI: 57600, VHI: 115200,
+	 *	SHI: 230400, WARP: 460800)
+	 * ** Steps 1, 2 are done courtesy of tty_get_baud_rate
+	 * 3. You can also set baud rate by setting custom divisor as follows
+	 *    - set tty->termios->c_cflag speed to B38400
+	 *    - call TIOCSSERIAL ioctl with (struct serial_struct) set as
+	 *	follows:
+	 *      o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
+	 *      o custom_divisor set to baud_base / your_new_baudrate
+	 * ** Step 3 is done courtesy of code borrowed from serial.c
+	 *    I should really spend some time and separate + move this common
+	 *    code to serial.c, it is replicated in nearly every serial driver
+	 *    you see.
+	 */
+
+	/* 1. Get the baud rate from the tty settings, this observes
+	      alt_speed hack */
+
+	baud = tty_get_baud_rate(tty);
+	dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud);
+
+	/* 2. Observe 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;
+		dbg("%s - custom divisor %d sets baud rate to %d",
+				__func__, priv->custom_divisor, baud);
+	}
+
+	/* 3. Convert baudrate to device-specific divisor */
+
+	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) {
+			dbg("%s - Baudrate (%d) requested is not supported",
+							__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 {
+			dbg("%s - Baud rate too high!", __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 (((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 == product_id)) &&
+			    (baud == 19200)) {
+				baud = 1200000;
+			}
+			div_value = ftdi_232bm_baud_to_divisor(baud);
+		} else {
+			dbg("%s - Baud rate too high!", __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 {
+			dbg("%s - Baud rate too high!", __func__);
+			div_value = ftdi_232bm_baud_to_divisor(9600);
+			div_okay = 0;
+			baud = 9600;
+		}
+		break;
+	} /* priv->chip_type */
+
+	if (div_okay) {
+		dbg("%s - Baud rate set to %d (divisor 0x%lX) on chip %s",
+			__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 urb_value;
+	__u16 urb_index;
+	__u32 urb_index_value;
+	int rv;
+
+	urb_index_value = get_ftdi_divisor(tty, port);
+	urb_value = (__u16)urb_index_value;
+	urb_index = (__u16)(urb_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? */
+		urb_index = (__u16)((urb_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,
+			    urb_value, urb_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->flags & ASYNC_LOW_LATENCY)
+		l = 1;
+
+	dbg("%s: setting latency timer = %i", __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;
+
+	dbg("%s", __func__);
+
+	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 < 0)
+		dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
+	else
+		priv->latency = buf[0];
+
+	kfree(buf);
+
+	return rv;
+}
+
+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;
+
+	if (!retinfo)
+		return -EFAULT;
+	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 & ~ASYNC_USR_MASK) !=
+		     (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;
+
+	write_latency_timer(port);
+
+check_and_exit:
+	if ((old_priv.flags & ASYNC_SPD_MASK) !=
+	     (priv->flags & ASYNC_SPD_MASK)) {
+		if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			tty->alt_speed = 57600;
+		else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			tty->alt_speed = 115200;
+		else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			tty->alt_speed = 230400;
+		else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			tty->alt_speed = 460800;
+		else
+			tty->alt_speed = 0;
+	}
+	if (((old_priv.flags & ASYNC_SPD_MASK) !=
+	     (priv->flags & ASYNC_SPD_MASK)) ||
+	    (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
+	     (old_priv.custom_divisor != priv->custom_divisor))) {
+		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,
+			struct serial_struct __user *retinfo)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	unsigned int result = 0;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	if (priv->transmit_empty)
+		result = TIOCSER_TEMT;
+
+	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;
+	dbg("%s: bcdDevice = 0x%x, bNumInterfaces = %u", __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) {
+			dbg("%s: something fishy - bcdDevice too low for multi-interface device",
+					__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) */
+		/* (It might be a BM because of the iSerialNumber bug,
+		 * but it will still work as an AM device.) */
+		priv->chip_type = FT8U232AM;
+	} 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_serial *serial = port->serial;
+	struct usb_device *udev = serial->dev;
+
+	struct usb_interface *interface = serial->interface;
+	struct usb_endpoint_descriptor *ep_desc;
+
+	unsigned num_endpoints;
+	unsigned i;
+
+	num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+	dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
+
+	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++) {
+		dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
+			interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+		ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+		if (ep_desc->wMaxPacketSize == 0) {
+			ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
+			dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i);
+		}
+	}
+
+	/* set max packet size based on descriptor */
+	priv->max_packet_size = usb_endpoint_maxp(ep_desc);
+
+	dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
+}
+
+
+/*
+ * ***************************************************************************
+ * Sysfs Attribute
+ * ***************************************************************************
+ */
+
+static ssize_t show_latency_timer(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 store_latency_timer(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);
+	int v = simple_strtoul(valbuf, NULL, 10);
+	int rv;
+
+	priv->latency = v;
+	rv = write_latency_timer(port);
+	if (rv < 0)
+		return -EIO;
+	return count;
+}
+
+/* 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 store_event_char(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;
+	int v = simple_strtoul(valbuf, NULL, 10);
+	int rv;
+
+	dbg("%s: setting event char = %i", __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) {
+		dbg("Unable to write event character: %i", rv);
+		return -EIO;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer,
+							store_latency_timer);
+static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_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;
+
+	dbg("%s", __func__);
+
+	/* 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) {
+		dbg("sysfs attributes for %s", 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);
+
+	dbg("%s", __func__);
+
+	/* 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)
+{
+	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;
+	struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+
+
+	dbg("%s", __func__);
+
+	priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,
+					sizeof(struct ftdi_private));
+		return -ENOMEM;
+	}
+
+	kref_init(&priv->kref);
+	mutex_init(&priv->cfg_lock);
+	memset(&priv->icount, 0x00, sizeof(priv->icount));
+
+	priv->flags = ASYNC_LOW_LATENCY;
+
+	if (quirk && quirk->port_probe)
+		quirk->port_probe(priv);
+
+	priv->port = port;
+	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)
+{
+	dbg("%s", __func__);
+
+	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)
+{
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%s setting NDI device latency to %d", __func__, latency);
+	dev_info(&udev->dev, "NDI device with a latency value of %d", 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;
+
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%s", __func__);
+
+	if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems"))
+		return ftdi_jtag_probe(serial);
+
+	if (udev->product &&
+		(!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;
+
+	dbg("%s", __func__);
+
+	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;
+}
+
+/*
+ * The Matrix Orbital VK204-25-USB has an invalid IN endpoint.
+ * We have to correct it if we want to read from it.
+ */
+static int ftdi_mtxorb_hack_setup(struct usb_serial *serial)
+{
+	struct usb_host_endpoint *ep = serial->dev->ep_in[1];
+	struct usb_endpoint_descriptor *ep_desc = &ep->desc;
+
+	if (ep->enabled && ep_desc->wMaxPacketSize == 0) {
+		ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
+		dev_info(&serial->dev->dev,
+			 "Fixing invalid wMaxPacketSize on read pipe\n");
+	}
+
+	return 0;
+}
+
+static void ftdi_sio_priv_release(struct kref *k)
+{
+	struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
+
+	kfree(priv);
+}
+
+static int ftdi_sio_port_remove(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	dbg("%s", __func__);
+
+	wake_up_interruptible(&port->delta_msr_wait);
+
+	remove_sysfs_attrs(port);
+
+	kref_put(&priv->kref, ftdi_sio_priv_release);
+
+	return 0;
+}
+
+static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct ktermios dummy;
+	struct usb_device *dev = port->serial->dev;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	int result;
+
+	dbg("%s", __func__);
+
+	/* 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) {
+		memset(&dummy, 0, sizeof(dummy));
+		ftdi_set_termios(tty, port, &dummy);
+	}
+
+	/* Start reading from the device */
+	result = usb_serial_generic_open(tty, port);
+	if (!result)
+		kref_get(&priv->kref);
+
+	return result;
+}
+
+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);
+}
+
+/*
+ * usbserial:__serial_close  only calls ftdi_close if the point is open
+ *
+ *   This only gets called when it is the last close
+ */
+static void ftdi_close(struct usb_serial_port *port)
+{
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+	dbg("%s", __func__);
+
+	usb_serial_generic_close(port);
+	kref_put(&priv->kref, ftdi_sio_priv_release);
+}
+
+/* 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;
+			priv->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);
+		priv->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 tty_struct *tty,
+		struct usb_serial_port *port, struct ftdi_private *priv,
+		char *packet, int len)
+{
+	int i;
+	char status;
+	char flag;
+	char *ch;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (len < 2) {
+		dbg("malformed packet");
+		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)
+			priv->icount.cts++;
+		if (diff_status & FTDI_RS0_DSR)
+			priv->icount.dsr++;
+		if (diff_status & FTDI_RS0_RI)
+			priv->icount.rng++;
+		if (diff_status & FTDI_RS0_RLSD)
+			priv->icount.dcd++;
+
+		wake_up_interruptible(&port->delta_msr_wait);
+		priv->prev_status = status;
+	}
+
+	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;
+			priv->icount.brk++;
+			usb_serial_handle_break(port);
+		} else if (packet[1] & FTDI_RS_PE) {
+			flag = TTY_PARITY;
+			priv->icount.parity++;
+		} else if (packet[1] & FTDI_RS_FE) {
+			flag = TTY_FRAME;
+			priv->icount.frame++;
+		}
+		/* Overrun is special, not associated with a char */
+		if (packet[1] & FTDI_RS_OE) {
+			priv->icount.overrun++;
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		}
+	}
+
+	/* 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 */
+	priv->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(tty, *ch, flag);
+		}
+	} else {
+		tty_insert_flip_string_fixed_flag(tty, ch, flag, len);
+	}
+
+	return len;
+}
+
+static void ftdi_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct tty_struct *tty;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	char *data = (char *)urb->transfer_buffer;
+	int i;
+	int len;
+	int count = 0;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	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(tty, port, priv, &data[i], len);
+	}
+
+	if (count)
+		tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+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 urb_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_urb_value NEVER has the break bit set in it */
+
+	if (break_state)
+		urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
+	else
+		urb_value = priv->last_set_data_urb_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,
+			urb_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);
+	}
+
+	dbg("%s break state is %d - urb is %d", __func__,
+						break_state, urb_value);
+
+}
+
+/* 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 ftdi_private *priv = usb_get_serial_port_data(port);
+	struct ktermios *termios = tty->termios;
+	unsigned int cflag = termios->c_cflag;
+	__u16 urb_value; /* will hold the new flags */
+
+	/* Added for xon/xoff support */
+	unsigned int iflag = termios->c_iflag;
+	unsigned char vstop;
+	unsigned char vstart;
+
+	dbg("%s", __func__);
+
+	/* Force baud rate if this device requires it, unless it is set to
+	   B0. */
+	if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) {
+		dbg("%s: forcing baud rate for this device", __func__);
+		tty_encode_baud_rate(tty, priv->force_baud,
+					priv->force_baud);
+	}
+
+	/* Force RTS-CTS if this device requires it. */
+	if (priv->force_rtscts) {
+		dbg("%s: forcing rtscts for this device", __func__);
+		termios->c_cflag |= CRTSCTS;
+	}
+
+	/*
+	 * All FTDI UART chips are limited to CS7/8. We won't pretend to
+	 * support CS5/6 and revert the CSIZE setting instead.
+	 */
+	if ((C_CSIZE(tty) != CS8) && (C_CSIZE(tty) != CS7)) {
+		dev_warn(&port->dev, "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 */
+
+	urb_value = 0;
+	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+		      FTDI_SIO_SET_DATA_STOP_BITS_1);
+	if (cflag & PARENB) {
+		if (cflag & CMSPAR)
+			urb_value |= cflag & PARODD ?
+				     FTDI_SIO_SET_DATA_PARITY_MARK :
+				     FTDI_SIO_SET_DATA_PARITY_SPACE;
+		else
+			urb_value |= cflag & PARODD ?
+				     FTDI_SIO_SET_DATA_PARITY_ODD :
+				     FTDI_SIO_SET_DATA_PARITY_EVEN;
+	} else {
+		urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE;
+	}
+	switch (cflag & CSIZE) {
+	case CS7:
+		urb_value |= 7;
+		dev_dbg(&port->dev, "Setting CS7\n");
+		break;
+	default:
+	case CS8:
+		urb_value |= 8;
+		dev_dbg(&port->dev, "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_urb_value = urb_value;
+
+	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			    FTDI_SIO_SET_DATA_REQUEST,
+			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			    urb_value , priv->interface,
+			    NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
+		dev_err(&port->dev, "%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(&port->dev,
+				"%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(&port->dev, "%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);
+	}
+
+	/* Set flow control */
+	/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
+no_c_cflag_changes:
+	if (cflag & CRTSCTS) {
+		dbg("%s Setting to CRTSCTS flow control", __func__);
+		if (usb_control_msg(dev,
+				    usb_sndctrlpipe(dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
+				    NULL, 0, WDR_TIMEOUT) < 0) {
+			dev_err(&port->dev,
+				"urb failed to set to rts/cts flow control\n");
+		}
+
+	} else {
+		/*
+		 * Xon/Xoff code
+		 *
+		 * Check the IXOFF status in the iflag component of the
+		 * termios structure. If IXOFF is not set, the pre-xon/xoff
+		 * code is executed.
+		 */
+		if (iflag & IXOFF) {
+			dbg("%s  request to enable xonxoff iflag=%04x",
+							__func__, iflag);
+			/* Try to enable the XON/XOFF on the ftdi_sio
+			 * Set the vstart and vstop -- could have been done up
+			 * above where a lot of other dereferencing is done but
+			 * that would be very inefficient as vstart and vstop
+			 * are not always needed.
+			 */
+			vstart = termios->c_cc[VSTART];
+			vstop = termios->c_cc[VSTOP];
+			urb_value = (vstop << 8) | (vstart);
+
+			if (usb_control_msg(dev,
+					    usb_sndctrlpipe(dev, 0),
+					    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+					    urb_value , (FTDI_SIO_XON_XOFF_HS
+							 | priv->interface),
+					    NULL, 0, WDR_TIMEOUT) < 0) {
+				dev_err(&port->dev, "urb failed to set to "
+					"xon/xoff flow control\n");
+			}
+		} else {
+			/* else clause to only run if cflag ! CRTSCTS and iflag
+			 * ! XOFF. CHECKME Assuming XON/XOFF handled by tty
+			 * stack - not by device */
+			dbg("%s Turning off hardware flow control", __func__);
+			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(&port->dev,
+					"urb failed to clear flow control\n");
+			}
+		}
+
+	}
+}
+
+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;
+	int len;
+	int ret;
+
+	dbg("%s TIOCMGET", __func__);
+
+	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);
+	if (ret < 0)
+		goto out;
+
+	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;
+out:
+	kfree(buf);
+	return ret;
+}
+
+static int ftdi_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s TIOCMSET", __func__);
+	return update_mctrl(port, set, clear);
+}
+
+static int ftdi_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct async_icount *ic = &priv->icount;
+
+	icount->cts = ic->cts;
+	icount->dsr = ic->dsr;
+	icount->rng = ic->rng;
+	icount->dcd = ic->dcd;
+	icount->tx = ic->tx;
+	icount->rx = ic->rx;
+	icount->frame = ic->frame;
+	icount->parity = ic->parity;
+	icount->overrun = ic->overrun;
+	icount->brk = ic->brk;
+	icount->buf_overrun = ic->buf_overrun;
+	return 0;
+}
+
+static int ftdi_ioctl(struct tty_struct *tty,
+					unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	dbg("%s cmd 0x%04x", __func__, cmd);
+
+	/* Based on code from acm.c and others */
+	switch (cmd) {
+
+	case TIOCGSERIAL: /* gets serial port data */
+		return get_serial_info(port,
+					(struct serial_struct __user *) arg);
+
+	case TIOCSSERIAL: /* sets serial port data */
+		return set_serial_info(tty, port,
+					(struct serial_struct __user *) arg);
+
+	/*
+	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+	 * - mask passed in arg for lines of interest
+	 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+	 * Caller should use TIOCGICOUNT to see which one it was.
+	 *
+	 * This code is borrowed from linux/drivers/char/serial.c
+	 */
+	case TIOCMIWAIT:
+		cprev = priv->icount;
+		for (;;) {
+			interruptible_sleep_on(&port->delta_msr_wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			cnow = priv->icount;
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+	case TIOCSERGETLSR:
+		return get_lsr_info(port, (struct serial_struct __user *)arg);
+		break;
+	default:
+		break;
+	}
+	/* This is not necessarily an error - turns out the higher layers
+	 * will do some ioctls themselves (see comment above)
+	 */
+	dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd);
+	return -ENOIOCTLCMD;
+}
+
+static int __init ftdi_init(void)
+{
+	int retval;
+
+	dbg("%s", __func__);
+	if (vendor > 0 && product > 0) {
+		/* Add user specified VID/PID to reserved element of table. */
+		int i;
+		for (i = 0; id_table_combined[i].idVendor; i++)
+			;
+		id_table_combined[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+		id_table_combined[i].idVendor = vendor;
+		id_table_combined[i].idProduct = product;
+	}
+	retval = usb_serial_register_drivers(&ftdi_driver, serial_drivers);
+	if (retval == 0)
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+			       DRIVER_DESC "\n");
+	return retval;
+}
+
+static void __exit ftdi_exit(void)
+{
+	dbg("%s", __func__);
+
+	usb_serial_deregister_drivers(&ftdi_driver, serial_drivers);
+}
+
+
+module_init(ftdi_init);
+module_exit(ftdi_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "User specified vendor ID (default="
+		__MODULE_STRING(FTDI_VID)")");
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "User specified product ID");
+
+module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.h
new file mode 100644
index 0000000..ed58c6f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio.h
@@ -0,0 +1,564 @@
+/*
+ * 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 (1 | (FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_DTR_LOW  (0 | (FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH (2 | (FTDI_SIO_SET_RTS_MASK << 8))
+#define FTDI_SIO_SET_RTS_LOW (0 | (FTDI_SIO_SET_RTS_MASK << 8))
+
+/*
+ * 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio_ids.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio_ids.h
new file mode 100644
index 0000000..1fee973
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ftdi_sio_ids.h
@@ -0,0 +1,1464 @@
+/*
+ * 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
+
+/*
+ * 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 */
+
+
+/********************************/
+/** third-party VID/PID combos **/
+/********************************/
+
+
+
+/*
+ * Atmel STK541
+ */
+#define ATMEL_VID		0x03eb /* Vendor ID */
+#define STK541_PID		0x2109 /* Zigbee Controller */
+
+/*
+ * 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
+
+/*
+ * 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
+
+/*
+ * 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
+
+/*
+ * 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
+
+/* Olimex */
+#define OLIMEX_VID			0x15BA
+#define OLIMEX_ARM_USB_OCD_PID		0x0003
+#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
+
+/*
+ * RT Systems programming cables for various ham radios
+ */
+#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://zeitconrol.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: 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/funsoft.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/funsoft.c
new file mode 100644
index 0000000..4577b36
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/funsoft.c
@@ -0,0 +1,52 @@
+/*
+ * Funsoft Serial USB driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+static bool debug;
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x1404, 0xcddc) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver funsoft_driver = {
+	.name =		"funsoft",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver funsoft_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"funsoft",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&funsoft_device, NULL
+};
+
+module_usb_serial_driver(funsoft_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/garmin_gps.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/garmin_gps.c
new file mode 100644
index 0000000..ba5b527
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/garmin_gps.c
@@ -0,0 +1,1523 @@
+/*
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.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;
+
+/* debug flag */
+static bool debug;
+
+#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;
+};
+
+
+#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 struct usb_driver garmin_driver = {
+	.name =		"garmin_gps",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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 (0 == 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)))
+		return 1;
+	else
+		return 0;
+}
+
+
+
+static void send_to_tty(struct usb_serial_port *port,
+			char *data, unsigned int actual_length)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->port);
+
+	if (tty && actual_length) {
+
+		usb_serial_debug_data(debug, &port->dev,
+					__func__, actual_length, data);
+
+		tty_insert_flip_string(tty, data, actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+}
+
+
+/******************************************************************************
+ * 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 containg data ... */
+	if (data_length) {
+		pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
+								GFP_ATOMIC);
+		if (pkt == NULL) {
+			dev_err(&garmin_data_p->port->dev, "out of memory\n");
+			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);
+
+		dbg("%s - added: pkt: %d - %d bytes",
+			__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;
+
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%s - pkt-id: 0x%X.", __func__, 0xFF & 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++ = 0xFF & (-cksum);
+	*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)
+{
+	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(debug, &garmin_data_p->port->dev,
+			       __func__, count-GSP_INITIAL_OFFSET, recpkt);
+
+	if (size != (count-GSP_INITIAL_OFFSET-3)) {
+		dbg("%s - invalid size, expected %d bytes, got %d",
+			__func__, size, (count-GSP_INITIAL_OFFSET-3));
+		return -EINVPKT;
+	}
+
+	cksum += *recpkt++;
+	cksum += *recpkt++;
+
+	/* sanity check, remove after test ... */
+	if ((__u8 *)&(usbdata[3]) != recpkt) {
+		dbg("%s - ptr mismatch %p - %p",
+			__func__, &(usbdata[4]), recpkt);
+		return -EINVPKT;
+	}
+
+	while (n < size) {
+		cksum += *recpkt++;
+		n++;
+	}
+
+	if ((0xff & (cksum + *recpkt)) != 0) {
+		dbg("%s - invalid checksum, expected %02x, got %02x",
+			__func__, 0xff & -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)
+{
+	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);
+
+	/* dbg("%s - dle=%d skip=%d size=%d count=%d",
+		__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;
+					dbg("ACK packet complete.");
+				} else if (data == NAK) {
+					ack_or_nak_seen = NAK;
+					dbg("NAK packet complete.");
+				} else {
+					dbg("packet complete - id=0x%X.",
+						0xFF & 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) {
+			dbg("%s - packet too large.", __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)
+{
+	const unsigned char *src;
+	unsigned char *dst;
+	int pktid = 0;
+	int datalen = 0;
+	int cksum = 0;
+	int i = 0;
+	int k;
+
+	dbg("%s - state %d - %d bytes.", __func__,
+					garmin_data_p->state, count);
+
+	k = garmin_data_p->outsize;
+	if ((k+count) > GPS_OUT_BUFSIZ) {
+		dbg("packet too large");
+		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;
+	}
+
+	dbg("%s - %d bytes in buffer, %d bytes in pkt.", __func__, k, i);
+
+	/* garmin_data_p->outbuffer now contains a complete packet */
+
+	usb_serial_debug_data(debug, &garmin_data_p->port->dev,
+				__func__, k, garmin_data_p->outbuffer);
+
+	garmin_data_p->outsize = 0;
+
+	if (GARMIN_LAYERID_APPL != getLayerId(garmin_data_p->outbuffer)) {
+		dbg("not an application packet (%d)",
+				getLayerId(garmin_data_p->outbuffer));
+		return -1;
+	}
+
+	if (pktid > 255) {
+		dbg("packet-id %d too large", pktid);
+		return -2;
+	}
+
+	if (datalen > 255) {
+		dbg("packet-size %d too large", 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 = 0xFF & -cksum;
+	*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) {
+		dbg("%s - next pkt: %d", __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 */
+			dbg("%s - packet size too large: %d", __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);
+	dbg("%s - usb_reset_device", __func__);
+	status = usb_reset_device(port->serial->dev);
+	if (status)
+		dbg("%s - usb_reset_device failed: %d",
+			__func__, status);
+	return status;
+}
+
+
+
+/*
+ * clear all cached data
+ */
+static int garmin_clear(struct garmin_data *garmin_data_p)
+{
+	unsigned long flags;
+	int status = 0;
+
+	/* 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 status;
+}
+
+
+static int garmin_init_session(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	int status = 0;
+	int i = 0;
+
+	if (status == 0) {
+		usb_kill_urb(port->interrupt_in_urb);
+
+		dbg("%s - adding interrupt input", __func__);
+		status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (status)
+			dev_err(&serial->dev->dev,
+			  "%s - failed submitting interrupt urb, error %d\n",
+							__func__, status);
+	}
+
+	/*
+	 * using the initialization method from gpsbabel. See comments in
+	 * gpsbabel/jeeps/gpslibusb.c gusb_reset_toggles()
+	 */
+	if (status == 0) {
+		dbg("%s - starting session ...", __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)
+				break;
+		}
+
+		if (status > 0)
+			status = 0;
+	}
+
+	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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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->write_urb);
+	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 usb_serial *serial = port->serial;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	dbg("%s - port %d - mode=%d state=%d flags=0x%X", __func__,
+		port->number, garmin_data_p->mode,
+		garmin_data_p->state, garmin_data_p->flags);
+
+	if (!serial)
+		return;
+
+	garmin_clear(garmin_data_p);
+
+	/* shutdown our urbs */
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->write_urb);
+
+	/* 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);
+
+		dbg("%s - port %d", __func__, port->number);
+
+		if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer)) {
+
+			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;
+
+	dbg("%s - port %d, state %d", __func__, port->number,
+		garmin_data_p->state);
+
+	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) {
+		dev_err(&port->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(&port->dev, "no more free urbs\n");
+		kfree(buffer);
+		return -ENOMEM;
+	}
+
+	memcpy(buffer, buf, count);
+
+	usb_serial_debug_data(debug, &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 (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
+
+		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 */
+	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;
+	}
+
+	/* 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)
+{
+	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(debug, &port->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)
+		    && GARMIN_LAYERID_PRIVATE ==
+				getLayerId(garmin_data_p->privpkt)) {
+
+			dbg("%s - processing private request %d",
+				__func__, pktid);
+
+			/* drop all unfinished transfers */
+			garmin_clear(garmin_data_p);
+
+			switch (pktid) {
+
+			case PRIV_PKTID_SET_DEBUG:
+				if (pktsiz != 4)
+					return -EINVPKT;
+				debug = __le32_to_cpu(privpkt[3]);
+				dbg("%s - debug level set to 0x%X",
+					__func__, debug);
+				break;
+
+			case PRIV_PKTID_SET_MODE:
+				if (pktsiz != 4)
+					return -EINVPKT;
+				garmin_data_p->mode = __le32_to_cpu(privpkt[3]);
+				dbg("%s - mode set to %d",
+					__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]);
+				dbg("%s - initial_mode set to %d",
+					__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 actice */
+		dbg("%s - pkt dropped", __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 usb_serial *serial =  port->serial;
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+	int retval;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (!serial) {
+		dbg("%s - bad serial pointer, exiting", __func__);
+		return;
+	}
+
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+			__func__, status);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &port->dev,
+				__func__, urb->actual_length, data);
+
+	garmin_read_process(garmin_data_p, data, urb->actual_length, 1);
+
+	if (urb->actual_length == 0 &&
+			0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) {
+		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 (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) {
+			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 {
+		dbg("%s - end of bulk data", __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 */
+		dbg("%s - urb shutting down with status: %d",
+			__func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+			__func__, status);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+				urb->actual_length, urb->transfer_buffer);
+
+	if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) &&
+	    0 == memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY,
+				sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) {
+
+		dbg("%s - bulk data available.", __func__);
+
+		if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+
+			/* 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))
+			 && 0 == memcmp(data, GARMIN_START_SESSION_REPLY,
+					sizeof(GARMIN_START_SESSION_REPLY))) {
+
+		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));
+
+		dbg("%s - start-of-session reply seen - serial %u.",
+			__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);
+
+	dbg("%s - port %d", __func__, port->number);
+	/* 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;
+
+	dbg("%s - port %d", __func__, port->number);
+	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 (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+		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(unsigned long data)
+{
+	struct garmin_data *garmin_data_p = (struct garmin_data *) data;
+
+	/* 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_attach(struct usb_serial *serial)
+{
+	int status = 0;
+	struct usb_serial_port *port = serial->port[0];
+	struct garmin_data *garmin_data_p = NULL;
+
+	dbg("%s", __func__);
+
+	garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+	if (garmin_data_p == NULL) {
+		dev_err(&port->dev, "%s - Out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	init_timer(&garmin_data_p->timer);
+	spin_lock_init(&garmin_data_p->lock);
+	INIT_LIST_HEAD(&garmin_data_p->pktlist);
+	/* garmin_data_p->timer.expires = jiffies + session_timeout; */
+	garmin_data_p->timer.data = (unsigned long)garmin_data_p;
+	garmin_data_p->timer.function = timeout_handler;
+	garmin_data_p->port = port;
+	garmin_data_p->state = 0;
+	garmin_data_p->flags = 0;
+	garmin_data_p->count = 0;
+	usb_set_serial_port_data(port, garmin_data_p);
+
+	status = garmin_init_session(port);
+
+	return status;
+}
+
+
+static void garmin_disconnect(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	dbg("%s", __func__);
+
+	usb_kill_urb(port->interrupt_in_urb);
+	del_timer_sync(&garmin_data_p->timer);
+}
+
+
+static void garmin_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+	dbg("%s", __func__);
+
+	kfree(garmin_data_p);
+}
+
+
+/* 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,
+	.attach              = garmin_attach,
+	.disconnect          = garmin_disconnect,
+	.release             = garmin_release,
+	.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(garmin_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(initial_mode, int, S_IRUGO);
+MODULE_PARM_DESC(initial_mode, "Initial mode");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/generic.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/generic.c
new file mode 100644
index 0000000..546177f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/generic.c
@@ -0,0 +1,573 @@
+/*
+ * USB Serial Converter Generic functions
+ *
+ * Copyright (C) 2010 - 2011 Johan Hovold (jhovold@gmail.com)
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.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>
+
+static int debug;
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+
+static int generic_probe(struct usb_interface *interface,
+			 const struct usb_device_id *id);
+
+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. */
+
+/* we want to look at all devices, as the vendor/product id can change
+ * depending on the command line argument */
+static const struct usb_device_id generic_serial_ids[] = {
+	{.driver_info = 42},
+	{}
+};
+
+static struct usb_driver generic_driver = {
+	.name =		"usbserial_generic",
+	.probe =	generic_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	generic_serial_ids,
+};
+
+/* All of the device info needed for the Generic Serial Converter */
+struct usb_serial_driver usb_serial_generic_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"generic",
+	},
+	.id_table =		generic_device_ids,
+	.num_ports =		1,
+	.disconnect =		usb_serial_generic_disconnect,
+	.release =		usb_serial_generic_release,
+	.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
+};
+
+static int generic_probe(struct usb_interface *interface,
+			       const struct usb_device_id *id)
+{
+	const struct usb_device_id *id_pattern;
+
+	id_pattern = usb_match_id(interface, generic_device_ids);
+	if (id_pattern != NULL)
+		return usb_serial_probe(interface, id);
+	return -ENODEV;
+}
+#endif
+
+int usb_serial_generic_register(int _debug)
+{
+	int retval = 0;
+
+	debug = _debug;
+#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;
+
+	/* register our generic driver with ourselves */
+	retval = usb_serial_register_drivers(&generic_driver, serial_drivers);
+#endif
+	return retval;
+}
+
+void usb_serial_generic_deregister(void)
+{
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	/* remove our generic driver */
+	usb_serial_deregister_drivers(&generic_driver, serial_drivers);
+#endif
+}
+
+int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int result = 0;
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* clear the throttle flags */
+	spin_lock_irqsave(&port->lock, flags);
+	port->throttled = 0;
+	port->throttle_req = 0;
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/* if we have a bulk endpoint, start reading from it */
+	if (port->bulk_in_size)
+		result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
+
+	return result;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_open);
+
+static void generic_cleanup(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+	int i;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (serial->dev) {
+		/* shutdown any bulk transfers that might be going on */
+		if (port->bulk_out_size) {
+			usb_kill_urb(port->write_urb);
+			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]);
+		}
+	}
+}
+
+void usb_serial_generic_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+	generic_cleanup(port);
+}
+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 - kick off an URB write
+ * @port:	Pointer to the &struct usb_serial_port data
+ *
+ * Returns zero on success, or a negative errno value
+ */
+static int usb_serial_generic_write_start(struct usb_serial_port *port)
+{
+	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(debug, &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, GFP_ATOMIC);
+	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 */
+}
+
+/**
+ * usb_serial_generic_write - generic write function for serial USB devices
+ * @tty:	Pointer to &struct tty_struct for the device
+ * @port:	Pointer to the &usb_serial_port structure for the device
+ * @buf:	Pointer to the data to write
+ * @count:	Number of bytes to write
+ *
+ * Returns the number of characters actually written, which may be anything
+ * from zero to @count. If an error occurs, it returns the 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* only do something if we have a bulk out endpoint */
+	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);
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+
+	dbg("%s - returns %d", __func__, chars);
+	return chars;
+}
+
+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;
+
+	dbg("%s - port %d, urb %d\n", __func__, port->number, index);
+
+	res = usb_submit_urb(port->read_urbs[index], mem_flags);
+	if (res) {
+		if (res != -EPERM) {
+			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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+	struct tty_struct *tty;
+	char *ch = (char *)urb->transfer_buffer;
+	int i;
+
+	if (!urb->actual_length)
+		return;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		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(tty, 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(tty, *ch, TTY_NORMAL);
+		}
+	}
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+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 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);
+
+	dbg("%s - port %d, urb %d, len %d\n", __func__, port->number, i,
+							urb->actual_length);
+	if (urb->status) {
+		dbg("%s - non-zero urb status: %d\n", __func__, urb->status);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+						urb->actual_length, data);
+	port->serial->type->process_read_urb(urb);
+
+	/* 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+
+	if (status) {
+		dbg("%s - non-zero urb status: %d", __func__, status);
+
+		spin_lock_irqsave(&port->lock, flags);
+		kfifo_reset_out(&port->write_fifo);
+		spin_unlock_irqrestore(&port->lock, flags);
+	} else {
+		usb_serial_generic_write_start(port);
+	}
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* Set the throttle request flag. It will be picked up
+	 * by usb_serial_generic_read_bulk_callback(). */
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* Clear the throttle flags */
+	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);
+
+#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 structure for the open port
+ *	@tty: tty_struct structure 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;
+
+	dbg("%s - port %d, status %d", __func__, usb_port->number, status);
+
+	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 (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
+			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);
+			if (r < 0)
+				c++;
+		}
+	}
+
+	return c ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
+
+void usb_serial_generic_disconnect(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	/* stop reads and writes on all ports */
+	for (i = 0; i < serial->num_ports; ++i)
+		generic_cleanup(serial->port[i]);
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_disconnect);
+
+void usb_serial_generic_release(struct usb_serial *serial)
+{
+	dbg("%s", __func__);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/hp4x.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/hp4x.c
new file mode 100644
index 0000000..2563e788
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/hp4x.c
@@ -0,0 +1,63 @@
+/*
+ * HP4x Calculators Serial USB driver
+ *
+ * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net)
+ * Copyright (C) 2001-2005 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this
+ * driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.00"
+#define DRIVER_DESC "HP4x (48/49) Generic Serial driver"
+
+#define HP_VENDOR_ID 0x03f0
+#define HP49GP_PRODUCT_ID 0x0121
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(HP_VENDOR_ID, HP49GP_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver hp49gp_driver = {
+	.name =		"hp4X",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver hp49gp_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"hp4X",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&hp49gp_device, NULL
+};
+
+module_usb_serial_driver(hp49gp_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_16654.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_16654.h
new file mode 100644
index 0000000..a53abc9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_16654.h
@@ -0,0 +1,195 @@
+/************************************************************************
+ *
+ *	16654.H		Definitions for 16C654 UART used on EdgePorts
+ *
+ *	Copyright (C) 1998 Inside Out Networks, Inc.
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ ************************************************************************/
+
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.c
new file mode 100644
index 0000000..32099d7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.c
@@ -0,0 +1,3197 @@
+/*
+ * Edgeport USB Serial Converter driver
+ *
+ * Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/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/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 */
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.7"
+#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 CHASE_TIMEOUT		(5*HZ)		/* 5 seconds */
+#define OPEN_TIMEOUT		(5*HZ)		/* 5 seconds */
+#define COMMAND_TIMEOUT		(5*HZ)		/* 5 seconds */
+
+/* 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 async_icount	icount;
+	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},
+};
+
+/* local variables */
+static bool debug;
+
+/* 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_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount);
+static int  edge_startup(struct usb_serial *serial);
+static void edge_disconnect(struct usb_serial *serial);
+static void edge_release(struct usb_serial *serial);
+
+#include "io_tables.h"	/* all of the devices that this driver supports */
+
+/* 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 device *dev, struct tty_struct *tty,
+				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(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)
+{
+	__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) {
+		printk(KERN_ERR "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;
+
+	dbg("Current Boot Image version %d.%d.%d",
+	    edge_serial->boot_descriptor.MajorVersion,
+	    edge_serial->boot_descriptor.MinorVersion,
+	    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
+
+
+	if (BootNewVer > BootCurVer) {
+		dbg("**Update Boot Image from %d.%d.%d to %d.%d.%d",
+		    edge_serial->boot_descriptor.MajorVersion,
+		    edge_serial->boot_descriptor.MinorVersion,
+		    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
+		    BootMajorVersion, BootMinorVersion, BootBuildNumber);
+
+		dbg("Downloading new Boot Image");
+
+		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 {
+		dbg("Boot Image -- already up to date");
+	}
+	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;
+
+	dbg("%s - USB String ID = %d", __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_product_info *product_info)
+{
+	/* Dump Product Info structure */
+	dbg("**Product Information:");
+	dbg("  ProductId             %x", product_info->ProductId);
+	dbg("  NumPorts              %d", product_info->NumPorts);
+	dbg("  ProdInfoVer           %d", product_info->ProdInfoVer);
+	dbg("  IsServer              %d", product_info->IsServer);
+	dbg("  IsRS232               %d", product_info->IsRS232);
+	dbg("  IsRS422               %d", product_info->IsRS422);
+	dbg("  IsRS485               %d", product_info->IsRS485);
+	dbg("  RomSize               %d", product_info->RomSize);
+	dbg("  RamSize               %d", product_info->RamSize);
+	dbg("  CpuRev                %x", product_info->CpuRev);
+	dbg("  BoardRev              %x", product_info->BoardRev);
+	dbg("  BootMajorVersion      %d.%d.%d", product_info->BootMajorVersion,
+	    product_info->BootMinorVersion,
+	    le16_to_cpu(product_info->BootBuildNumber));
+	dbg("  FirmwareMajorVersion  %d.%d.%d",
+			product_info->FirmwareMajorVersion,
+			product_info->FirmwareMinorVersion,
+			le16_to_cpu(product_info->FirmwareBuildNumber));
+	dbg("  ManufactureDescDate   %d/%d/%d",
+			product_info->ManufactureDescDate[0],
+			product_info->ManufactureDescDate[1],
+			product_info->ManufactureDescDate[2]+1900);
+	dbg("  iDownloadFile         0x%x", product_info->iDownloadFile);
+	dbg("  EpicVer               %d", 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(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 = &ep->epic_descriptor;
+	struct edge_compatibility_bits *bits;
+
+	ep->is_epic = 0;
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				 USB_REQUEST_ION_GET_EPIC_DESC,
+				 0xC0, 0x00, 0x00,
+				 &ep->epic_descriptor,
+				 sizeof(struct edge_compatibility_descriptor),
+				 300);
+
+	dbg("%s result = %d", __func__, result);
+
+	if (result > 0) {
+		ep->is_epic = 1;
+		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(product_info);
+
+		bits = &ep->epic_descriptor.Supports;
+		dbg("**EPIC descriptor:");
+		dbg("  VendEnableSuspend: %s", bits->VendEnableSuspend	? "TRUE": "FALSE");
+		dbg("  IOSPOpen         : %s", bits->IOSPOpen		? "TRUE": "FALSE");
+		dbg("  IOSPClose        : %s", bits->IOSPClose		? "TRUE": "FALSE");
+		dbg("  IOSPChase        : %s", bits->IOSPChase		? "TRUE": "FALSE");
+		dbg("  IOSPSetRxFlow    : %s", bits->IOSPSetRxFlow	? "TRUE": "FALSE");
+		dbg("  IOSPSetTxFlow    : %s", bits->IOSPSetTxFlow	? "TRUE": "FALSE");
+		dbg("  IOSPSetXChar     : %s", bits->IOSPSetXChar	? "TRUE": "FALSE");
+		dbg("  IOSPRxCheck      : %s", bits->IOSPRxCheck	? "TRUE": "FALSE");
+		dbg("  IOSPSetClrBreak  : %s", bits->IOSPSetClrBreak	? "TRUE": "FALSE");
+		dbg("  IOSPWriteMCR     : %s", bits->IOSPWriteMCR	? "TRUE": "FALSE");
+		dbg("  IOSPWriteLCR     : %s", bits->IOSPWriteLCR	? "TRUE": "FALSE");
+		dbg("  IOSPSetBaudRate  : %s", bits->IOSPSetBaudRate	? "TRUE": "FALSE");
+		dbg("  TrueEdgeport     : %s", bits->TrueEdgeport	? "TRUE": "FALSE");
+	}
+
+	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 edgeport_port *edge_port;
+	struct usb_serial_port *port;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int length = urb->actual_length;
+	int bytes_avail;
+	int position;
+	int txCredits;
+	int portNumber;
+	int result;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+						__func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __func__, status);
+		goto exit;
+	}
+
+	/* process this interrupt-read even if there are no ports open */
+	if (length) {
+		usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
+						__func__, length, data);
+
+		if (length > 1) {
+			bytes_avail = data[0] | (data[1] << 8);
+			if (bytes_avail) {
+				spin_lock(&edge_serial->es_lock);
+				edge_serial->rxBytesAvail += bytes_avail;
+				dbg("%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d", __func__, bytes_avail, edge_serial->rxBytesAvail, edge_serial->read_in_progress);
+
+				if (edge_serial->rxBytesAvail > 0 &&
+				    !edge_serial->read_in_progress) {
+					dbg("%s - posting a read", __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(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __func__, result);
+						edge_serial->read_in_progress = false;
+					}
+				}
+				spin_unlock(&edge_serial->es_lock);
+			}
+		}
+		/* 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(&edge_port->ep_lock);
+					edge_port->txCredits += txCredits;
+					spin_unlock(&edge_port->ep_lock);
+					dbg("%s - txcredits for port%d = %d",
+							__func__, portNumber,
+							edge_port->txCredits);
+
+					/* tell the tty driver that something
+					   has changed */
+					tty = tty_port_tty_get(
+						&edge_port->port->port);
+					if (tty) {
+						tty_wakeup(tty);
+						tty_kref_put(tty);
+					}
+					/* 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;
+	unsigned char		*data = urb->transfer_buffer;
+	int			retval;
+	__u16			raw_data_length;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __func__, status);
+		edge_serial->read_in_progress = false;
+		return;
+	}
+
+	if (urb->actual_length == 0) {
+		dbg("%s - read bulk callback with no data", __func__);
+		edge_serial->read_in_progress = false;
+		return;
+	}
+
+	raw_data_length = urb->actual_length;
+
+	usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
+					__func__, raw_data_length, data);
+
+	spin_lock(&edge_serial->es_lock);
+
+	/* decrement our rxBytes available by the number that we just got */
+	edge_serial->rxBytesAvail -= raw_data_length;
+
+	dbg("%s - Received = %d, rxBytesAvail %d", __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) {
+		dbg("%s - posting a read", __func__);
+		retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
+		if (retval) {
+			dev_err(&urb->dev->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(&edge_serial->es_lock);
+}
+
+
+/*****************************************************************************
+ * 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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __func__, status);
+	}
+
+	tty = tty_port_tty_get(&edge_port->port->port);
+
+	if (tty && edge_port->open) {
+		/* let the tty driver wakeup if it has a special
+		   write_wakeup function */
+		tty_wakeup(tty);
+	}
+	tty_kref_put(tty);
+
+	/* 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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	atomic_dec(&CmdUrbs);
+	dbg("%s - FREE URB %p (outstanding %d)", __func__,
+					urb, atomic_read(&CmdUrbs));
+
+
+	/* clean up the transfer buffer */
+	kfree(urb->transfer_buffer);
+
+	/* Free the command urb */
+	usb_free_urb(urb);
+
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+							__func__, status);
+		return;
+	}
+
+	/* Get pointer to tty */
+	tty = tty_port_tty_get(&edge_port->port->port);
+
+	/* tell the tty driver that something has changed */
+	if (tty && edge_port->open)
+		tty_wakeup(tty);
+	tty_kref_put(tty);
+
+	/* 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 usb_serial *serial;
+	struct edgeport_serial *edge_serial;
+	int response;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(&port->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 icount structure */
+	memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount));
+
+	/* 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(&port->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 */
+		dbg("%s - open timedout", __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) {
+		dbg("%s - no memory", __func__);
+		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) {
+		dbg("%s - no memory", __func__);
+		edge_close(port);
+		return -ENOMEM;
+	}
+
+	dbg("%s(%d) - Initialize TX fifo to %d bytes",
+			__func__, port->number, edge_port->maxTxCredits);
+
+	dbg("%s exited", __func__);
+
+	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)
+{
+	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) {
+			dbg("%s - Got Chase Response", __func__);
+
+			/* did we get all of our credit back? */
+			if (edge_port->txCredits == edge_port->maxTxCredits) {
+				dbg("%s - Got all credits", __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;
+				dbg("%s - Chase TIMEOUT", __func__);
+				return;
+			}
+		} else {
+			/* Reset timeout value back to 10 seconds */
+			dbg("%s - Last %d, Current %d", __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)
+{
+	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) {
+			dbg("%s - TX Buffer Empty", __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);
+
+		dbg("%s wait", __func__);
+
+		if (lastCount == fifo->count) {
+			/* No activity.. count down. */
+			loop--;
+			if (loop == 0) {
+				dbg("%s - TIMEOUT", __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;
+
+	dbg("%s - port %d", __func__, 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;
+
+	/* block until tx is empty */
+	block_until_tx_empty(edge_port);
+
+	edge_port->closePending = true;
+
+	if ((!edge_serial->is_epic) ||
+	    ((edge_serial->is_epic) &&
+	     (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+		/* flush and chase */
+		edge_port->chaseResponsePending = true;
+
+		dbg("%s - Sending IOSP_CMD_CHASE_PORT", __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->is_epic) &&
+	     (edge_serial->epic_descriptor.Supports.IOSPClose))) {
+	       /* close the port */
+		dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __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;
+
+	dbg("%s exited", __func__);
+}
+
+/*****************************************************************************
+ * 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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));
+
+	dbg("%s(%d) of %d byte(s) Fifo room  %d -- will copy %d bytes",
+			__func__, port->number, 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) {
+		dbg("%s - copySize = Zero", __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);
+	dbg("%s - copy %d bytes of %d into fifo ", __func__,
+					firsthalf, bytesleft);
+
+	/* now copy our data */
+	memcpy(&fifo->fifo[fifo->head], data, firsthalf);
+	usb_serial_debug_data(debug, &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) {
+		dbg("%s - copy rest of data %d", __func__, secondhalf);
+		memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
+		usb_serial_debug_data(debug, &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);
+
+	dbg("%s wrote %d byte(s) TxCredits %d, Fifo %d", __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 urb	*urb;
+	unsigned char	*buffer;
+	int		status;
+	int		count;
+	int		bytesleft;
+	int		firsthalf;
+	int		secondhalf;
+	unsigned long	flags;
+
+	dbg("%s(%d)", __func__, edge_port->port->number);
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+	if (edge_port->write_in_progress ||
+	    !edge_port->open             ||
+	    (fifo->count == 0)) {
+		dbg("%s(%d) EXIT - fifo %d, PendingWrite = %d",
+				__func__, edge_port->port->number,
+				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)) {
+		dbg("%s(%d) Not enough credit - fifo %d TxCredit %d",
+			__func__, edge_port->port->number, 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 == NULL) {
+		dev_err_console(edge_port->port,
+				"%s - no more kernel memory...\n", __func__);
+		edge_port->write_in_progress = false;
+		goto exit_send;
+	}
+	buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->number
+				- edge_port->port->serial->minor, count);
+	buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->number
+				- edge_port->port->serial->minor, 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(debug, &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->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->icount.tx -= count;
+	}
+	dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d",
+			__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;
+
+	dbg("%s", __func__);
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->closePending)
+		return 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (!edge_port->open) {
+		dbg("%s - port not opened", __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);
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s", __func__);
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->closePending)
+		return 0;
+
+	if (!edge_port->open) {
+		dbg("%s - port not opened", __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) {
+		dbg("%s(port %d) - returns %d", __func__,
+						port->number, 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dbg("%s - port not opened", __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 (tty->termios->c_cflag & CRTSCTS) {
+		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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dbg("%s - port not opened", __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 (tty->termios->c_cflag & CRTSCTS) {
+		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);
+	unsigned int cflag;
+
+	cflag = tty->termios->c_cflag;
+	dbg("%s - clfag %08x iflag %08x", __func__,
+	    tty->termios->c_cflag, tty->termios->c_iflag);
+	dbg("%s - old clfag %08x old iflag %08x", __func__,
+	    old_termios->c_cflag, old_termios->c_iflag);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (edge_port == NULL)
+		return;
+
+	if (!edge_port->open) {
+		dbg("%s - port not opened", __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) {
+		dbg("%s -- Empty", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 */
+
+
+	dbg("%s -- %x", __func__, result);
+
+	return result;
+}
+
+static int edge_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct async_icount cnow;
+	cnow = edge_port->icount;
+
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
+			__func__,  port->number, icount->rx, icount->tx);
+	return 0;
+}
+
+static int get_serial_info(struct edgeport_port *edge_port,
+				struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= edge_port->port->serial->minor;
+	tmp.port		= edge_port->port->number;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	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);
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCSERGETLSR:
+		dbg("%s (%d) TIOCSERGETLSR", __func__,  port->number);
+		return get_lsr_info(edge_port, (unsigned int __user *) arg);
+
+	case TIOCGSERIAL:
+		dbg("%s (%d) TIOCGSERIAL", __func__,  port->number);
+		return get_serial_info(edge_port, (struct serial_struct __user *) arg);
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		cprev = edge_port->icount;
+		while (1) {
+			prepare_to_wait(&port->delta_msr_wait,
+						&wait, TASK_INTERRUPTIBLE);
+			schedule();
+			finish_wait(&port->delta_msr_wait, &wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			cnow = edge_port->icount;
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+		/* NOTREACHED */
+		break;
+
+	}
+	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->is_epic) &&
+	     (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+		/* flush and chase */
+		edge_port->chaseResponsePending = true;
+
+		dbg("%s - Sending IOSP_CMD_CHASE_PORT", __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->is_epic) &&
+	     (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) {
+		if (break_state == -1) {
+			dbg("%s - Sending IOSP_CMD_SET_BREAK", __func__);
+			status = send_iosp_ext_cmd(edge_port,
+						IOSP_CMD_SET_BREAK, 0);
+		} else {
+			dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __func__);
+			status = send_iosp_ext_cmd(edge_port,
+						IOSP_CMD_CLEAR_BREAK, 0);
+		}
+		if (status)
+			dbg("%s - error sending break set/clear command.",
+				__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 usb_serial_port *port;
+	struct edgeport_port *edge_port;
+	struct tty_struct *tty;
+	__u16 lastBufferLength;
+	__u16 rxLen;
+
+	dbg("%s", __func__);
+
+	lastBufferLength = bufferLength + 1;
+
+	while (bufferLength > 0) {
+		/* failsafe incase we get a message that we don't understand */
+		if (lastBufferLength == bufferLength) {
+			dbg("%s - stuck in loop, exiting it.", __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;
+
+			dbg("%s - Hdr1=%02X Hdr2=%02X", __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);
+				dbg("%s - Data for Port %u Len %u",
+						__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) {
+					tty = tty_port_tty_get(
+						&edge_port->port->port);
+					if (tty) {
+						dbg("%s - Sending %d bytes to TTY for port %d",
+							__func__, rxLen, edge_serial->rxPort);
+						edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen);
+						tty_kref_put(tty);
+					}
+					edge_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;
+	__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;
+	}
+
+	dbg("%s - port %d", __func__, edge_serial->rxPort);
+
+	if (code == IOSP_EXT_STATUS) {
+		switch (byte2) {
+		case IOSP_EXT_STATUS_CHASE_RSP:
+			/* we want to do EXT status regardless of port
+			 * open/closed */
+			dbg("%s - Port %u EXT CHASE_RSP Data = %02x",
+					__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:
+			dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============", __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;
+		dbg("%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d", __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:
+		dbg("%s - Port %u LSR Status = %02x",
+					__func__, edge_serial->rxPort, byte2);
+		handle_new_lsr(edge_port, false, byte2, 0);
+		break;
+
+	case IOSP_STATUS_LSR_DATA:
+		dbg("%s - Port %u LSR Status = %02x, Data = %02x",
+				__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:
+	 *		dbg("%s - Port %u LSR Status = %02x Data = %02x",
+	 *			__func__, edge_serial->rxPort, byte2, byte3);
+	 *		break;
+	 */
+	case IOSP_STATUS_MSR:
+		dbg("%s - Port %u MSR Status = %02x",
+					__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:
+		dbg("%s - Unrecognized IOSP status code %u", __func__, code);
+		break;
+	}
+}
+
+
+/*****************************************************************************
+ * edge_tty_recv
+ *	this function passes data on to the tty flip buffer
+ *****************************************************************************/
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
+					unsigned char *data, int length)
+{
+	int cnt;
+
+	cnt = tty_insert_flip_string(tty, data, length);
+	if (cnt < length) {
+		dev_err(dev, "%s - dropping data, %d bytes lost\n",
+				__func__, length - cnt);
+	}
+	data += cnt;
+	length -= cnt;
+
+	tty_flip_buffer_push(tty);
+}
+
+
+/*****************************************************************************
+ * 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;
+
+	dbg("%s %02x", __func__, newMsr);
+
+	if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
+			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
+		icount = &edge_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->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;
+
+	dbg("%s - %02x", __func__, newLsr);
+
+	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) {
+		struct tty_struct *tty =
+				tty_port_tty_get(&edge_port->port->port);
+		if (tty) {
+			edge_tty_recv(&edge_port->port->dev, tty, &data, 1);
+			tty_kref_put(tty);
+		}
+	}
+	/* update input line counters */
+	icount = &edge_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;
+
+	dbg("%s - %x, %x, %d", __func__, extAddr, addr, length);
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n",
+							__func__, 64);
+		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;
+
+/*		dbg("%s - writing %x, %x, %d", __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;
+
+/*	dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); */
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n",
+								__func__, 64);
+		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;
+/*		dbg("%s - writing %x, %x, %d", __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_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.
+ *	If successful returns the number of bytes read, otherwise it returns
+ *	a negative error number of the problem.
+ ****************************************************************************/
+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;
+
+	dbg("%s - %x, %x, %d", __func__, extAddr, addr, length);
+
+	transfer_buffer =  kmalloc(64, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&serial->dev->dev,
+			"%s - kmalloc(%d) failed.\n", __func__, 64);
+		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;
+/*		dbg("%s - %x, %x, %d", __func__,
+				extAddr, addr, current_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 < 0)
+			break;
+		memcpy(data, transfer_buffer, current_length);
+		length -= current_length;
+		addr += current_length;
+		data += current_length;
+	}
+
+	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;
+
+	dbg("%s - %d, %d", __func__, command, param);
+
+	buffer = kmalloc(10, GFP_ATOMIC);
+	if (!buffer) {
+		dev_err(&edge_port->port->dev,
+				"%s - kmalloc(%d) failed.\n", __func__, 10);
+		return -ENOMEM;
+	}
+
+	currentCommand = buffer;
+
+	MAKE_CMD_EXT_CMD(&currentCommand, &length,
+		edge_port->port->number - edge_port->port->serial->minor,
+		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);
+	int status = 0;
+	struct urb *urb;
+
+	usb_serial_debug_data(debug, &edge_port->port->dev,
+						__func__, length, buffer);
+
+	/* Allocate our next urb */
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	atomic_inc(&CmdUrbs);
+	dbg("%s - ALLOCATE URB %p (outstanding %d)",
+				__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(&edge_port->port->dev,
+		    "%s - usb_submit_urb(write command) failed, status = %d\n",
+							__func__, status);
+		usb_kill_urb(urb);
+		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 */
+		dbg("%s - command timed out", __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);
+	unsigned char *cmdBuffer;
+	unsigned char *currCmd;
+	int cmdLen = 0;
+	int divisor;
+	int status;
+	unsigned char number =
+		edge_port->port->number - edge_port->port->serial->minor;
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) {
+		dbg("SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d",
+		    edge_port->port->number, baudRate);
+		return 0;
+	}
+
+	dbg("%s - port = %d, baud = %d", __func__,
+					edge_port->port->number, baudRate);
+
+	status = calc_baud_rate_divisor(baudRate, &divisor);
+	if (status) {
+		dev_err(&edge_port->port->dev, "%s - bad baud rate\n",
+								__func__);
+		return status;
+	}
+
+	/* Alloc memory for the string of commands. */
+	cmdBuffer =  kmalloc(0x100, GFP_ATOMIC);
+	if (!cmdBuffer) {
+		dev_err(&edge_port->port->dev,
+			"%s - kmalloc(%d) failed.\n", __func__, 0x100);
+		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(int baudrate, int *divisor)
+{
+	int i;
+	__u16 custom;
+
+
+	dbg("%s - %d", __func__, baudrate);
+
+	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;
+
+		dbg("%s - Baud %d = %d", __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);
+	unsigned char *cmdBuffer;
+	unsigned char *currCmd;
+	unsigned long cmdLen = 0;
+	int status;
+
+	dbg("%s - write to %s register 0x%02x",
+			(regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPWriteMCR &&
+	    regNum == MCR) {
+		dbg("SendCmdWriteUartReg - Not writing to MCR Register");
+		return 0;
+	}
+
+	if (edge_serial->is_epic &&
+	    !edge_serial->epic_descriptor.Supports.IOSPWriteLCR &&
+	    regNum == LCR) {
+		dbg("SendCmdWriteUartReg - Not writing to LCR Register");
+		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->number - edge_port->port->serial->minor,
+		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 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;
+
+	dbg("%s - port %d", __func__, edge_port->port->number);
+
+	if (!edge_port->open &&
+	    !edge_port->openPending) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	cflag = tty->termios->c_cflag;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = LCR_BITS_5; mask = 0x1f;
+		dbg("%s - data bits = 5", __func__);
+		break;
+	case CS6:
+		lData = LCR_BITS_6; mask = 0x3f;
+		dbg("%s - data bits = 6", __func__);
+		break;
+	case CS7:
+		lData = LCR_BITS_7; mask = 0x7f;
+		dbg("%s - data bits = 7", __func__);
+		break;
+	default:
+	case CS8:
+		lData = LCR_BITS_8;
+		dbg("%s - data bits = 8", __func__);
+		break;
+	}
+
+	lParity = LCR_PAR_NONE;
+	if (cflag & PARENB) {
+		if (cflag & CMSPAR) {
+			if (cflag & PARODD) {
+				lParity = LCR_PAR_MARK;
+				dbg("%s - parity = mark", __func__);
+			} else {
+				lParity = LCR_PAR_SPACE;
+				dbg("%s - parity = space", __func__);
+			}
+		} else if (cflag & PARODD) {
+			lParity = LCR_PAR_ODD;
+			dbg("%s - parity = odd", __func__);
+		} else {
+			lParity = LCR_PAR_EVEN;
+			dbg("%s - parity = even", __func__);
+		}
+	} else {
+		dbg("%s - parity = none", __func__);
+	}
+
+	if (cflag & CSTOPB) {
+		lStop = LCR_STOP_2;
+		dbg("%s - stop bits = 2", __func__);
+	} else {
+		lStop = LCR_STOP_1;
+		dbg("%s - stop bits = 1", __func__);
+	}
+
+	/* figure out the flow control settings */
+	rxFlow = txFlow = 0x00;
+	if (cflag & CRTSCTS) {
+		rxFlow |= IOSP_RX_FLOW_RTS;
+		txFlow |= IOSP_TX_FLOW_CTS;
+		dbg("%s - RTS/CTS is enabled", __func__);
+	} else {
+		dbg("%s - RTS/CTS is disabled", __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->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;
+			dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+					__func__, start_char, stop_char);
+		} else {
+			dbg("%s - INBOUND XON/XOFF is disabled", __func__);
+		}
+
+		/* if we are implementing OUTBOUND XON/XOFF */
+		if (I_IXON(tty)) {
+			txFlow |= IOSP_TX_FLOW_XON_XOFF;
+			dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+					__func__, start_char, stop_char);
+		} else {
+			dbg("%s - OUTBOUND XON/XOFF is disabled", __func__);
+		}
+	}
+
+	/* Set flow control to the configured value */
+	if ((!edge_serial->is_epic) ||
+	    ((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->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;
+	}
+
+	dbg("%s - baud rate = %d", __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)
+{
+	int response;
+
+	dbg("getting manufacturer descriptor");
+
+	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 < 1)
+		dev_err(&edge_serial->serial->dev->dev,
+			"error in getting manufacturer descriptor\n");
+	else {
+		char string[30];
+		dbg("**Manufacturer Descriptor");
+		dbg("  RomSize:        %dK",
+			edge_serial->manuf_descriptor.RomSize);
+		dbg("  RamSize:        %dK",
+			edge_serial->manuf_descriptor.RamSize);
+		dbg("  CpuRev:         %d",
+			edge_serial->manuf_descriptor.CpuRev);
+		dbg("  BoardRev:       %d",
+			edge_serial->manuf_descriptor.BoardRev);
+		dbg("  NumPorts:       %d",
+			edge_serial->manuf_descriptor.NumPorts);
+		dbg("  DescDate:       %d/%d/%d",
+			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);
+		dbg("  SerialNumber: %s", string);
+		unicode_to_ascii(string, sizeof(string),
+			edge_serial->manuf_descriptor.AssemblyNumber,
+			edge_serial->manuf_descriptor.AssemblyNumLength/2);
+		dbg("  AssemblyNumber: %s", string);
+		unicode_to_ascii(string, sizeof(string),
+		    edge_serial->manuf_descriptor.OemAssyNumber,
+		    edge_serial->manuf_descriptor.OemAssyNumLength/2);
+		dbg("  OemAssyNumber:  %s", string);
+		dbg("  UartType:       %d",
+			edge_serial->manuf_descriptor.UartType);
+		dbg("  IonPid:         %d",
+			edge_serial->manuf_descriptor.IonPid);
+		dbg("  IonConfig:      %d",
+			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)
+{
+	int response;
+
+	dbg("getting boot descriptor");
+
+	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 < 1)
+		dev_err(&edge_serial->serial->dev->dev,
+				"error in getting boot descriptor\n");
+	else {
+		dbg("**Boot Descriptor:");
+		dbg("  BootCodeLength: %d",
+		    le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
+		dbg("  MajorVersion:   %d",
+			edge_serial->boot_descriptor.MajorVersion);
+		dbg("  MinorVersion:   %d",
+			edge_serial->boot_descriptor.MinorVersion);
+		dbg("  BuildNumber:    %d",
+			le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
+		dbg("  Capabilities:   0x%x",
+		      le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
+		dbg("  UConfig0:       %d",
+			edge_serial->boot_descriptor.UConfig0);
+		dbg("  UConfig1:       %d",
+			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)
+{
+	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:
+			dbg("No download file specified, skipping download");
+			return;
+
+		default:
+			return;
+	}
+
+	response = request_ihex_firmware(&fw, fw_name,
+				    &edge_serial->serial->dev->dev);
+	if (response) {
+		printk(KERN_ERR "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];
+
+	dbg("%s %d.%d.%d", 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;
+		}
+	}
+
+	dbg("sending exec_dl_code");
+	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 edgeport_port *edge_port;
+	struct usb_device *dev;
+	int i, j;
+	int response;
+	bool interrupt_in_found;
+	bool bulk_in_found;
+	bool bulk_out_found;
+	static __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 == NULL) {
+		dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+		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(&serial->dev->dev, "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);
+	}
+
+	dbg("%s - time 1 %ld", __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);
+
+		dbg("%s - time 2 %ld", __func__, jiffies);
+
+		/* Check current Edgeport EEPROM and update if necessary */
+		update_edgeport_E2PROM(edge_serial);
+
+		dbg("%s - time 3 %ld", __func__, jiffies);
+
+		/* set the configuration to use #1 */
+/*		dbg("set_configuration 1"); */
+/*		usb_set_configuration (dev, 1); */
+	}
+	dbg("  FirmwareMajorVersion  %d.%d.%d",
+	    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. */
+
+	/* set up our port private structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL);
+		if (edge_port == NULL) {
+			dev_err(&serial->dev->dev, "%s - Out of memory\n",
+								   __func__);
+			for (j = 0; j < i; ++j) {
+				kfree(usb_get_serial_port_data(serial->port[j]));
+				usb_set_serial_port_data(serial->port[j],
+									NULL);
+			}
+			usb_set_serial_data(serial, NULL);
+			kfree(edge_serial);
+			return -ENOMEM;
+		}
+		spin_lock_init(&edge_port->ep_lock);
+		edge_port->port = serial->port[i];
+		usb_set_serial_port_data(serial->port[i], edge_port);
+	}
+
+	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 */
+				dbg("found interrupt in");
+
+				/* 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) {
+					dev_err(&dev->dev, "out of memory\n");
+					return -ENOMEM;
+				}
+				edge_serial->interrupt_in_buffer =
+					kmalloc(buffer_size, GFP_KERNEL);
+				if (!edge_serial->interrupt_in_buffer) {
+					dev_err(&dev->dev, "out of memory\n");
+					usb_free_urb(edge_serial->interrupt_read_urb);
+					return -ENOMEM;
+				}
+				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 */
+				dbg("found bulk in");
+
+				/* not set up yet, so do it now */
+				edge_serial->read_urb =
+						usb_alloc_urb(0, GFP_KERNEL);
+				if (!edge_serial->read_urb) {
+					dev_err(&dev->dev, "out of memory\n");
+					return -ENOMEM;
+				}
+				edge_serial->bulk_in_buffer =
+					kmalloc(buffer_size, GFP_KERNEL);
+				if (!edge_serial->bulk_in_buffer) {
+					dev_err(&dev->dev, "out of memory\n");
+					usb_free_urb(edge_serial->read_urb);
+					return -ENOMEM;
+				}
+				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 */
+				dbg("found bulk out");
+				edge_serial->bulk_out_endpoint =
+						endpoint->bEndpointAddress;
+				bulk_out_found = true;
+			}
+		}
+
+		if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
+			dev_err(&dev->dev, "Error - the proper endpoints "
+				"were not found!\n");
+			return -ENODEV;
+		}
+
+		/* 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->dev,
+				"%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);
+
+	dbg("%s", __func__);
+
+	/* stop reads and writes on all ports */
+	/* free up our endpoint stuff */
+	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);
+	}
+}
+
+
+/****************************************************************************
+ * 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);
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+
+	kfree(edge_serial);
+}
+
+module_usb_serial_driver(io_driver, serial_drivers);
+
+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");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.h
new file mode 100644
index 0000000..ad9c1d4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_edgeport.h
@@ -0,0 +1,134 @@
+/************************************************************************
+ *
+ *	io_edgeport.h	Edgeport Linux Interface definitions
+ *
+ *	Copyright (C) 2000 Inside Out Networks, Inc.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *
+ ************************************************************************/
+
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ionsp.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ionsp.h
new file mode 100644
index 0000000..5cc591b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ionsp.h
@@ -0,0 +1,455 @@
+/************************************************************************
+ *
+ *	IONSP.H		Definitions for I/O Networks Serial Protocol
+ *
+ *	Copyright (C) 1997-1998 Inside Out Networks, Inc.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	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/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_tables.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_tables.h
new file mode 100644
index 0000000..d0e7c9a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_tables.h
@@ -0,0 +1,227 @@
+/*
+ * IO Edgeport Driver tables
+ *
+ *	Copyright (C) 2001
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ */
+
+#ifndef IO_TABLES_H
+#define IO_TABLES_H
+
+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);
+
+static struct usb_driver io_driver = {
+	.name =		"io_edgeport",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table_combined,
+};
+
+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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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
+};
+
+#endif
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.c
new file mode 100644
index 0000000..5175182
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.c
@@ -0,0 +1,2824 @@
+/*
+ * 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>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/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/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"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.7mode043006"
+#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 */
+
+#define EDGE_OUT_BUF_SIZE	1024
+
+
+/* Product information read from the Edgeport */
+struct product_info {
+	int	TiMode;			/* Current TI Mode  */
+	__u8	hardware_type;		/* Type of hardware */
+} __attribute__((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 async_icount	icount;
+	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 kfifo write_fifo;
+};
+
+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;
+};
+
+
+/* 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 struct usb_driver io_driver = {
+	.name =		"io_ti",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table_combined,
+};
+
+
+static unsigned char OperationalMajorVersion;
+static unsigned char OperationalMinorVersion;
+static unsigned short OperationalBuildNumber;
+
+static bool debug;
+
+static int closing_wait = EDGE_CLOSING_WAIT;
+static bool ignore_cpu_rev;
+static int default_uart_mode;		/* RS232 */
+
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
+			  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 tty_struct *tty);
+
+/* sysfs attributes */
+static int edge_create_sysfs_attrs(struct usb_serial_port *port);
+static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
+
+
+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) {
+		dbg("%s - wanted to write %d, but only wrote %d",
+					     __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 status;
+
+	status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+			(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
+			value, index, data, size, 1000);
+	if (status < 0)
+		return status;
+	if (status != size) {
+		dbg("%s - wanted to write %d, but only wrote %d",
+		     __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);
+}
+
+/* clear tx/rx buffers and fifo in TI UMP */
+static int purge_port(struct usb_serial_port *port, __u16 mask)
+{
+	int port_number = port->number - port->serial->minor;
+
+	dbg("%s - port %d, mask %x", __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;
+
+	dbg("%s - @ %x for %d", __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) {
+			dbg("%s - @ %x for %d", __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) {
+			dbg("%s - ERROR %x", __func__, status);
+			return status;
+		}
+
+		if (read_length > 1)
+			usb_serial_debug_data(debug, &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) {
+			dbg("%s - ERROR %x", __func__, status);
+			return status;
+		}
+	}
+
+	dbg("%s - start_address = %x, length = %d",
+					__func__, start_address, length);
+	usb_serial_debug_data(debug, &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) {
+			dev_err(&serial->serial->dev->dev,
+					"%s - out of memory\n", __func__);
+			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);
+		if (status)
+			return status;
+	}
+
+	dbg("%s - start_sddr = %x, length = %d",
+					__func__, start_address, length);
+	usb_serial_debug_data(debug, &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)
+{
+	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;
+
+	dbg("%s - BytesInFirstPage Addr = %x, length = %d",
+					__func__, start_address, write_length);
+	usb_serial_debug_data(debug, &serial->serial->dev->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);
+	if (status) {
+		dbg("%s - ERROR %d", __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;
+
+		dbg("%s - Page Write Addr = %x, length = %d",
+					__func__, start_address, write_length);
+		usb_serial_debug_data(debug, &serial->serial->dev->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);
+		if (status) {
+			dev_err(&serial->serial->dev->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) {
+		dev_err(&port->port->dev, "%s - out of memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	lsr = kmalloc(1, GFP_KERNEL);	/* Sigh, that's right, just one byte,
+					   as not all platforms can do DMA
+					   from stack */
+	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;
+
+	dbg("%s - XByteCount    0x%X", __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;
+	dbg("%s - LSR = 0x%X", __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:
+	dbg("%s - return %d", __func__, bytes_left);
+
+	kfree(lsr);
+	kfree(oedb);
+	return bytes_left;
+}
+
+static void chase_port(struct edgeport_port *port, unsigned long timeout,
+								int flush)
+{
+	int baud_rate;
+	struct tty_struct *tty = tty_port_tty_get(&port->port->port);
+	wait_queue_t wait;
+	unsigned long flags;
+
+	if (!tty)
+		return;
+
+	if (!timeout)
+		timeout = (HZ * EDGE_CLOSING_WAIT)/100;
+
+	/* wait for data to drain from the buffer */
+	spin_lock_irqsave(&port->ep_lock, flags);
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&tty->write_wait, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kfifo_len(&port->write_fifo) == 0
+		|| timeout == 0 || signal_pending(current)
+		|| !usb_get_intfdata(port->port->serial->interface))
+			/* disconnect */
+			break;
+		spin_unlock_irqrestore(&port->ep_lock, flags);
+		timeout = schedule_timeout(timeout);
+		spin_lock_irqsave(&port->ep_lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&tty->write_wait, &wait);
+	if (flush)
+		kfifo_reset_out(&port->write_fifo);
+	spin_unlock_irqrestore(&port->ep_lock, flags);
+	tty_kref_put(tty);
+
+	/* wait for data to drain from the device */
+	timeout += jiffies;
+	while ((long)(jiffies - timeout) < 0 && !signal_pending(current)
+	&& usb_get_intfdata(port->port->serial->interface)) {
+		/* not disconnected */
+		if (!tx_active(port))
+			break;
+		msleep(10);
+	}
+
+	/* disconnected */
+	if (!usb_get_intfdata(port->port->serial->interface))
+		return;
+
+	/* wait one more character time, based on baud rate */
+	/* (tx_active doesn't seem to wait for the last byte) */
+	baud_rate = port->baud_rate;
+	if (baud_rate == 0)
+		baud_rate = 50;
+	msleep(max(1, DIV_ROUND_UP(10000, baud_rate)));
+}
+
+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.
+	 */
+
+	dbg("%s - Number of Interfaces = %d",
+				__func__, dev->config->desc.bNumInterfaces);
+	dbg("%s - MAX Power            = %d",
+				__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) {
+		dbg("%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) {
+		dev_err(dev, "%s - out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(dev, "%s - out of memory when allocating buffer\n",
+								__func__);
+		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;
+			dbg("%s - structure too big, erroring out.", __func__);
+			break;
+		}
+
+		dbg("%s Type = 0x%x", __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;
+
+	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+	if (!rom_desc) {
+		dev_err(&serial->serial->dev->dev, "%s - out of memory\n",
+								__func__);
+		return -ENOMEM;
+	}
+	start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
+								rom_desc);
+
+	if (!start_address) {
+		dbg("%s - Edge Descriptor not found in I2C", __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;
+	dbg("%s - IonConfig      0x%x", __func__, desc->IonConfig);
+	dbg("%s - Version          %d", __func__, desc->Version);
+	dbg("%s - Cpu/Board      0x%x", __func__, desc->CpuRev_BoardRev);
+	dbg("%s - NumPorts         %d", __func__, desc->NumPorts);
+	dbg("%s - NumVirtualPorts  %d", __func__, desc->NumVirtualPorts);
+	dbg("%s - TotalPorts       %d", __func__, desc->TotalPorts);
+
+exit:
+	kfree(rom_desc);
+	return status;
+}
+
+/* Build firmware header used for firmware update */
+static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
+{
+	__u8 *buffer;
+	int buffer_size;
+	int i;
+	int err;
+	__u8 cs = 0;
+	struct ti_i2c_desc *i2c_header;
+	struct ti_i2c_image_header *img_header;
+	struct ti_i2c_firmware_rec *firmware_rec;
+	const struct firmware *fw;
+	const char *fw_name = "edgeport/down3.bin";
+
+	/* 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) {
+		dev_err(dev, "%s - out of memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	// Set entire image of 0xffs
+	memset(buffer, 0xff, buffer_size);
+
+	err = request_firmware(&fw, fw_name, dev);
+	if (err) {
+		printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
+		       fw_name, err);
+		kfree(buffer);
+		return err;
+	}
+
+	/* Save Download Version Number */
+	OperationalMajorVersion = fw->data[0];
+	OperationalMinorVersion = fw->data[1];
+	OperationalBuildNumber = fw->data[2] | (fw->data[3] << 8);
+
+	/* Copy version number into firmware record */
+	firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
+
+	firmware_rec->Ver_Major	= OperationalMajorVersion;
+	firmware_rec->Ver_Minor	= OperationalMinorVersion;
+
+	/* 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));
+
+	release_firmware(fw);
+
+	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	= OperationalMajorVersion;
+	firmware_rec->Ver_Minor	= OperationalMinorVersion;
+
+	return 0;
+}
+
+/* Try to figure out what type of I2c we have */
+static int i2c_type_bootmode(struct edgeport_serial *serial)
+{
+	int status;
+	u8 *data;
+
+	data = kmalloc(1, GFP_KERNEL);
+	if (!data) {
+		dev_err(&serial->serial->dev->dev,
+				"%s - out of memory\n", __func__);
+		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)
+		dbg("%s - read 2 status error = %d", __func__, status);
+	else
+		dbg("%s - read 2 data = 0x%x", __func__, *data);
+	if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
+		dbg("%s - ROM_TYPE_II", __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)
+		dbg("%s - read 3 status error = %d", __func__, status);
+	else
+		dbg("%s - read 2 data = 0x%x", __func__, *data);
+	if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
+		dbg("%s - ROM_TYPE_III", __func__);
+		serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III;
+		goto out;
+	}
+
+	dbg("%s - Unknown", __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);
+}
+
+/**
+ * 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->dev->dev;
+	int status = 0;
+	int start_address;
+	struct edge_ti_manuf_descriptor *ti_manuf_desc;
+	struct usb_interface_descriptor *interface;
+	int download_cur_ver;
+	int download_new_ver;
+
+	/* 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)
+		return status;
+
+	interface = &serial->serial->interface->cur_altsetting->desc;
+	if (!interface) {
+		dev_err(dev, "%s - no interface set, error!\n", __func__);
+		return -ENODEV;
+	}
+
+	/*
+	 * 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;
+	else
+		/* Otherwise we will remain in configuring mode */
+		serial->product_info.TiMode = TI_MODE_CONFIGURING;
+
+	/********************************************************************/
+	/* Download Mode */
+	/********************************************************************/
+	if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) {
+		struct ti_i2c_desc *rom_desc;
+
+		dbg("%s - RUNNING IN DOWNLOAD MODE", __func__);
+
+		status = check_i2c_image(serial);
+		if (status) {
+			dbg("%s - DOWNLOAD MODE -- BAD I2C", __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) {
+			dev_err(dev, "%s - out of memory.\n", __func__);
+			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) {
+			dbg("%s - Wrong CPU Rev %d (Must be 2)",
+				__func__, ti_cpu_rev(ti_manuf_desc));
+			kfree(ti_manuf_desc);
+			return -EINVAL;
+  		}
+
+		rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+		if (!rom_desc) {
+			dev_err(dev, "%s - out of memory.\n", __func__);
+			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;
+
+			dbg("%s - Found Type FIRMWARE (Type 2) record",
+								__func__);
+
+			firmware_version = kmalloc(sizeof(*firmware_version),
+								GFP_KERNEL);
+			if (!firmware_version) {
+				dev_err(dev, "%s - out of memory.\n", __func__);
+				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 = (OperationalMajorVersion << 8) +
+					   (OperationalMinorVersion);
+
+			dbg("%s - >> FW Versions Device %d.%d  Driver %d.%d",
+			    __func__,
+			    firmware_version->Ver_Major,
+			    firmware_version->Ver_Minor,
+			    OperationalMajorVersion,
+			    OperationalMinorVersion);
+
+			/* Check if we have an old version in the I2C and
+			   update if necessary */
+			if (download_cur_ver < download_new_ver) {
+				dbg("%s - Update I2C dld from %d.%d to %d.%d",
+				    __func__,
+				    firmware_version->Ver_Major,
+				    firmware_version->Ver_Minor,
+				    OperationalMajorVersion,
+				    OperationalMinorVersion);
+
+				record = kmalloc(1, GFP_KERNEL);
+				if (!record) {
+					dev_err(dev, "%s - out of memory.\n",
+							__func__);
+					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;
+				}
+
+				dbg("%s - HARDWARE RESET", __func__);
+
+				/* Reset UMP -- Back to BOOT MODE */
+				status = ti_vsend_sync(serial->serial->dev,
+						UMPC_HARDWARE_RESET,
+						0, 0, NULL, 0);
+
+				dbg("%s - HARDWARE RESET return %d",
+						__func__, status);
+
+				/* return an error on purpose. */
+				kfree(record);
+				kfree(firmware_version);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENODEV;
+			}
+			kfree(firmware_version);
+		}
+		/* Search for type 0xF2 record (firmware blank record) */
+		else if ((start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 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) {
+				dev_err(dev, "%s - out of memory.\n", __func__);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENOMEM;
+			}
+
+			vheader = kmalloc(HEADER_SIZE, GFP_KERNEL);
+			if (!vheader) {
+				dev_err(dev, "%s - out of memory.\n", __func__);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -ENOMEM;
+			}
+
+			dbg("%s - Found Type BLANK FIRMWARE (Type F2) record",
+								__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, dev);
+			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) {
+				dbg("%s - can't read header back", __func__);
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return status;
+			}
+			if (memcmp(vheader, header, HEADER_SIZE)) {
+				dbg("%s - write download record failed",
+					__func__);
+				kfree(vheader);
+				kfree(header);
+				kfree(rom_desc);
+				kfree(ti_manuf_desc);
+				return -EINVAL;
+			}
+
+			kfree(vheader);
+			kfree(header);
+
+			dbg("%s - Start firmware update", __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);
+
+		  	dbg("%s - Update complete 0x%x", __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;
+	}
+
+	/********************************************************************/
+	/* Boot Mode */
+	/********************************************************************/
+	dbg("%s - RUNNING IN BOOT MODE", __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) {
+		dbg("%s - VID = 0x%x", __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;
+		int err;
+		const struct firmware *fw;
+		const char *fw_name = "edgeport/down3.bin";
+
+		/* 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) {
+			dev_err(dev, "%s - out of memory.\n", __func__);
+			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) {
+			dbg("%s - Wrong CPU Rev %d (Must be 2)",
+					__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) {
+			dev_err(dev, "%s - out of memory\n", __func__);
+			return -ENOMEM;
+		}
+
+		/* Initialize the buffer to 0xff (pad the buffer) */
+		memset(buffer, 0xff, buffer_size);
+
+		err = request_firmware(&fw, fw_name, dev);
+		if (err) {
+			printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
+			       fw_name, err);
+			kfree(buffer);
+			return err;
+		}
+		memcpy(buffer, &fw->data[4], fw->size - 4);
+		release_firmware(fw);
+
+		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  */
+		dbg("%s - Downloading operational code image (TI UMP)",
+								__func__);
+		status = download_code(serial, buffer, buffer_size);
+
+		kfree(buffer);
+
+		if (status) {
+			dbg("%s - Error downloading operational code image",
+								__func__);
+			return status;
+		}
+
+		/* Device will reboot */
+		serial->product_info.TiMode = TI_MODE_TRANSITIONING;
+
+		dbg("%s - Download successful -- Device rebooting...",
+								__func__);
+
+		/* return an error on purpose */
+		return -ENODEV;
+	}
+
+stayinbootmode:
+	/* Eprom is invalid or blank stay in boot mode */
+	dbg("%s - STAYING IN BOOT MODE", __func__);
+	serial->product_info.TiMode = TI_MODE_BOOT;
+
+	return 0;
+}
+
+
+static int ti_do_config(struct edgeport_port *port, int feature, int on)
+{
+	int port_number = port->port->number - port->port->serial->minor;
+	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;
+
+	dbg("%s - %x", __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;
+
+	dbg("%s - %02x", __func__, msr);
+
+	if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
+			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
+		icount = &edge_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->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->hw_stopped = 0;
+			tty_wakeup(tty);
+		} else {
+			tty->hw_stopped = 1;
+		}
+	}
+	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));
+	struct tty_struct *tty;
+
+	dbg("%s - %02x", __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) {
+		tty = tty_port_tty_get(&edge_port->port->port);
+		if (tty) {
+			edge_tty_recv(&edge_port->port->dev, tty, &data, 1);
+			tty_kref_put(tty);
+		}
+	}
+
+	/* update input line counters */
+	icount = &edge_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;
+	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;
+
+	dbg("%s", __func__);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dev_err(&urb->dev->dev, "%s - nonzero urb status received: "
+			"%d\n", __func__, status);
+		goto exit;
+	}
+
+	if (!length) {
+		dbg("%s - no data in urb", __func__);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
+						__func__, length, data);
+
+	if (length != 2) {
+		dbg("%s - expecting packet of size 2, got %d",
+							__func__, length);
+		goto exit;
+	}
+
+	port_number = TIUMP_GET_PORT_FROM_CODE(data[0]);
+	function    = TIUMP_GET_FUNC_FROM_CODE(data[0]);
+	dbg("%s - port_number %d, function %d, info 0x%x",
+	     __func__, port_number, function, data[1]);
+	port = edge_serial->serial->port[port_number];
+	edge_port = usb_get_serial_port_data(port);
+	if (!edge_port) {
+		dbg("%s - edge_port not found", __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 */
+			dbg("%s - LSR Event Port %u LSR Status = %02x",
+			     __func__, port_number, lsr);
+			edge_port->lsr_event = 1;
+			edge_port->lsr_mask = lsr;
+		} else {
+			dbg("%s - ===== Port %d LSR Status = %02x ======",
+			     __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];
+		dbg("%s - ===== Port %u MSR Status = %02x ======",
+		     __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;
+	unsigned char *data = urb->transfer_buffer;
+	struct tty_struct *tty;
+	int retval = 0;
+	int port_number;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __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->number - edge_port->port->serial->minor;
+
+	if (edge_port->lsr_event) {
+		edge_port->lsr_event = 0;
+		dbg("%s ===== Port %u LSR Status = %02x, Data = %02x ======",
+		     __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;
+	}
+
+	tty = tty_port_tty_get(&edge_port->port->port);
+	if (tty && urb->actual_length) {
+		usb_serial_debug_data(debug, &edge_port->port->dev,
+					__func__, urb->actual_length, data);
+		if (edge_port->close_pending)
+			dbg("%s - close pending, dropping data on the floor",
+								__func__);
+		else
+			edge_tty_recv(&edge_port->port->dev, tty, data,
+							urb->actual_length);
+		edge_port->icount.rx += urb->actual_length;
+	}
+	tty_kref_put(tty);
+
+exit:
+	/* continue read unless stopped */
+	spin_lock(&edge_port->ep_lock);
+	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(&edge_port->ep_lock);
+	if (retval)
+		dev_err(&urb->dev->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			 __func__, retval);
+}
+
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
+					unsigned char *data, int length)
+{
+	int queued;
+
+	queued = tty_insert_flip_string(tty, data, length);
+	if (queued < length)
+		dev_err(dev, "%s - dropping data, %d bytes lost\n",
+			__func__, length - queued);
+	tty_flip_buffer_push(tty);
+}
+
+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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __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(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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (edge_port == NULL)
+		return -ENODEV;
+
+	port_number = port->number - port->serial->minor;
+	switch (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");
+		return -ENODEV;
+	}
+
+	dbg("%s - port_number = %d, uart_base = %04x, dma_address = %04x",
+				__func__, port_number, edge_port->uart_base,
+				edge_port->dma_address);
+
+	dev = port->serial->dev;
+
+	memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount));
+
+	/* 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));
+
+	dbg("%s - Sending UMPC_OPEN_PORT", __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;
+	}
+
+	dbg("ShadowMSR 0x%X", edge_port->shadow_msr);
+
+	/* Set Initial MCR */
+	edge_port->shadow_mcr = MCR_RTS | MCR_DTR;
+	dbg("ShadowMCR 0x%X", 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;
+		if (!urb) {
+			dev_err(&port->dev,
+				"%s - no interrupt urb present, exiting\n",
+				__func__);
+			status = -EINVAL;
+			goto release_es_lock;
+		}
+		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;
+	if (!urb) {
+		dev_err(&port->dev, "%s - no read urb present, exiting\n",
+								__func__);
+		status = -EINVAL;
+		goto unlink_int_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;
+
+	dbg("%s - exited", __func__);
+
+	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;
+	int port_number;
+	int status;
+
+	dbg("%s - port %d", __func__, 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;
+
+	/* chase the port close and flush */
+	chase_port(edge_port, (HZ * closing_wait) / 100, 1);
+
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->write_urb);
+	edge_port->ep_write_urb_in_use = 0;
+
+	/* assuming we can still talk to the device,
+	 * send a close port command to it */
+	dbg("%s - send umpc_close_port", __func__);
+	port_number = port->number - port->serial->minor;
+	status = send_cmd(port->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;
+
+	dbg("%s - exited", __func__);
+}
+
+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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __func__);
+		return 0;
+	}
+
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->close_pending == 1)
+		return -ENODEV;
+
+	count = kfifo_in_locked(&edge_port->write_fifo, data, count,
+							&edge_port->ep_lock);
+	edge_send(tty);
+
+	return count;
+}
+
+static void edge_send(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	int count, result;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(&edge_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(debug, &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->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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(&edge_port->write_fifo);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (edge_port == NULL)
+		return 0;
+	if (edge_port->close_pending == 1)
+		return 0;
+
+	spin_lock_irqsave(&edge_port->ep_lock, flags);
+	chars = kfifo_len(&edge_port->write_fifo);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	dbg("%s - returns %d", __func__, chars);
+	return chars;
+}
+
+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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 ump_uart_config *config;
+	int baud;
+	unsigned cflag;
+	int status;
+	int port_number = edge_port->port->number -
+					edge_port->port->serial->minor;
+
+	dbg("%s - port %d", __func__, edge_port->port->number);
+
+	config = kmalloc (sizeof (*config), GFP_KERNEL);
+	if (!config) {
+		*tty->termios = *old_termios;
+		dev_err(&edge_port->port->dev, "%s - out of memory\n",
+								__func__);
+		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;
+		    dbg("%s - data bits = 5", __func__);
+		    break;
+	case CS6:
+		    config->bDataBits = UMP_UART_CHAR6BITS;
+		    dbg("%s - data bits = 6", __func__);
+		    break;
+	case CS7:
+		    config->bDataBits = UMP_UART_CHAR7BITS;
+		    dbg("%s - data bits = 7", __func__);
+		    break;
+	default:
+	case CS8:
+		    config->bDataBits = UMP_UART_CHAR8BITS;
+		    dbg("%s - data bits = 8", __func__);
+			    break;
+	}
+
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
+			config->bParity = UMP_UART_ODDPARITY;
+			dbg("%s - parity = odd", __func__);
+		} else {
+			config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
+			config->bParity = UMP_UART_EVENPARITY;
+			dbg("%s - parity = even", __func__);
+		}
+	} else {
+		config->bParity = UMP_UART_NOPARITY;
+		dbg("%s - parity = none", __func__);
+	}
+
+	if (cflag & CSTOPB) {
+		config->bStopBits = UMP_UART_STOPBIT2;
+		dbg("%s - stop bits = 2", __func__);
+	} else {
+		config->bStopBits = UMP_UART_STOPBIT1;
+		dbg("%s - stop bits = 1", __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;
+		dbg("%s - RTS/CTS is enabled", __func__);
+	} else {
+		dbg("%s - RTS/CTS is disabled", __func__);
+		tty->hw_stopped = 0;
+		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;
+		dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+		     __func__, config->cXon, config->cXoff);
+	} else
+		dbg("%s - INBOUND XON/XOFF is disabled", __func__);
+
+	/* if we are implementing OUTBOUND XON/XOFF */
+	if (I_IXON(tty)) {
+		config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X;
+		dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+		     __func__, config->cXon, config->cXoff);
+	} else
+		dbg("%s - OUTBOUND XON/XOFF is disabled", __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
+		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 */
+
+	dbg("%s - baud rate = %d, wBaudRate = %d", __func__, baud,
+							config->wBaudRate);
+
+	dbg("wBaudRate:   %d", (int)(461550L / config->wBaudRate));
+	dbg("wFlags:    0x%x", config->wFlags);
+	dbg("bDataBits:   %d", config->bDataBits);
+	dbg("bParity:     %d", config->bParity);
+	dbg("bStopBits:   %d", config->bStopBits);
+	dbg("cXon:        %d", config->cXon);
+	dbg("cXoff:       %d", config->cXoff);
+	dbg("bUartMode:   %d", 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)
+		dbg("%s - error %d when trying to write config to device",
+		     __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);
+	unsigned int cflag;
+
+	cflag = tty->termios->c_cflag;
+
+	dbg("%s - clfag %08x iflag %08x", __func__,
+	    tty->termios->c_cflag, tty->termios->c_iflag);
+	dbg("%s - old clfag %08x old iflag %08x", __func__,
+	    old_termios->c_cflag, old_termios->c_iflag);
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 */
+
+
+	dbg("%s -- %x", __func__, result);
+	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+	return result;
+}
+
+static int edge_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	struct async_icount *ic = &edge_port->icount;
+
+	icount->cts = ic->cts;
+	icount->dsr = ic->dsr;
+	icount->rng = ic->rng;
+	icount->dcd = ic->dcd;
+	icount->tx = ic->tx;
+        icount->rx = ic->rx;
+        icount->frame = ic->frame;
+        icount->parity = ic->parity;
+        icount->overrun = ic->overrun;
+        icount->brk = ic->brk;
+        icount->buf_overrun = ic->buf_overrun;
+	return 0;
+}
+
+static int get_serial_info(struct edgeport_port *edge_port,
+				struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= edge_port->port->serial->minor;
+	tmp.port		= edge_port->port->number;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	tmp.xmit_fifo_size	= edge_port->port->bulk_out_size;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= closing_wait;
+
+	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);
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
+		return get_serial_info(edge_port,
+				(struct serial_struct __user *) arg);
+	case TIOCMIWAIT:
+		dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
+		cprev = edge_port->icount;
+		while (1) {
+			interruptible_sleep_on(&port->delta_msr_wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			cnow = edge_port->icount;
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+		/* not reached */
+		break;
+	}
+	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 */
+
+	dbg("%s - state = %d", __func__, break_state);
+
+	/* chase the port close */
+	chase_port(edge_port, 0, 0);
+
+	if (break_state == -1)
+		bv = 1;	/* On */
+	status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
+	if (status)
+		dbg("%s - error %d sending break set/clear command.",
+		     __func__, status);
+}
+
+static int edge_startup(struct usb_serial *serial)
+{
+	struct edgeport_serial *edge_serial;
+	struct edgeport_port *edge_port;
+	struct usb_device *dev;
+	int status;
+	int i;
+
+	dev = serial->dev;
+
+	/* create our private serial structure */
+	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
+	if (edge_serial == NULL) {
+		dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	mutex_init(&edge_serial->es_lock);
+	edge_serial->serial = serial;
+	usb_set_serial_data(serial, edge_serial);
+
+	status = download_fw(edge_serial);
+	if (status) {
+		kfree(edge_serial);
+		return status;
+	}
+
+	/* set up our port private structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL);
+		if (edge_port == NULL) {
+			dev_err(&serial->dev->dev, "%s - Out of memory\n",
+								__func__);
+			goto cleanup;
+		}
+		spin_lock_init(&edge_port->ep_lock);
+		if (kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE,
+								GFP_KERNEL)) {
+			dev_err(&serial->dev->dev, "%s - Out of memory\n",
+								__func__);
+			kfree(edge_port);
+			goto cleanup;
+		}
+		edge_port->port = serial->port[i];
+		edge_port->edge_serial = edge_serial;
+		usb_set_serial_port_data(serial->port[i], edge_port);
+		edge_port->bUartMode = default_uart_mode;
+	}
+
+	return 0;
+
+cleanup:
+	for (--i; i >= 0; --i) {
+		edge_port = usb_get_serial_port_data(serial->port[i]);
+		kfifo_free(&edge_port->write_fifo);
+		kfree(edge_port);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	kfree(edge_serial);
+	usb_set_serial_data(serial, NULL);
+	return -ENOMEM;
+}
+
+static void edge_disconnect(struct usb_serial *serial)
+{
+	dbg("%s", __func__);
+}
+
+static void edge_release(struct usb_serial *serial)
+{
+	int i;
+	struct edgeport_port *edge_port;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		edge_port = usb_get_serial_port_data(serial->port[i]);
+		kfifo_free(&edge_port->write_fifo);
+		kfree(edge_port);
+	}
+	kfree(usb_get_serial_data(serial));
+}
+
+
+/* Sysfs Attributes */
+
+static ssize_t show_uart_mode(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 store_uart_mode(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);
+
+	dbg("%s: setting uart_mode = %d", __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(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode,
+							store_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;
+}
+
+
+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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_create_sysfs_attrs,
+	.port_remove		= edge_remove_sysfs_attrs,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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_callback,
+};
+
+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,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.disconnect		= edge_disconnect,
+	.release		= edge_release,
+	.port_probe		= edge_create_sysfs_attrs,
+	.port_remove		= edge_remove_sysfs_attrs,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.get_icount		= edge_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_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&edgeport_1port_device, &edgeport_2port_device, NULL
+};
+
+module_usb_serial_driver(io_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("edgeport/down3.bin");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.h
new file mode 100644
index 0000000..1bd67b2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_ti.h
@@ -0,0 +1,186 @@
+/*****************************************************************************
+ *
+ *	Copyright (C) 1997-2002 Inside Out Networks, Inc.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *
+ *	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) >> 4) - 3)
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_usbvend.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_usbvend.h
new file mode 100644
index 0000000..6f6a856
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/io_usbvend.h
@@ -0,0 +1,683 @@
+/************************************************************************
+ *
+ *	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.
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ ************************************************************************/
+
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipaq.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipaq.c
new file mode 100644
index 0000000..10c02b8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipaq.c
@@ -0,0 +1,670 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001 - 2002
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ */
+
+#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>
+
+#define KP_RETRIES	100
+
+/*
+ * Version Information
+ */
+
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
+#define DRIVER_DESC "USB PocketPC PDA driver"
+
+static __u16 product, vendor;
+static bool debug;
+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);
+static int  ipaq_startup(struct usb_serial *serial);
+
+static struct usb_device_id ipaq_id_table [] = {
+	/* The first entry is a placeholder for the insmod-specified device */
+	{ USB_DEVICE(0x049F, 0x0003) },
+	{ 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);
+
+static struct usb_driver ipaq_driver = {
+	.name =		"ipaq",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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--) {
+		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)
+{
+	/*
+	 * some devices have 3 endpoints, the 3rd of which
+	 * must be ignored as it would make the core
+	 * create a second port which oopses when used
+	 */
+	int ipaq_num_ports = 1;
+
+	dbg("%s - numberofendpoints: %d", __FUNCTION__,
+		(int)serial->interface->cur_altsetting->desc.bNumEndpoints);
+
+	/*
+	 * a few devices have 4 endpoints, seemingly Yakuma devices,
+	 * and we need the second pair, so let them have 2 ports
+	 *
+	 * TODO: can we drop port 1 ?
+	 */
+	if (serial->interface->cur_altsetting->desc.bNumEndpoints > 3) {
+		ipaq_num_ports = 2;
+	}
+
+	return ipaq_num_ports;
+}
+
+
+static int ipaq_startup(struct usb_serial *serial)
+{
+	dbg("%s", __func__);
+
+	/* 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 (serial->num_bulk_in < serial->num_ports ||
+			serial->num_bulk_out < serial->num_ports)
+		return -ENODEV;
+
+	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;
+	}
+
+	dbg("%s - iPAQ module configured for %d ports",
+		__FUNCTION__, serial->num_ports);
+
+	return usb_reset_configuration(serial->dev);
+}
+
+static int __init ipaq_init(void)
+{
+	int retval;
+
+	if (vendor) {
+		ipaq_id_table[0].idVendor = vendor;
+		ipaq_id_table[0].idProduct = product;
+	}
+
+	retval = usb_serial_register_drivers(&ipaq_driver, serial_drivers);
+	if (retval == 0)
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+			       DRIVER_DESC "\n");
+	return retval;
+}
+
+static void __exit ipaq_exit(void)
+{
+	usb_serial_deregister_drivers(&ipaq_driver, serial_drivers);
+}
+
+
+module_init(ipaq_init);
+module_exit(ipaq_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+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");
+
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipw.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipw.c
new file mode 100644
index 0000000..76a0640
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ipw.c
@@ -0,0 +1,344 @@
+/*
+ * 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>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * 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/init.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"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION	"v0.4"
+#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 usb_ipw_ids[] = {
+	{ USB_DEVICE(IPW_VID, IPW_PID) },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(usb, usb_ipw_ids);
+
+static struct usb_driver usb_ipw_driver = {
+	.name =		"ipwtty",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	usb_ipw_ids,
+};
+
+static bool debug;
+
+static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_device *dev = port->serial->dev;
+	u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
+	u8 *buf_flow_init;
+	int result;
+
+	dbg("%s", __func__);
+
+	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 */
+	dbg("%s: Sending SIO_INIT (we guess)", __func__);
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			 IPW_SIO_INIT,
+			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 0,
+			 0, /* index */
+			 NULL,
+			 0,
+			 100000);
+	if (result < 0)
+		dev_err(&port->dev,
+			"Init of modem failed (error = %d)\n", result);
+
+	/* reset the bulk pipes */
+	usb_clear_halt(dev,
+			usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
+	usb_clear_halt(dev,
+			usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
+
+	/*--2: Start reading from the device */
+	dbg("%s: setting up bulk read callback", __func__);
+	usb_wwan_open(tty, port);
+
+	/*--3: Tell the modem to open the floodgates on the rx bulk channel */
+	dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__);
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 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(&port->dev,
+			"Enabling bulk RxRead failed (error = %d)\n", result);
+
+	/*--4: setup the initial flowcontrol */
+	dbg("%s:setting init flowcontrol (%s)", __func__, buf_flow_init);
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 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(&port->dev,
+			"initial flowcontrol failed (error = %d)\n", result);
+
+	kfree(buf_flow_init);
+	return 0;
+}
+
+/* fake probe - only to allocate data structures */
+static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+	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_wwan_release(serial);
+	usb_set_serial_data(serial, NULL);
+	kfree(data);
+}
+
+static void ipw_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_device *dev = port->serial->dev;
+	int result;
+
+	dbg("%s: on = %d", __func__, on);
+
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 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(&port->dev, "setting dtr failed (error = %d)\n",
+								result);
+
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 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(&port->dev, "setting rts failed (error = %d)\n",
+								result);
+}
+
+static void ipw_close(struct usb_serial_port *port)
+{
+	struct usb_device *dev = port->serial->dev;
+	int result;
+
+	/*--3: purge */
+	dbg("%s:sending purge", __func__);
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			 IPW_SIO_PURGE, USB_TYPE_VENDOR |
+			 		USB_RECIP_INTERFACE | USB_DIR_OUT,
+			 0x03,
+			 0,
+			 NULL,
+			 0,
+			 200000);
+	if (result < 0)
+		dev_err(&port->dev, "purge failed (error = %d)\n", result);
+
+
+	/* send RXBULK_off (tell modem to stop transmitting bulk data on
+	   rx chan) */
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 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(&port->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 =		usb_ipw_ids,
+	.num_ports =		1,
+	.disconnect =		usb_wwan_disconnect,
+	.open =			ipw_open,
+	.close =		ipw_close,
+	.probe =		ipw_probe,
+	.attach =		usb_wwan_startup,
+	.release =		ipw_release,
+	.dtr_rts =		ipw_dtr_rts,
+	.write =		usb_wwan_write,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ipw_device, NULL
+};
+
+module_usb_serial_driver(usb_ipw_driver, serial_drivers);
+
+/* Module information */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ir-usb.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ir-usb.c
new file mode 100644
index 0000000..84965cd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ir-usb.c
@@ -0,0 +1,476 @@
+/*
+ * 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 program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.5"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>"
+#define DRIVER_DESC "USB IR Dongle driver"
+
+static bool debug;
+
+/* 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_driver ir_driver = {
+	.name		= "ir-usb",
+	.probe		= usb_serial_probe,
+	.disconnect	= usb_serial_disconnect,
+	.id_table	= 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_irda_cs_descriptor *desc)
+{
+	dbg("bLength=%x", desc->bLength);
+	dbg("bDescriptorType=%x", desc->bDescriptorType);
+	dbg("bcdSpecRevision=%x", __le16_to_cpu(desc->bcdSpecRevision));
+	dbg("bmDataSize=%x", desc->bmDataSize);
+	dbg("bmWindowSize=%x", desc->bmWindowSize);
+	dbg("bmMinTurnaroundTime=%d", desc->bmMinTurnaroundTime);
+	dbg("wBaudRate=%x", __le16_to_cpu(desc->wBaudRate));
+	dbg("bmAdditionalBOFs=%x", desc->bmAdditionalBOFs);
+	dbg("bIrdaRateSniff=%x", desc->bIrdaRateSniff);
+	dbg("bMaxUnicastList=%x", 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_device *dev, unsigned int ifnum)
+{
+	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);
+
+	dbg("%s -  ret=%d", __func__, ret);
+	if (ret < sizeof(*desc)) {
+		dbg("%s - class descriptor read %s (%d)",
+				__func__,
+				(ret < 0) ? "failed" : "too short",
+				ret);
+		goto error;
+	}
+	if (desc->bDescriptorType != USB_DT_CS_IRDA) {
+		dbg("%s - bad class descriptor type", __func__);
+		goto error;
+	}
+
+	irda_usb_dump_class_desc(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;
+
+	irda_desc = irda_usb_find_class_desc(serial->dev, 0);
+	if (!irda_desc) {
+		dev_err(&serial->dev->dev,
+			"IRDA class descriptor not found, device not bound\n");
+		return -ENODEV;
+	}
+
+	dbg("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s",
+		__func__,
+		(irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "",
+		(irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "",
+		(irda_desc->wBaudRate & 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+	struct tty_struct *tty;
+
+	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 = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+	tty_insert_flip_string(tty, data + 1, urb->actual_length - 1);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void ir_set_termios_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	kfree(urb->transfer_buffer);
+
+	if (status)
+		dbg("%s - non-zero urb status: %d", __func__, 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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) {
+		dev_err(&port->dev, "%s - no more urbs\n", __func__);
+		return;
+	}
+	transfer_buffer = kmalloc(1, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&port->dev, "%s - out of memory\n", __func__);
+		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)
+{
+	int retval;
+
+	if (buffer_size) {
+		ir_device.bulk_in_size = buffer_size;
+		ir_device.bulk_out_size = buffer_size;
+	}
+
+	retval = usb_serial_register_drivers(&ir_driver, serial_drivers);
+	if (retval == 0)
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+			       DRIVER_DESC "\n");
+	return retval;
+}
+
+static void __exit ir_exit(void)
+{
+	usb_serial_deregister_drivers(&ir_driver, serial_drivers);
+}
+
+
+module_init(ir_init);
+module_exit(ir_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.c
new file mode 100644
index 0000000..53c639c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.c
@@ -0,0 +1,1324 @@
+/*
+ * 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)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *  And tested with help of WB Electronics
+ *
+ */
+#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/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>
+
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+static bool debug = 1;
+#else
+static bool debug;
+#endif
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.12"
+#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);
+
+static struct usb_driver iuu_driver = {
+	.name = "iuu_phoenix",
+	.probe = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+	.id_table = 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 void read_rxcmd_callback(struct urb *urb);
+
+struct iuu_private {
+	spinlock_t lock;	/* store irq state */
+	wait_queue_head_t delta_msr_wait;
+	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 *dbgbuf;		/* debug buffer */
+	u8 len;
+	int vcc;		/* vcc (either 3 or 5 V) */
+	u32 baud;
+	u32 boost;
+	u32 clk;
+};
+
+
+static void iuu_free_buf(struct iuu_private *priv)
+{
+	kfree(priv->buf);
+	kfree(priv->dbgbuf);
+	kfree(priv->writebuf);
+}
+
+static int iuu_alloc_buf(struct iuu_private *priv)
+{
+	priv->buf = kzalloc(256, GFP_KERNEL);
+	priv->dbgbuf = kzalloc(256, GFP_KERNEL);
+	priv->writebuf = kzalloc(256, GFP_KERNEL);
+	if (!priv->buf || !priv->dbgbuf || !priv->writebuf) {
+		iuu_free_buf(priv);
+		dbg("%s problem allocation buffer", __func__);
+		return -ENOMEM;
+	}
+	dbg("%s - Privates buffers allocation success", __func__);
+	return 0;
+}
+
+static int iuu_startup(struct usb_serial *serial)
+{
+	struct iuu_private *priv;
+	priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL);
+	dbg("%s- priv allocation success", __func__);
+	if (!priv)
+		return -ENOMEM;
+	if (iuu_alloc_buf(priv)) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+	priv->vcc = vcc_default;
+	spin_lock_init(&priv->lock);
+	init_waitqueue_head(&priv->delta_msr_wait);
+	usb_set_serial_port_data(serial->port[0], priv);
+	return 0;
+}
+
+/* Release function */
+static void iuu_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	if (!port)
+		return;
+
+	dbg("%s", __func__);
+
+	if (priv) {
+		iuu_free_buf(priv);
+		dbg("%s - I will free all", __func__);
+		usb_set_serial_port_data(port, NULL);
+
+		dbg("%s - priv is not anymore in port structure", __func__);
+		kfree(priv);
+
+		dbg("%s priv is now kfree", __func__);
+	}
+}
+
+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 */
+	dbg("%s (%d) msg : SET = 0x%04x, CLEAR = 0x%04x ", __func__,
+	    port->number, set, clear);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) {
+		dbg("%s TIOCMSET RESET called !!!", __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;
+
+	dbg("%s - enter", __func__);
+
+	if (status) {
+		dbg("%s - status = %d", __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;
+	dbg("%s - enter", __func__);
+
+	/* 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;
+
+	dbg("%s - enter", __func__);
+
+	if (status) {
+		dbg("%s - status = %d", __func__, status);
+		/* error stop all */
+		return;
+	}
+
+	st = urb->transfer_buffer;
+	dbg("%s - enter", __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;
+
+	dbg("%s - status = %d", __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;
+
+	dbg("%s - enter", __func__);
+
+	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;
+
+	dbg("%s - enter", __func__);
+
+	/* 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)
+		dbg("%s - error = %2x", __func__, status);
+	else
+		dbg("%s - write OK !", __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;
+
+	dbg("%s - enter", __func__);
+
+	/* 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)
+		dbg("%s - error = %2x", __func__, status);
+	else
+		dbg("%s - read OK !", __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;
+
+	dbg("%s - enter", __func__);
+
+	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)
+		dbg("%s - led error status = %2x", __func__, status);
+	else
+		dbg("%s - led OK !", __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 == 1) {
+		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 == 1) {
+		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;
+
+	dbg("%s - enter", __func__);
+
+	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) {
+			dbg("%s - write error ", __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;
+	DIV = DIV;
+	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)
+		dbg("%s - write error ", __func__);
+	return status;
+}
+
+static int iuu_uart_flush(struct usb_serial_port *port)
+{
+	int i;
+	int status;
+	u8 rxcmd = IUU_UART_RX;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	dbg("%s - enter", __func__);
+
+	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) {
+			dbg("%s - uart_flush_write error", __func__);
+			return status;
+		}
+
+		status = read_immediate(port, &priv->len, 1);
+		if (status != IUU_OPERATION_OK) {
+			dbg("%s - uart_flush_read error", __func__);
+			return status;
+		}
+
+		if (priv->len > 0) {
+			dbg("%s - uart_flush datalen is : %i ", __func__,
+			    priv->len);
+			status = read_immediate(port, priv->buf, priv->len);
+			if (status != IUU_OPERATION_OK) {
+				dbg("%s - uart_flush_read error", __func__);
+				return status;
+			}
+		}
+	}
+	dbg("%s - uart_flush_read OK!", __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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	dbg("%s - status = %d", __func__, status);
+
+	if (status) {
+		if (status == -EPROTO) {
+			/* reschedule needed */
+		}
+		return;
+	}
+
+	dbg("%s - %i chars to write", __func__, urb->actual_length);
+	tty = tty_port_tty_get(&port->port);
+	if (data == NULL)
+		dbg("%s - data is NULL !!!", __func__);
+	if (tty && urb->actual_length && data) {
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+	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 i;
+	int buf_len;
+	char *buf_ptr = port->write_urb->transfer_buffer;
+	dbg("%s - enter", __func__);
+
+	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);
+	if (debug == 1) {
+		for (i = 0; i < buf_len; i++)
+			sprintf(priv->dbgbuf + i*2 ,
+				"%02X", priv->writebuf[i]);
+		priv->dbgbuf[buf_len+i*2] = 0;
+		dbg("%s - writing %i chars : %s", __func__,
+		    buf_len, priv->dbgbuf);
+	}
+	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;
+	dbg("%s - enter", __func__);
+
+	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++;
+
+	dbg("%s - enter", __func__);
+
+	if (status) {
+		dbg("%s - status = %d", __func__, status);
+		/* error stop all */
+		return;
+	}
+	if (data == NULL)
+		dbg("%s - data is NULL !!!", __func__);
+
+	if (urb->actual_length == 1  && data != NULL)
+		len = (int) data[0];
+
+	if (urb->actual_length > 1) {
+		dbg("%s - urb->actual_length = %i", __func__,
+		    urb->actual_length);
+		error = 1;
+		return;
+	}
+	/* if len > 0 call readbuf */
+
+	if (len > 0 && error == 0) {
+		dbg("%s - call read buf - len to read is %i ",
+			__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 */
+	dbg("%s - rxcmd recall", __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;
+	dbg("%s - enter", __func__);
+
+	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;
+
+	dbg("%s - status = %d", __func__, 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);
+	dbg("%s - submit result = %d", __func__, result);
+}
+
+static int iuu_uart_on(struct usb_serial_port *port)
+{
+	int status;
+	u8 *buf;
+
+	buf = kmalloc(sizeof(u8) * 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) {
+		dbg("%s - uart_on error", __func__);
+		goto uart_enable_failed;
+	}
+	/*  iuu_reset() the card after iuu_uart_on() */
+	status = iuu_uart_flush(port);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - uart_flush error", __func__);
+uart_enable_failed:
+	kfree(buf);
+	return status;
+}
+
+/*  Diables 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)
+		dbg("%s - uart_off error", __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;
+
+	dbg("%s - enter baud_base=%d", __func__, baud_base);
+	dataout = kmalloc(sizeof(u8) * 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)
+		dbg("%s - uart_off error", __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;
+
+	dbg("%s - enter c_ospeed or baud=%d", __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); */
+	struct usb_serial *serial;
+
+	serial = port->serial;
+	if (!serial)
+		return;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	iuu_uart_off(port);
+	if (serial->dev) {
+		/* free writebuf */
+		/* shutdown our urbs */
+		dbg("%s - shutting down urbs", __func__);
+		usb_kill_urb(port->write_urb);
+		usb_kill_urb(port->read_urb);
+		usb_kill_urb(port->interrupt_in_urb);
+		iuu_led(port, 0, 0, 0xF000, 0xFF);
+	}
+}
+
+static void iuu_init_termios(struct tty_struct *tty)
+{
+	dbg("%s - enter", __func__);
+	*(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;
+	u8 *buf;
+	int result;
+	int baud;
+	u32 actual;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	baud = tty->termios->c_ospeed;
+	tty->termios->c_ispeed = baud;
+	/* Re-encode speed */
+	tty_encode_baud_rate(tty, baud, baud);
+
+	dbg("%s -  port %d, baud %d", __func__, port->number, baud);
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	buf = kmalloc(10, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	priv->poll = 0;
+
+	/* initialize writebuf */
+#define FISH(a, b, c, d) do { \
+	result = usb_control_msg(port->serial->dev,	\
+				usb_rcvctrlpipe(port->serial->dev, 0),	\
+				b, a, c, d, buf, 1, 1000); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d - %x", a, b, c, d, result, \
+				buf[0]); } while (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); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d", 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);
+	kfree(buf);
+	iuu_led(port, 0xF000, 0xF000, 0, 0xFF);
+	iuu_uart_on(port);
+	if (boost < 100)
+		boost = 100;
+	priv->boost = boost;
+	priv->baud = baud;
+	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);
+
+	dbg("%s - initialization done", __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(&port->dev, "%s - failed submitting read urb,"
+			" error %d\n", __func__, result);
+		iuu_close(port);
+	} else {
+		dbg("%s - rxcmd OK", __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;
+
+	dbg("%s - enter", __func__);
+
+	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)
+		dbg("%s - vcc error status = %2x", __func__, status);
+	else
+		dbg("%s - vcc OK !", __func__);
+
+	return status;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t show_vcc_mode(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 store_vcc_mode(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 (strict_strtoul(buf, 10, &v)) {
+		dev_err(dev, "%s - vcc_mode: %s is not a unsigned long\n",
+				__func__, buf);
+		goto fail_store_vcc_mode;
+	}
+
+	dbg("%s: setting vcc_mode = %ld", __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(vcc_mode, S_IRUSR | S_IWUSR, show_vcc_mode,
+	store_vcc_mode);
+
+static int iuu_create_sysfs_attrs(struct usb_serial_port *port)
+{
+	dbg("%s", __func__);
+
+	return device_create_file(&port->dev, &dev_attr_vcc_mode);
+}
+
+static int iuu_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+	dbg("%s", __func__);
+
+	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,
+	.bulk_in_size = 512,
+	.bulk_out_size = 512,
+	.port_probe = iuu_create_sysfs_attrs,
+	.port_remove = iuu_remove_sysfs_attrs,
+	.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,
+	.attach = iuu_startup,
+	.release = iuu_release,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&iuu_device, NULL
+};
+
+module_usb_serial_driver(iuu_driver, serial_drivers);
+
+MODULE_AUTHOR("Alain Degreffe eczema@ecze.com");
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(DRIVER_VERSION);
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+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/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.h
new file mode 100644
index 0000000..b82630a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/iuu_phoenix.h
@@ -0,0 +1,122 @@
+/*
+ * 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 )
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *  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/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.c
new file mode 100644
index 0000000..0879ac7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.c
@@ -0,0 +1,2637 @@
+/*
+  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>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  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/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/firmware.h>
+#include <linux/ihex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "keyspan.h"
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1.5"
+#define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
+#define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
+
+#define INSTAT_BUFLEN	32
+#define GLOCONT_BUFLEN	64
+#define INDAT49W_BUFLEN	512
+
+	/* 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[INSTAT_BUFLEN];
+
+	/* 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[INDAT49W_BUFLEN];
+
+	/* XXX this one probably will need a lock */
+	struct urb	*glocont_urb;
+	char		glocont_buf[GLOCONT_BUFLEN];
+	char		ctrl_buf[8];	/* 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][64];
+	/* Output endpoints and buffer for this port */
+	struct urb	*out_urbs[2];
+	char		out_buffer[2][64];
+
+	/* Input ack endpoint */
+	struct urb	*inack_urb;
+	char		inack_buffer[1];
+
+	/* Output control endpoint */
+	struct urb	*outcont_urb;
+	char		outcont_buffer[64];
+
+	/* 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"
+
+
+module_usb_serial_driver(keyspan_driver, serial_drivers);
+
+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;
+
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%s", __func__);
+
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+	cflag = tty->termios->c_cflag;
+	device_port = port->number - port->serial->minor;
+
+	/* 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(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 - calcuate 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;
+	}
+
+	dbg("%s - for port %d (%d chars), flip=%d",
+	    __func__, port->number, 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 */
+			dbg("%s - no output urb :(", __func__);
+			return count;
+		}
+
+		dbg("%s - endpoint %d flip %d",
+			__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)
+			dbg("usb_submit_urb(write bulk) failed (%d)", 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;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dbg("%s - nonzero status: %x on endpoint %d.",
+		    __func__, status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	tty = tty_port_tty_get(&port->port);
+	if (tty && 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(tty, 0, TTY_OVERRUN);
+			}
+			for (i = 1; i < urb->actual_length ; ++i)
+				tty_insert_flip_char(tty, data[i], TTY_NORMAL);
+		} else {
+			/* some bytes had errors, every byte has status */
+			dbg("%s - RX error!!!!", __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(tty, 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(tty, data[i+1], flag);
+			}
+		}
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __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);
+	dbg("%s - urb %d", __func__, urb == p_priv->out_urbs[1]);
+
+	usb_serial_port_softint(port);
+}
+
+static void	usa26_inack_callback(struct urb *urb)
+{
+	dbg("%s", __func__);
+
+}
+
+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) {
+		dbg("%s - sending setup", __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;
+	struct tty_struct			*tty;
+	int old_dcd_state, err;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+	if (urb->actual_length != 9) {
+		dbg("%s - %d byte report??", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa26_portStatusMessage *)data;
+
+#if 0
+	dbg("%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d",
+	    __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, msg->ri, msg->_txOff,
+	    msg->_txXoff, msg->rxEnabled, msg->controlResponse);
+#endif
+
+	/* Now do something useful with the data */
+
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dbg("%s - Unexpected port number %d", __func__, msg->port);
+		goto exit;
+	}
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+
+	/* 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 = tty_port_tty_get(&port->port);
+		if (tty && !C_CLOCAL(tty))
+			tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+exit: ;
+}
+
+static void	usa26_glocont_callback(struct urb *urb)
+{
+	dbg("%s", __func__);
+}
+
+
+static void usa28_indat_callback(struct urb *urb)
+{
+	int                     err;
+	struct usb_serial_port  *port;
+	struct tty_struct       *tty;
+	unsigned char           *data;
+	struct keyspan_port_private             *p_priv;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	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) {
+			dbg("%s - nonzero status: %x on endpoint %d.",
+			    __func__, status, usb_pipeendpoint(urb->pipe));
+			return;
+		}
+
+		port =  urb->context;
+		p_priv = usb_get_serial_port_data(port);
+		data = urb->transfer_buffer;
+
+		tty =tty_port_tty_get(&port->port);
+		if (tty && urb->actual_length) {
+			tty_insert_flip_string(tty, data, urb->actual_length);
+			tty_flip_buffer_push(tty);
+		}
+		tty_kref_put(tty);
+
+		/* Resubmit urb so we continue receiving */
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err != 0)
+			dbg("%s - resubmit read urb failed. (%d)",
+							__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)
+{
+	dbg("%s", __func__);
+}
+
+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) {
+		dbg("%s - sending setup", __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;
+	struct tty_struct			*tty;
+	int old_dcd_state;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+
+	if (urb->actual_length != sizeof(struct keyspan_usa28_portStatusMessage)) {
+		dbg("%s - bad length %d", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	/*dbg("%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__
+	    data[0], data[1], data[2], data[3], data[4], data[5],
+	    data[6], data[7], data[8], data[9], data[10], data[11]);*/
+
+	/* Now do something useful with the data */
+	msg = (struct keyspan_usa28_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dbg("%s - Unexpected port number %d", __func__, msg->port);
+		goto exit;
+	}
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+
+	/* 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 = tty_port_tty_get(&port->port);
+		if (tty && !C_CLOCAL(tty)) 
+			tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+
+		/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+exit: ;
+}
+
+static void	usa28_glocont_callback(struct urb *urb)
+{
+	dbg("%s", __func__);
+}
+
+
+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;
+
+	dbg("%s", __func__);
+
+	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) {
+			dbg("%s - sending setup", __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;
+
+	dbg("%s", __func__);
+
+	serial =  urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+
+	if (urb->actual_length !=
+			sizeof(struct keyspan_usa49_portStatusMessage)) {
+		dbg("%s - bad length %d", __func__, urb->actual_length);
+		goto exit;
+	}
+
+	/*dbg(" %x %x %x %x %x %x %x %x %x %x %x", __func__,
+	    data[0], data[1], data[2], data[3], data[4], data[5],
+	    data[6], data[7], data[8], data[9], data[10]);*/
+
+	/* Now do something useful with the data */
+	msg = (struct keyspan_usa49_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->portNumber >= serial->num_ports) {
+		dbg("%s - Unexpected port number %d",
+					__func__, msg->portNumber);
+		goto exit;
+	}
+	port = serial->port[msg->portNumber];
+	p_priv = usb_get_serial_port_data(port);
+
+	/* 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) {
+		struct tty_struct *tty = tty_port_tty_get(&port->port);
+		if (tty && !C_CLOCAL(tty))
+			tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+exit:	;
+}
+
+static void	usa49_inack_callback(struct urb *urb)
+{
+	dbg("%s", __func__);
+}
+
+static void	usa49_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dbg("%s - nonzero status: %x on endpoint %d.", __func__,
+		    status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	tty = tty_port_tty_get(&port->port);
+	if (tty && urb->actual_length) {
+		/* 0x80 bit is error flag */
+		if ((data[0] & 0x80) == 0) {
+			/* no error on any byte */
+			tty_insert_flip_string(tty, 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(tty, 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(tty, data[i+1], flag);
+			}
+		}
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+}
+
+static void usa49wg_indat_callback(struct urb *urb)
+{
+	int			i, len, x, err;
+	struct usb_serial	*serial;
+	struct usb_serial_port	*port;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	serial = urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+
+	/* inbound data is in the form P#, len, status, data */
+	i = 0;
+	len = 0;
+
+	if (urb->actual_length) {
+		while (i < urb->actual_length) {
+
+			/* Check port number from message*/
+			if (data[i] >= serial->num_ports) {
+				dbg("%s - Unexpected port number %d",
+					__func__, data[i]);
+				return;
+			}
+			port = serial->port[data[i++]];
+			tty = tty_port_tty_get(&port->port);
+			len = data[i++];
+
+			/* 0x80 bit is error flag */
+			if ((data[i] & 0x80) == 0) {
+				/* no error on any byte */
+				i++;
+				for (x = 1; x < len ; ++x)
+					tty_insert_flip_char(tty, data[i++], 0);
+			} else {
+				/*
+				 * some bytes had errors, every byte has status
+				 */
+				for (x = 0; x + 1 < len; x += 2) {
+					int stat = data[i];
+					int flag = TTY_NORMAL;
+
+					if (stat & RXERROR_OVERRUN) {
+						tty_insert_flip_char(tty, 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(tty,
+							data[i+1], flag);
+					i += 2;
+				}
+			}
+			tty_flip_buffer_push(tty);
+			tty_kref_put(tty);
+		}
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+}
+
+/* not used, usa-49 doesn't have per-port control endpoints */
+static void usa49_outcont_callback(struct urb *urb)
+{
+	dbg("%s", __func__);
+}
+
+static void usa90_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	struct keyspan_port_private	 	*p_priv;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	if (status) {
+		dbg("%s - nonzero status: %x on endpoint %d.",
+		    __func__, status, endpoint);
+		return;
+	}
+
+	port =  urb->context;
+	p_priv = usb_get_serial_port_data(port);
+
+	if (urb->actual_length) {
+		tty = tty_port_tty_get(&port->port);
+		/* if current mode is DMA, looks like usa28 format
+		   otherwise looks like usa26 data format */
+
+		if (p_priv->baud > 57600)
+			tty_insert_flip_string(tty, 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(tty, 0,
+								TTY_OVERRUN);
+				}
+				for (i = 1; i < urb->actual_length ; ++i)
+					tty_insert_flip_char(tty, data[i],
+								TTY_NORMAL);
+			}  else {
+			/* some bytes had errors, every byte has status */
+				dbg("%s - RX error!!!!", __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(
+								tty, 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(tty, data[i+1],
+									flag);
+				}
+			}
+		}
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __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;
+	struct tty_struct			*tty;
+	int old_dcd_state, err;
+	int status = urb->status;
+
+	serial =  urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+	if (urb->actual_length < 14) {
+		dbg("%s - %d byte report??", __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);
+
+	/* 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 = tty_port_tty_get(&port->port);
+		if (tty && !C_CLOCAL(tty))
+			tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __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) {
+		dbg("%s - sending setup", __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;
+
+	dbg("%s", __func__);
+
+	serial = urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __func__, status);
+		return;
+	}
+
+	if (urb->actual_length !=
+			sizeof(struct keyspan_usa67_portStatusMessage)) {
+		dbg("%s - bad length %d", __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) {
+		dbg("%s - Unexpected port number %d", __func__, msg->port);
+		return;
+	}
+
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+
+	/* 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) {
+		struct tty_struct *tty = tty_port_tty_get(&port->port);
+		if (tty && !C_CLOCAL(tty))
+			tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Resubmit urb so we continue receiving */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __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;
+
+	dbg("%s", __func__);
+
+	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) {
+			dbg("%s - sending setup", __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;
+
+	dbg("%s", __func__);
+	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;
+	struct keyspan_serial_private 	*s_priv;
+	struct usb_serial 		*serial = port->serial;
+	const struct keyspan_device_details	*d_details;
+	int				i, err;
+	int				baud_rate, device_port;
+	struct urb			*urb;
+	unsigned int			cflag = 0;
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = p_priv->device_details;
+
+	dbg("%s - port%d.", __func__, port->number);
+
+	/* 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)
+			dbg("%s - submit urb %d failed (%d)",
+							__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->number - port->serial->minor;
+	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(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 inline void stop_urb(struct urb *urb)
+{
+	if (urb && urb->status == -EINPROGRESS)
+		usb_kill_urb(urb);
+}
+
+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 usb_serial	*serial = port->serial;
+	struct keyspan_serial_private 	*s_priv;
+	struct keyspan_port_private 	*p_priv;
+
+	dbg("%s", __func__);
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+
+	p_priv->rts_state = 0;
+	p_priv->dtr_state = 0;
+
+	if (serial->dev) {
+		keyspan_send_setup(port, 2);
+		/* pilot-xfer seems to work best with this delay */
+		mdelay(100);
+		/* keyspan_set_termios(port, NULL); */
+	}
+
+	/*while (p_priv->outcont_urb->status == -EINPROGRESS) {
+		dbg("%s - urb in progress", __func__);
+	}*/
+
+	p_priv->out_flip = 0;
+	p_priv->in_flip = 0;
+
+	if (serial->dev) {
+		/* Stop reading/writing urbs */
+		stop_urb(p_priv->inack_urb);
+		/* stop_urb(p_priv->outcont_urb); */
+		for (i = 0; i < 2; i++) {
+			stop_urb(p_priv->in_urbs[i]);
+			stop_urb(p_priv->out_urbs[i]);
+		}
+	}
+}
+
+/* download the firmware to a pre-renumeration device */
+static int keyspan_fake_startup(struct usb_serial *serial)
+{
+	int 				response;
+	const struct ihex_binrec 	*record;
+	char				*fw_name;
+	const struct firmware		*fw;
+
+	dbg("Keyspan startup version %04x product %04x",
+	    le16_to_cpu(serial->dev->descriptor.bcdDevice),
+	    le16_to_cpu(serial->dev->descriptor.idProduct));
+
+	if ((le16_to_cpu(serial->dev->descriptor.bcdDevice) & 0x8000)
+								!= 0x8000) {
+		dbg("Firmware already loaded.  Quitting.");
+		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;
+	}
+
+	if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) {
+		dev_err(&serial->dev->dev, "Required keyspan firmware image (%s) unavailable.\n", fw_name);
+		return(1);
+	}
+
+	dbg("Uploading Keyspan %s firmware.", fw_name);
+
+		/* download the firmware image */
+	response = ezusb_set_reset(serial, 1);
+
+	record = (const struct ihex_binrec *)fw->data;
+
+	while (record) {
+		response = ezusb_writememory(serial, be32_to_cpu(record->addr),
+					     (unsigned char *)record->data,
+					     be16_to_cpu(record->len), 0xa0);
+		if (response < 0) {
+			dev_err(&serial->dev->dev, "ezusb_writememory failed for Keyspan firmware (%d %04X %p %d)\n",
+				response, be32_to_cpu(record->addr),
+				record->data, be16_to_cpu(record->len));
+			break;
+		}
+		record = ihex_next_binrec(record);
+	}
+	release_firmware(fw);
+		/* bring device out of reset. Renumeration will occur in a
+		   moment and the new device will bind to the real driver */
+	response = ezusb_set_reset(serial, 0);
+
+	/* 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 */
+
+	dbg("%s - alloc for endpoint %d.", __func__, endpoint);
+	urb = usb_alloc_urb(0, GFP_KERNEL);		/* No ISO */
+	if (urb == NULL) {
+		dbg("%s - alloc for endpoint %d failed.", __func__, endpoint);
+		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;
+	}
+
+	dbg("%s - using urb %p for %s endpoint %x",
+	    __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)
+{
+	int				i, j;
+	struct keyspan_serial_private 	*s_priv;
+	const struct keyspan_device_details	*d_details;
+	struct usb_serial_port		*port;
+	struct keyspan_port_private	*p_priv;
+	struct callbacks		*cback;
+	int				endp;
+
+	dbg("%s", __func__);
+
+	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);
+
+	/* Setup endpoints for each port specific thing */
+	for (i = 0; i < d_details->num_ports; i++) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+
+		/* Do indat endpoints first, once for each flip */
+		endp = d_details->indat_endpoints[i];
+		for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) {
+			p_priv->in_urbs[j] = keyspan_setup_urb
+				(serial, endp, USB_DIR_IN, port,
+				 p_priv->in_buffer[j], 64,
+				 cback->indat_callback);
+		}
+		for (; j < 2; ++j)
+			p_priv->in_urbs[j] = NULL;
+
+		/* outdat endpoints also have flip */
+		endp = d_details->outdat_endpoints[i];
+		for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) {
+			p_priv->out_urbs[j] = keyspan_setup_urb
+				(serial, endp, USB_DIR_OUT, port,
+				 p_priv->out_buffer[j], 64,
+				 cback->outdat_callback);
+		}
+		for (; j < 2; ++j)
+			p_priv->out_urbs[j] = NULL;
+
+		/* inack endpoint */
+		p_priv->inack_urb = keyspan_setup_urb
+			(serial, d_details->inack_endpoints[i], USB_DIR_IN,
+			 port, p_priv->inack_buffer, 1, cback->inack_callback);
+
+		/* outcont endpoint */
+		p_priv->outcont_urb = keyspan_setup_urb
+			(serial, d_details->outcont_endpoints[i], USB_DIR_OUT,
+			 port, p_priv->outcont_buffer, 64,
+			 cback->outcont_callback);
+	}
+}
+
+/* usa19 function doesn't require prescaler */
+static int keyspan_usa19_calc_baud(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) */
+
+	dbg("%s - %d.", __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)
+		dbg("%s - %d %02x %02x.",
+				__func__, baud_rate, *rate_hi, *rate_low);
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+/* usa19hs function doesn't require prescaler */
+static int keyspan_usa19hs_calc_baud(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 */
+
+	dbg("%s - %d.", __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)
+		dbg("%s - %d %02x %02x.",
+			__func__, baud_rate, *rate_hi, *rate_low);
+
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+static int keyspan_usa19w_calc_baud(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;
+
+	dbg("%s - %d.", __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;
+		/*  dbg("%s - %d %d", __func__, *prescaler, div); */
+	}
+	return KEYSPAN_BAUD_RATE_OK;
+}
+
+	/* USA-28 supports different maximum baud rates on each port */
+static int keyspan_usa28_calc_baud(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) */
+
+	dbg("%s - %d.", __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);
+	dbg("%s - %d OK.", __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;
+	int 					outcont_urb;
+	struct urb				*this_urb;
+	int 					device_port, err;
+
+	dbg("%s reset=%d", __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->number - port->serial->minor;
+
+	outcont_urb = d_details->outcont_endpoints[device_port];
+	this_urb = p_priv->outcont_urb;
+
+	dbg("%s - endpoint %d", __func__, usb_pipeendpoint(this_urb->pipe));
+
+		/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dbg("%s - oops no urb.", __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) {
+		/*  dbg("%s - already writing", __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
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.",
+						__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)
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err);
+#if 0
+	else {
+		dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__
+		    outcont_urb, this_urb->transfer_buffer_length,
+		    usb_pipeendpoint(this_urb->pipe));
+	}
+#endif
+
+	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;
+
+	dbg("%s", __func__);
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+	device_port = port->number - port->serial->minor;
+
+	/* only do something if we have a bulk out endpoint */
+	this_urb = p_priv->outcont_urb;
+	if (this_urb == NULL) {
+		dbg("%s - oops no urb.", __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) {
+		dbg("%s already writing", __func__);
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa28_portControlMessage));
+
+	msg.setBaudRate = 1;
+	if (d_details->calculate_baud_rate(p_priv->baud, d_details->baudclk,
+		&msg.baudHi, &msg.baudLo, NULL, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+		dbg("%s - Invalid baud rate requested %d.",
+						__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)
+		dbg("%s - usb_submit_urb(setup) failed", __func__);
+#if 0
+	else {
+		dbg("%s - usb_submit_urb(setup) OK %d bytes", __func__,
+		    this_urb->transfer_buffer_length);
+	}
+#endif
+
+	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;
+
+	dbg("%s", __func__);
+
+	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->number - port->serial->minor;
+
+	/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dbg("%s - oops no urb for port %d.", __func__, port->number);
+		return -1;
+	}
+
+	dbg("%s - endpoint %d port %d (%d)",
+			__func__, usb_pipeendpoint(this_urb->pipe),
+			port->number, 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) {
+		/*  dbg("%s - already writing", __func__); */
+		mdelay(5);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa49_portControlMessage));
+
+	/*msg.portNumber = port->number;*/
+	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
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.",
+						__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)
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err);
+#if 0
+	else {
+		dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__,
+			   outcont_urb, this_urb->transfer_buffer_length,
+			   usb_pipeendpoint(this_urb->pipe));
+	}
+#endif
+
+	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;
+
+	dbg("%s", __func__);
+
+	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) {
+		dbg("%s - oops no urb.", __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) {
+		dbg("%s already writing", __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
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.",
+						__func__, p_priv->baud);
+			p_priv->baud = 9600;
+			d_details->calculate_baud_rate(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)
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __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;
+
+	dbg("%s", __func__);
+
+	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->number - port->serial->minor;
+
+	/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dbg("%s - oops no urb for port %d.", __func__,
+			port->number);
+		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) {
+		/*  dbg("%s - already writing", __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
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.",
+						__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)
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __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;
+
+	dbg("%s", __func__);
+
+	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 usb_serial_port		*port;
+	struct keyspan_serial_private 	*s_priv;
+	struct keyspan_port_private	*p_priv;
+	const struct keyspan_device_details	*d_details;
+
+	dbg("%s", __func__);
+
+	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) {
+		dbg("%s - kmalloc for keyspan_serial_private failed.",
+								__func__);
+		return -ENOMEM;
+	}
+
+	s_priv->device_details = d_details;
+	usb_set_serial_data(serial, s_priv);
+
+	/* Now setup per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		p_priv = kzalloc(sizeof(struct keyspan_port_private),
+								GFP_KERNEL);
+		if (!p_priv) {
+			dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __func__, i);
+			return 1;
+		}
+		p_priv->device_details = d_details;
+		usb_set_serial_port_data(port, p_priv);
+	}
+
+	keyspan_setup_urbs(serial);
+
+	if (s_priv->instat_urb != NULL) {
+		err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL);
+		if (err != 0)
+			dbg("%s - submit instat urb failed %d", __func__,
+				err);
+	}
+	if (s_priv->indat_urb != NULL) {
+		err = usb_submit_urb(s_priv->indat_urb, GFP_KERNEL);
+		if (err != 0)
+			dbg("%s - submit indat urb failed %d", __func__,
+				err);
+	}
+
+	return 0;
+}
+
+static void keyspan_disconnect(struct usb_serial *serial)
+{
+	int				i, j;
+	struct usb_serial_port		*port;
+	struct keyspan_serial_private 	*s_priv;
+	struct keyspan_port_private	*p_priv;
+
+	dbg("%s", __func__);
+
+	s_priv = usb_get_serial_data(serial);
+
+	/* Stop reading/writing urbs */
+	stop_urb(s_priv->instat_urb);
+	stop_urb(s_priv->glocont_urb);
+	stop_urb(s_priv->indat_urb);
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+		stop_urb(p_priv->inack_urb);
+		stop_urb(p_priv->outcont_urb);
+		for (j = 0; j < 2; j++) {
+			stop_urb(p_priv->in_urbs[j]);
+			stop_urb(p_priv->out_urbs[j]);
+		}
+	}
+
+	/* Now free them */
+	usb_free_urb(s_priv->instat_urb);
+	usb_free_urb(s_priv->indat_urb);
+	usb_free_urb(s_priv->glocont_urb);
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+		usb_free_urb(p_priv->inack_urb);
+		usb_free_urb(p_priv->outcont_urb);
+		for (j = 0; j < 2; j++) {
+			usb_free_urb(p_priv->in_urbs[j]);
+			usb_free_urb(p_priv->out_urbs[j]);
+		}
+	}
+}
+
+static void keyspan_release(struct usb_serial *serial)
+{
+	int				i;
+	struct usb_serial_port		*port;
+	struct keyspan_serial_private 	*s_priv;
+
+	dbg("%s", __func__);
+
+	s_priv = usb_get_serial_data(serial);
+
+	/*  dbg("Freeing serial->private."); */
+	kfree(s_priv);
+
+	/*  dbg("Freeing port->private."); */
+	/* Now free per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		kfree(usb_get_serial_port_data(port));
+	}
+}
+
+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");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.h
new file mode 100644
index 0000000..622853c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan.h
@@ -0,0 +1,623 @@
+/*
+  Keyspan USB to Serial Converter driver
+ 
+  (C) Copyright (C) 2000-2001
+      Hugh Blemings <hugh@blemings.org>
+   
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  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.
+  
+  See keyspan.c for update history.
+
+*/
+
+#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
+#define __LINUX_USB_SERIAL_KEYSPAN_H
+
+
+/* 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_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	(u32 baud_rate, u32 baudclk, 
+					 u8 *rate_hi, u8 *rate_low,
+					 u8 *prescaler, int portnum);
+
+static int  keyspan_usa19w_calc_baud	(u32 baud_rate, u32 baudclk,
+					 u8 *rate_hi, u8 *rate_low,
+					 u8 *prescaler, int portnum);
+
+static int  keyspan_usa28_calc_baud	(u32 baud_rate, u32 baudclk,
+					 u8 *rate_hi, u8 *rate_low,
+					 u8 *prescaler, int portnum);
+
+static int  keyspan_usa19hs_calc_baud	(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) (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);
+
+static struct usb_driver keyspan_driver = {
+	.name =		"keyspan",                
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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 */
+};
+
+/* 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,
+};
+
+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,
+};
+
+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,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&keyspan_pre_device, &keyspan_1port_device,
+	&keyspan_2port_device, &keyspan_4port_device, NULL
+};
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_pda.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_pda.c
new file mode 100644
index 0000000..693bcdf
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_pda.c
@@ -0,0 +1,844 @@
+/*
+ * USB Keyspan PDA / Xircom / Entregra 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>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static bool debug;
+
+/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
+#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE)
+	#define KEYSPAN
+#else
+	#undef KEYSPAN
+#endif
+#if defined(CONFIG_USB_SERIAL_XIRCOM) || defined(CONFIG_USB_SERIAL_XIRCOM_MODULE)
+	#define XIRCOM
+#else
+	#undef XIRCOM
+#endif
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1"
+#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 Entregra version of the same device */
+#define XIRCOM_VENDOR_ID		0x085a
+#define XIRCOM_FAKE_ID			0x8027
+#define ENTREGRA_VENDOR_ID		0x1645
+#define ENTREGRA_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(ENTREGRA_VENDOR_ID, ENTREGRA_FAKE_ID) },
+#endif
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+static struct usb_driver keyspan_pda_driver = {
+	.name =		"keyspan_pda",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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(ENTREGRA_VENDOR_ID, ENTREGRA_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;
+	struct tty_struct *tty = tty_port_tty_get(&port->port);
+	if (tty)
+		tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+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;
+
+	dbg(" request_unthrottle");
+	/* 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)
+		dbg("%s - error %d from usb_control_msg",
+		    __func__, result);
+}
+
+
+static void keyspan_pda_rx_interrupt(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	/* see if the message is data or a status interrupt */
+	switch (data[0]) {
+	case 0:
+		tty = tty_port_tty_get(&port->port);
+		 /* rest of message is rx data */
+		if (tty && urb->actual_length) {
+			tty_insert_flip_string(tty, data + 1,
+						urb->actual_length - 1);
+			tty_flip_buffer_push(tty);
+		}
+		tty_kref_put(tty);
+		break;
+	case 1:
+		/* status interrupt */
+		dbg(" rx int, d1=%d, d2=%d", 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",
+			__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;
+	dbg("keyspan_pda_rx_throttle port %d", port->number);
+	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 */
+	dbg("keyspan_pda_rx_unthrottle port %d", port->number);
+	if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
+		dbg(" usb_submit_urb(read urb) failed");
+}
+
+
+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)
+		dbg("%s - error %d from usb_control_msg",
+		    __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) {
+		dbg("can't handle requested baud rate");
+		/* 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 >= 0)
+		*value = *data;
+
+	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. */
+	dbg("keyspan_pda_write(%d)", count);
+	if (count == 0) {
+		dbg(" write request of 0 bytes");
+		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) {
+			dbg(" roomquery says %d", *room);
+			priv->tx_room = *room;
+		}
+		kfree(room);
+		if (rc < 0) {
+			dbg(" roomquery failed");
+			goto exit;
+		}
+		if (rc == 0) {
+			dbg(" roomquery returned 0 bytes");
+			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) {
+			dbg(" usb_submit_urb(write bulk) failed");
+			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 (serial->dev) {
+		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) {
+		dbg("%s - roomquery failed", __func__);
+		goto error;
+	}
+	if (rc == 0) {
+		dbg("%s - roomquery returned 0 bytes", __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) {
+		dbg("%s - usb_submit_urb(read int) failed", __func__);
+		goto error;
+	}
+error:
+	kfree(room);
+	return rc;
+}
+static void keyspan_pda_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+
+	if (serial->dev) {
+		/* shutdown our bulk reads and writes */
+		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;
+	const struct ihex_binrec *record;
+	const struct firmware *fw;
+
+	/* download the firmware here ... */
+	response = ezusb_set_reset(serial, 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) == ENTREGRA_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 (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) {
+		dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
+			fw_name);
+		return -ENOENT;
+	}
+	record = (const struct ihex_binrec *)fw->data;
+
+	while (record) {
+		response = ezusb_writememory(serial, be32_to_cpu(record->addr),
+					     (unsigned char *)record->data,
+					     be16_to_cpu(record->len), 0xa0);
+		if (response < 0) {
+			dev_err(&serial->dev->dev, "ezusb_writememory failed "
+				"for Keyspan PDA firmware (%d %04X %p %d)\n",
+				response, be32_to_cpu(record->addr),
+				record->data, be16_to_cpu(record->len));
+			break;
+		}
+		record = ihex_next_binrec(record);
+	}
+	release_firmware(fw);
+	/* bring device out of reset. Renumeration will occur in a moment
+	   and the new device will bind to the real driver */
+	response = ezusb_set_reset(serial, 0);
+
+	/* 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_startup(struct usb_serial *serial)
+{
+
+	struct keyspan_pda_private *priv;
+
+	/* allocate the private data structures for all ports. Well, for all
+	   one ports. */
+
+	priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
+	if (!priv)
+		return 1; /* error */
+	usb_set_serial_port_data(serial->port[0], priv);
+	init_waitqueue_head(&serial->port[0]->write_wait);
+	INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write);
+	INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle);
+	priv->serial = serial;
+	priv->port = serial->port[0];
+	return 0;
+}
+
+static void keyspan_pda_release(struct usb_serial *serial)
+{
+	dbg("%s", __func__);
+
+	kfree(usb_get_serial_port_data(serial->port[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 / Entregra 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,
+	.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,
+	.attach =		keyspan_pda_startup,
+	.release =		keyspan_pda_release,
+};
+
+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(keyspan_pda_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa26msg.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa26msg.h
new file mode 100644
index 0000000..3808727
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/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
+			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
+
+	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/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa28msg.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa28msg.h
new file mode 100644
index 0000000..dee454c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/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/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa49msg.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa49msg.h
new file mode 100644
index 0000000..163b2de
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/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/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa67msg.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa67msg.h
new file mode 100644
index 0000000..20fa3e2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/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/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa90msg.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/keyspan_usa90msg.h
new file mode 100644
index 0000000..86708ec
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/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/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.c
new file mode 100644
index 0000000..10f0540
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.c
@@ -0,0 +1,702 @@
+/*
+ * KLSI KL5KUSB105 chip RS232 converter driver
+ *
+ *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * 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/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/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "kl5kusb105.h"
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.4"
+#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_startup(struct usb_serial *serial);
+static void klsi_105_release(struct usb_serial *serial);
+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 int  klsi_105_tiocmset(struct tty_struct *tty,
+			unsigned int set, unsigned int clear);
+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) },
+	{ USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+	{ }		/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver kl5kusb105d_driver = {
+	.name =		"kl5kusb105d",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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,
+	/*.break_ctl =		klsi_105_break_ctl,*/
+	.tiocmget =		klsi_105_tiocmget,
+	.tiocmset =		klsi_105_tiocmset,
+	.attach =		klsi_105_startup,
+	.release =		klsi_105_release,
+	.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;
+} __attribute__ ((packed));
+
+struct klsi_105_private {
+	struct klsi_105_port_settings	cfg;
+	struct ktermios			termios;
+	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_info(&port->serial->dev->dev,
+		 "%d byte block, baudrate %x, databits %d, u1 %d, u2 %d\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;
+
+	dev_info(&port->serial->dev->dev, "sending SIO Poll request\n");
+
+	status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL);
+	if (!status_buf) {
+		dev_err(&port->dev, "%s - out of memory for status buffer.\n",
+				__func__);
+		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 < 0)
+		dev_err(&port->dev, "Reading line status failed (error = %d)\n",
+			rc);
+	else {
+		status = get_unaligned_le16(status_buf);
+
+		dev_info(&port->serial->dev->dev, "read status %x %x",
+			 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_startup(struct usb_serial *serial)
+{
+	struct klsi_105_private *priv;
+	int i;
+
+	/* check if we support the product id (see keyspan.c)
+	 * FIXME
+	 */
+
+	/* allocate the private data structure */
+	for (i = 0; i < serial->num_ports; i++) {
+		priv = kmalloc(sizeof(struct klsi_105_private),
+						   GFP_KERNEL);
+		if (!priv) {
+			dbg("%skmalloc for klsi_105_private failed.", __func__);
+			i--;
+			goto err_cleanup;
+		}
+		/* 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;
+
+		usb_set_serial_port_data(serial->port[i], priv);
+
+		spin_lock_init(&priv->lock);
+
+		/* priv->termios is left uninitialized until port opening */
+		init_waitqueue_head(&serial->port[i]->write_wait);
+	}
+
+	return 0;
+
+err_cleanup:
+	for (; i >= 0; i--) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
+}
+
+static void klsi_105_release(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+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;
+	int i;
+	unsigned long line_state;
+	struct klsi_105_port_settings *cfg;
+	unsigned long flags;
+
+	dbg("%s port %d", __func__, port->number);
+
+	/* 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) {
+		dev_err(&port->dev, "%s - out of memory for config buffer.\n",
+				__func__);
+		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);
+
+	/* set up termios structure */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->termios.c_iflag = tty->termios->c_iflag;
+	priv->termios.c_oflag = tty->termios->c_oflag;
+	priv->termios.c_cflag = tty->termios->c_cflag;
+	priv->termios.c_lflag = tty->termios->c_lflag;
+	for (i = 0; i < NCCS; i++)
+		priv->termios.c_cc[i] = tty->termios->c_cc[i];
+	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 exit;
+	}
+
+	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;
+	} else
+		dbg("%s - enabled reading", __func__);
+
+	rc = klsi_105_get_line_state(port, &line_state);
+	if (rc >= 0) {
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->line_state = line_state;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		dbg("%s - read line state 0x%lx", __func__, line_state);
+		retval = 0;
+	} else
+		retval = rc;
+
+exit:
+	kfree(cfg);
+	return retval;
+}
+
+static void klsi_105_close(struct usb_serial_port *port)
+{
+	int rc;
+
+	dbg("%s port %d", __func__, port->number);
+
+	mutex_lock(&port->serial->disc_mutex);
+	if (!port->serial->disconnected) {
+		/* 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,
+				"Disabling read failed (error = %d)\n", rc);
+	}
+	mutex_unlock(&port->serial->disc_mutex);
+
+	/* shutdown our bulk reads and writes */
+	usb_serial_generic_close(port);
+
+	/* wgg - do I need this? I think so. */
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+/* 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;
+	struct tty_struct *tty;
+	unsigned len;
+
+	/* empty urbs seem to happen, we ignore them */
+	if (!urb->actual_length)
+		return;
+
+	if (urb->actual_length <= KLSI_HDR_LEN) {
+		dbg("%s - malformed packet", __func__);
+		return;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	len = get_unaligned_le16(data);
+	if (len > urb->actual_length - KLSI_HDR_LEN) {
+		dbg("%s - packet length mismatch", __func__);
+		len = urb->actual_length - KLSI_HDR_LEN;
+	}
+
+	tty_insert_flip_string(tty, data + KLSI_HDR_LEN, len);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+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);
+	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) {
+		dev_err(&port->dev, "%s - out of memory for config buffer.\n",
+				__func__);
+		return;
+	}
+
+	/* lock while we are modifying the settings */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/*
+	 * Update baud rate
+	 */
+	baud = tty_get_baud_rate(tty);
+
+	if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
+		/* reassert DTR and (maybe) RTS on transition from B0 */
+		if ((old_cflag & CBAUD) == B0) {
+			dbg("%s: baud was B0", __func__);
+#if 0
+			priv->control_state |= TIOCM_DTR;
+			/* don't set RTS if using hardware flow control */
+			if (!(old_cflag & CRTSCTS))
+				priv->control_state |= TIOCM_RTS;
+			mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		}
+	}
+	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:
+		dbg("KLSI USB->Serial converter:"
+		    " unsupported baudrate request, using default of 9600");
+			priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+		baud = 9600;
+		break;
+	}
+	if ((cflag & CBAUD) == B0) {
+		dbg("%s: baud is B0", __func__);
+		/* Drop RTS and DTR */
+		/* maybe this should be simulated by sending read
+		 * disable and read enable messages?
+		 */
+		;
+#if 0
+		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+	}
+	tty_encode_baud_rate(tty, baud, baud);
+
+	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+		/* set the number of data bits */
+		switch (cflag & CSIZE) {
+		case CS5:
+			dbg("%s - 5 bits/byte not supported", __func__);
+			spin_unlock_irqrestore(&priv->lock, flags);
+			goto err;
+		case CS6:
+			dbg("%s - 6 bits/byte not supported", __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(&port->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);
+#if 0
+		priv->last_lcr = 0;
+
+		/* set the parity */
+		if (cflag & PARENB)
+			priv->last_lcr |= (cflag & PARODD) ?
+				MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
+		else
+			priv->last_lcr |= MCT_U232_PARITY_NONE;
+
+		/* set the number of stop bits */
+		priv->last_lcr |= (cflag & CSTOPB) ?
+			MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
+
+		mct_u232_set_line_ctrl(serial, priv->last_lcr);
+#endif
+		;
+	}
+	/*
+	 * 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;
+		/* Drop DTR/RTS if no flow control otherwise assert */
+#if 0
+		if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
+			priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+		else
+			priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		;
+	}
+	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);
+}
+
+#if 0
+static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv =
+				(struct mct_u232_private *)port->private;
+	unsigned char lcr = priv->last_lcr;
+
+	dbg("%sstate=%d", __func__, break_state);
+
+	/* LOCKING */
+	if (break_state)
+		lcr |= MCT_U232_SET_BREAK;
+
+	mct_u232_set_line_ctrl(serial, lcr);
+}
+#endif
+
+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;
+	dbg("%s - request, just guessing", __func__);
+
+	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);
+	dbg("%s - read line state 0x%lx", __func__, line_state);
+	return (int)line_state;
+}
+
+static int klsi_105_tiocmset(struct tty_struct *tty,
+			     unsigned int set, unsigned int clear)
+{
+	int retval = -EINVAL;
+
+	dbg("%s", __func__);
+
+/* if this ever gets implemented, it should be done something like this:
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	int control;
+
+	spin_lock_irqsave (&priv->lock, flags);
+	if (set & TIOCM_RTS)
+		priv->control_state |= TIOCM_RTS;
+	if (set & TIOCM_DTR)
+		priv->control_state |= TIOCM_DTR;
+	if (clear & TIOCM_RTS)
+		priv->control_state &= ~TIOCM_RTS;
+	if (clear & TIOCM_DTR)
+		priv->control_state &= ~TIOCM_DTR;
+	control = priv->control_state;
+	spin_unlock_irqrestore (&priv->lock, flags);
+	retval = mct_u232_set_modem_ctrl(serial, control);
+*/
+	return retval;
+}
+
+module_usb_serial_driver(kl5kusb105d_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "enable extensive debugging messages");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.h
new file mode 100644
index 0000000..22a90ba
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kl5kusb105.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+
+#define KLSI_VID		0x05e9
+#define KLSI_KL5KUSB105D_PID	0x00c0
+
+/* 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.c
new file mode 100644
index 0000000..c3a53ac
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.c
@@ -0,0 +1,693 @@
+/*
+ *  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.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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/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/ioctl.h>
+#include "kobil_sct.h"
+
+static bool debug;
+
+/* Version Information */
+#define DRIVER_VERSION "21/05/2004"
+#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_startup(struct usb_serial *serial);
+static void kobil_release(struct usb_serial *serial);
+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_callback(struct urb *purb);
+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_driver kobil_driver = {
+	.name =		"kobil",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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,
+	.attach =		kobil_startup,
+	.release =		kobil_release,
+	.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,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&kobil_device, NULL
+};
+
+struct kobil_private {
+	int write_int_endpoint_address;
+	int read_int_endpoint_address;
+	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_startup(struct usb_serial *serial)
+{
+	int i;
+	struct kobil_private *priv;
+	struct usb_device *pdev;
+	struct usb_host_config *actconfig;
+	struct usb_interface *interface;
+	struct usb_host_interface *altsetting;
+	struct usb_host_endpoint *endpoint;
+
+	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:
+		printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n");
+		break;
+	case KOBIL_ADAPTER_K_PRODUCT_ID:
+		printk(KERN_DEBUG
+		  "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
+		break;
+	case KOBIL_USBTWIN_PRODUCT_ID:
+		printk(KERN_DEBUG "KOBIL USBTWIN detected\n");
+		break;
+	case KOBIL_KAAN_SIM_PRODUCT_ID:
+		printk(KERN_DEBUG "KOBIL KAAN SIM detected\n");
+		break;
+	}
+	usb_set_serial_port_data(serial->port[0], priv);
+
+	/* search for the necessary endpoints */
+	pdev = serial->dev;
+	actconfig = pdev->actconfig;
+	interface = actconfig->interface[0];
+	altsetting = interface->cur_altsetting;
+	endpoint = altsetting->endpoint;
+
+	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
+		endpoint = &altsetting->endpoint[i];
+		if (usb_endpoint_is_int_out(&endpoint->desc)) {
+			dbg("%s Found interrupt out endpoint. Address: %d",
+				__func__, endpoint->desc.bEndpointAddress);
+			priv->write_int_endpoint_address =
+				endpoint->desc.bEndpointAddress;
+		}
+		if (usb_endpoint_is_int_in(&endpoint->desc)) {
+			dbg("%s Found interrupt in  endpoint. Address: %d",
+				__func__, endpoint->desc.bEndpointAddress);
+			priv->read_int_endpoint_address =
+				endpoint->desc.bEndpointAddress;
+		}
+	}
+	return 0;
+}
+
+
+static void kobil_release(struct usb_serial *serial)
+{
+	int i;
+	dbg("%s - port %d", __func__, serial->port[0]->number);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+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_lflag &= ~(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)
+{
+	int result = 0;
+	struct kobil_private *priv;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+	int write_urb_transfer_buffer_length = 8;
+
+	dbg("%s - port %d", __func__, port->number);
+	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;
+
+	/* allocate write_urb */
+	if (!port->write_urb) {
+		dbg("%s - port %d  Allocating port->write_urb",
+						__func__, port->number);
+		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!port->write_urb) {
+			dbg("%s - port %d usb_alloc_urb failed",
+						__func__, port->number);
+			kfree(transfer_buffer);
+			return -ENOMEM;
+		}
+	}
+
+	/* allocate memory for write_urb transfer buffer */
+	port->write_urb->transfer_buffer =
+			kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
+	if (!port->write_urb->transfer_buffer) {
+		kfree(transfer_buffer);
+		usb_free_urb(port->write_urb);
+		port->write_urb = NULL;
+		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
+	);
+	dbg("%s - port %d Send get_HW_version URB returns: %i",
+		__func__, port->number, result);
+	dbg("Harware version: %i.%i.%i",
+		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
+	);
+	dbg("%s - port %d Send get_FW_version URB returns: %i",
+					__func__, port->number, result);
+	dbg("Firmware version: %i.%i.%i",
+		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_rcvctrlpipe(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,
+			  transfer_buffer,
+			  0,
+			  KOBIL_TIMEOUT
+		);
+		dbg("%s - port %d Send set_baudrate URB returns: %i",
+					__func__, port->number, result);
+
+		/* reset all queues */
+		result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_Misc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  SUSBCR_MSC_ResetAllQueues,
+			  0,
+			  transfer_buffer,
+			  0,
+			  KOBIL_TIMEOUT
+		);
+		dbg("%s - port %d Send reset_all_queues URB returns: %i",
+					__func__, port->number, 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_ATOMIC);
+		dbg("%s - port %d Send read URB returns: %i",
+					__func__, port->number, result);
+	}
+
+	kfree(transfer_buffer);
+	return 0;
+}
+
+
+static void kobil_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	/* FIXME: Add rts/dtr methods */
+	if (port->write_urb) {
+		usb_poison_urb(port->write_urb);
+		kfree(port->write_urb->transfer_buffer);
+		usb_free_urb(port->write_urb);
+		port->write_urb = NULL;
+	}
+	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;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+/*	char *dbg_data; */
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (status) {
+		dbg("%s - port %d Read int status not zero: %d",
+		    __func__, port->number, status);
+		return;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	if (tty && urb->actual_length) {
+
+		/* BEGIN DEBUG */
+		/*
+		  dbg_data = kzalloc((3 *  purb->actual_length + 10)
+						* sizeof(char), GFP_KERNEL);
+		  if (! dbg_data) {
+			  return;
+		  }
+		  for (i = 0; i < purb->actual_length; i++) {
+			  sprintf(dbg_data +3*i, "%02X ", data[i]);
+		  }
+		  dbg(" <-- %s", dbg_data);
+		  kfree(dbg_data);
+		*/
+		/* END DEBUG */
+
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	dbg("%s - port %d Send read URB returns: %i",
+			__func__, port->number, result);
+}
+
+
+static void kobil_write_callback(struct urb *purb)
+{
+}
+
+
+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) {
+		dbg("%s - port %d write request of 0 bytes",
+						__func__, port->number);
+		return 0;
+	}
+
+	priv = usb_get_serial_port_data(port);
+
+	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
+		dbg("%s - port %d Error: write request bigger than buffer size", __func__, port->number);
+		return -ENOMEM;
+	}
+
+	/* Copy data to buffer */
+	memcpy(priv->buf + priv->filled, buf, count);
+	usb_serial_debug_data(debug, &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 = (todo < 8) ? todo : 8;
+			/* copy data to transfer buffer */
+			memcpy(port->write_urb->transfer_buffer,
+					priv->buf + priv->cur_pos, length);
+			usb_fill_int_urb(port->write_urb,
+				  port->serial->dev,
+				  usb_sndintpipe(port->serial->dev,
+					priv->write_int_endpoint_address),
+				  port->write_urb->transfer_buffer,
+				  length,
+				  kobil_write_callback,
+				  port,
+				  8
+			);
+
+			priv->cur_pos = priv->cur_pos + length;
+			result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+			dbg("%s - port %d Send write URB returns: %i",
+					__func__, port->number, 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);
+			dbg("%s - port %d Send read URB returns: %i",
+					__func__, port->number, result);
+		}
+	}
+	return count;
+}
+
+
+static int kobil_write_room(struct tty_struct *tty)
+{
+	/* dbg("%s - port %d", __func__, port->number); */
+	/* 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);
+
+	dbg("%s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x",
+	    __func__, port->number, result, transfer_buffer[0]);
+
+	result = 0;
+	if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
+		result = TIOCM_DSR;
+	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 kobil_private *priv;
+	int result;
+	int dtr = 0;
+	int rts = 0;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+
+	/* 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;
+	}
+
+	/* allocate memory for transfer buffer */
+	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	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)
+			dbg("%s - port %d Setting DTR",
+						__func__, port->number);
+		else
+			dbg("%s - port %d Clearing DTR",
+						__func__, port->number);
+		result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_SetStatusLinesOrQueues,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
+			  0,
+			  transfer_buffer,
+			  0,
+			  KOBIL_TIMEOUT);
+	} else {
+		if (rts != 0)
+			dbg("%s - port %d Setting RTS",
+						__func__, port->number);
+		else
+			dbg("%s - port %d Clearing RTS",
+						__func__, port->number);
+		result = usb_control_msg(port->serial->dev,
+			usb_rcvctrlpipe(port->serial->dev, 0),
+			SUSBCRequest_SetStatusLinesOrQueues,
+			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
+			0,
+			transfer_buffer,
+			0,
+			KOBIL_TIMEOUT);
+	}
+	dbg("%s - port %d Send set_status_line URB returns: %i",
+					__func__, port->number, result);
+	kfree(transfer_buffer);
+	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 = *old;
+		return;
+	}
+
+	speed = tty_get_baud_rate(tty);
+	switch (speed) {
+	case 1200:
+		urb_val = SUSBCR_SBR_1200;
+		break;
+	default:
+		speed = 9600;
+	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_rcvctrlpipe(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);
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+	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:
+		transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
+		if (!transfer_buffer)
+			return -ENOBUFS;
+
+		result = usb_control_msg(port->serial->dev,
+			  usb_rcvctrlpipe(port->serial->dev, 0),
+			  SUSBCRequest_Misc,
+			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+			  SUSBCR_MSC_ResetAllQueues,
+			  0,
+			  NULL, /* transfer_buffer, */
+			  0,
+			  KOBIL_TIMEOUT
+			);
+
+		dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result);
+		kfree(transfer_buffer);
+		return (result < 0) ? -EIO: 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+module_usb_serial_driver(kobil_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.h
new file mode 100644
index 0000000..be207f7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/kobil_sct.h
@@ -0,0 +1,77 @@
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.c
new file mode 100644
index 0000000..954fb86
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.c
@@ -0,0 +1,918 @@
+/*
+ * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
+ *
+ *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * 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/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 <asm/unaligned.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <linux/ioctl.h>
+#include "mct_u232.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "z2.1"		/* Linux in-kernel version */
+#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
+#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
+
+static bool debug;
+
+/*
+ * Function prototypes
+ */
+static int  mct_u232_startup(struct usb_serial *serial);
+static void mct_u232_release(struct usb_serial *serial);
+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 int  mct_u232_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+static int  mct_u232_get_icount(struct tty_struct *tty,
+			struct serial_icounter_struct *icount);
+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_combined[] = {
+	{ 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_combined);
+
+static struct usb_driver mct_u232_driver = {
+	.name =		"mct_u232",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table_combined,
+};
+
+static struct usb_serial_driver mct_u232_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"mct_u232",
+	},
+	.description =	     "MCT U232",
+	.id_table =	     id_table_combined,
+	.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,
+	.attach =	     mct_u232_startup,
+	.release =	     mct_u232_release,
+	.ioctl =             mct_u232_ioctl,
+	.get_icount =        mct_u232_get_icount,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&mct_u232_device, NULL
+};
+
+struct mct_u232_private {
+	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 */
+	struct async_icount  icount;
+};
+
+#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(cpu_to_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);
+	dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", 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;
+
+	dbg("set_baud_rate: send second control message, data = %02X",
+							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 *serial, 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(serial->dev, usb_sndctrlpipe(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(&serial->dev->dev,
+			"Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
+	dbg("set_line_ctrl: 0x%x", lcr);
+	kfree(buf);
+	return rc;
+} /* mct_u232_set_line_ctrl */
+
+static int mct_u232_set_modem_ctrl(struct usb_serial *serial,
+				   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(serial->dev, usb_sndctrlpipe(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);
+
+	dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr);
+
+	if (rc < 0) {
+		dev_err(&serial->dev->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 *serial,
+						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(serial->dev, usb_rcvctrlpipe(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 < 0) {
+		dev_err(&serial->dev->dev,
+			"Get MODEM STATus failed (error = %d)\n", rc);
+		*msr = 0;
+	} else {
+		*msr = buf[0];
+	}
+	dbg("get_modem_stat: 0x%x", *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(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;
+	dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state);
+} /* mct_u232_msr_to_state */
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int mct_u232_startup(struct usb_serial *serial)
+{
+	struct mct_u232_private *priv;
+	struct usb_serial_port *port, *rport;
+
+	priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	spin_lock_init(&priv->lock);
+	usb_set_serial_port_data(serial->port[0], priv);
+
+	init_waitqueue_head(&serial->port[0]->write_wait);
+
+	/* Puh, that's dirty */
+	port = serial->port[0];
+	rport = serial->port[1];
+	/* No unlinking, it wasn't submitted yet. */
+	usb_free_urb(port->read_urb);
+	port->read_urb = rport->interrupt_in_urb;
+	rport->interrupt_in_urb = NULL;
+	port->read_urb->context = port;
+
+	return 0;
+} /* mct_u232_startup */
+
+
+static void mct_u232_release(struct usb_serial *serial)
+{
+	struct mct_u232_private *priv;
+	int i;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		/* My special items, the standard routines free my urbs */
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+	}
+} /* mct_u232_release */
+
+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;
+
+	dbg("%s port %d", __func__, port->number);
+
+	/* 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 && (tty->termios->c_cflag & CBAUD))
+		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(serial, control_state);
+	mct_u232_set_line_ctrl(serial, last_lcr);
+
+	/* Read modem status and update control state */
+	mct_u232_get_modem_stat(serial, &last_msr);
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->last_msr = last_msr;
+	mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&port->dev,
+			"usb_submit_urb(read bulk) 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(port->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->serial, control_state);
+}
+
+static void mct_u232_close(struct usb_serial_port *port)
+{
+	dbg("%s port %d", __func__, port->number);
+
+	/*
+	 * Must kill the read urb as it is actually an interrupt urb, which
+	 * generic close thus fails to kill.
+	 */
+	usb_kill_urb(port->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);
+	struct usb_serial *serial = port->serial;
+	struct tty_struct *tty;
+	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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	if (!serial) {
+		dbg("%s - bad serial pointer, exiting", __func__);
+		return;
+	}
+
+	dbg("%s - port %d", __func__, port->number);
+	usb_serial_debug_data(debug, &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 = tty_port_tty_get(&port->port);
+			if (tty) {
+				tty_insert_flip_string(tty, data,
+						urb->actual_length);
+				tty_flip_buffer_push(tty);
+			}
+			tty_kref_put(tty);
+		}
+		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(&priv->control_state, priv->last_msr);
+
+	mct_u232_msr_to_icount(&priv->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->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) {
+		dbg("%s: baud was B0", __func__);
+		control_state |= TIOCM_DTR | TIOCM_RTS;
+		mct_u232_set_modem_ctrl(serial, control_state);
+	}
+
+	mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
+
+	if ((cflag & CBAUD) == B0) {
+		dbg("%s: baud is B0", __func__);
+		/* Drop RTS and DTR */
+		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(serial, 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(serial, 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 usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned char lcr;
+	unsigned long flags;
+
+	dbg("%sstate=%d", __func__, break_state);
+
+	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(serial, 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;
+
+	dbg("%s", __func__);
+
+	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 usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned int control_state;
+	unsigned long flags;
+
+	dbg("%s", __func__);
+
+	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(serial, 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+		(void) mct_u232_set_modem_ctrl(port->serial, 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+		(void) mct_u232_set_modem_ctrl(port->serial, control_state);
+	} else {
+		spin_unlock_irq(&priv->lock);
+	}
+}
+
+static int  mct_u232_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	DEFINE_WAIT(wait);
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+	struct async_icount cnow, cprev;
+	unsigned long flags;
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+
+	case TIOCMIWAIT:
+
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+
+		spin_lock_irqsave(&mct_u232_port->lock, flags);
+		cprev = mct_u232_port->icount;
+		spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+		for ( ; ; ) {
+			prepare_to_wait(&port->delta_msr_wait,
+					&wait, TASK_INTERRUPTIBLE);
+			schedule();
+			finish_wait(&port->delta_msr_wait, &wait);
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			spin_lock_irqsave(&mct_u232_port->lock, flags);
+			cnow = mct_u232_port->icount;
+			spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int  mct_u232_get_icount(struct tty_struct *tty,
+			struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+	struct async_icount *ic = &mct_u232_port->icount;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mct_u232_port->lock, flags);
+
+	icount->cts = ic->cts;
+	icount->dsr = ic->dsr;
+	icount->rng = ic->rng;
+	icount->dcd = ic->dcd;
+	icount->rx = ic->rx;
+	icount->tx = ic->tx;
+	icount->frame = ic->frame;
+	icount->overrun = ic->overrun;
+	icount->parity = ic->parity;
+	icount->brk = ic->brk;
+	icount->buf_overrun = ic->buf_overrun;
+
+	spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+
+	dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
+		__func__,  port->number, icount->rx, icount->tx);
+	return 0;
+}
+
+module_usb_serial_driver(mct_u232_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.h
new file mode 100644
index 0000000..d325bb8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mct_u232.h
@@ -0,0 +1,467 @@
+/*
+ * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver
+ *
+ *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/metro-usb.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/metro-usb.c
new file mode 100644
index 0000000..7c14671
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/metro-usb.c
@@ -0,0 +1,394 @@
+/*
+  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/init.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/errno.h>
+#include <linux/uaccess.h>
+#include <linux/usb/serial.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.2.0.0"
+#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 struct usb_device_id id_table[] = {
+	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) },
+	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
+	{ }, /* Terminating entry. */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* Input parameter constants. */
+static bool debug;
+
+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);
+	struct tty_struct *tty;
+	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__, result);
+		return;
+	default:
+		dev_dbg(&port->dev,
+			"%s - non-zero urb received, error code=%d\n",
+			__func__, result);
+		goto exit;
+	}
+
+
+	/* Set the data read from the usb port into the serial port buffer. */
+	tty = tty_port_tty_get(&port->port);
+	if (!tty) {
+		dev_dbg(&port->dev, "%s - bad tty pointer - exiting\n",
+			__func__);
+		return;
+	}
+
+	if (tty && urb->actual_length) {
+		/* Loop through the data copying each byte to the tty layer. */
+		tty_insert_flip_string(tty, data, urb->actual_length);
+
+		/* Force the data to the tty layer. */
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	/* Set any port variables. */
+	spin_lock_irqsave(&metro_priv->lock, flags);
+	throttled = metro_priv->throttled;
+	spin_unlock_irqrestore(&metro_priv->lock, flags);
+
+	/* Continue trying to read if set. */
+	if (!throttled) {
+		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,
+				 metrousb_read_int_callback, port, 1);
+
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+
+		if (result)
+			dev_dbg(&port->dev,
+				"%s - failed submitting interrupt in urb, error code=%d\n",
+				__func__, result);
+	}
+	return;
+
+exit:
+	/* Try to resubmit the urb. */
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_dbg(&port->dev,
+			"%s - failed submitting interrupt in urb, error code=%d\n",
+			__func__, result);
+}
+
+static void metrousb_cleanup(struct usb_serial_port *port)
+{
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	if (port->serial->dev) {
+		/* Shutdown any interrupt in urbs. */
+		if (port->interrupt_in_urb) {
+			usb_unlink_urb(port->interrupt_in_urb);
+			usb_kill_urb(port->interrupt_in_urb);
+		}
+	}
+}
+
+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;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	/* Make sure the urb is initialized. */
+	if (!port->interrupt_in_urb) {
+		dev_dbg(&port->dev, "%s - interrupt urb not initialized\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	/* 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_dbg(&port->dev,
+			"%s - failed submitting interrupt in urb, error code=%d\n",
+			__func__, result);
+		goto exit;
+	}
+
+	dev_dbg(&port->dev, "%s - port open\n", __func__);
+exit:
+	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_dbg(&serial->dev->dev,
+			"%s - set modem ctrl=0x%x failed, error code=%d\n",
+			__func__, mcr, retval);
+
+	return retval;
+}
+
+static void metrousb_shutdown(struct usb_serial *serial)
+{
+	int i = 0;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	/* Stop reading and writing on all ports. */
+	for (i = 0; i < serial->num_ports; ++i) {
+		/* Close any open urbs. */
+		metrousb_cleanup(serial->port[i]);
+
+		/* Free memory. */
+		kfree(usb_get_serial_port_data(serial->port[i]));
+		usb_set_serial_port_data(serial->port[i], NULL);
+
+		dev_dbg(&serial->dev->dev, "%s - freed port number=%d\n",
+			__func__, serial->port[i]->number);
+	}
+}
+
+static int metrousb_startup(struct usb_serial *serial)
+{
+	struct metrousb_private *metro_priv;
+	struct usb_serial_port *port;
+	int i = 0;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	/* Loop through the serial ports setting up the private structures.
+	 * Currently we only use one port. */
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+
+		/* Declare memory. */
+		metro_priv = kzalloc(sizeof(struct metrousb_private), GFP_KERNEL);
+		if (!metro_priv)
+			return -ENOMEM;
+
+		/* Initialize memory. */
+		spin_lock_init(&metro_priv->lock);
+		usb_set_serial_port_data(port, metro_priv);
+
+		dev_dbg(&serial->dev->dev, "%s - port number=%d\n ",
+			__func__, port->number);
+	}
+
+	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;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	/* 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;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	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;
+
+	dev_dbg(tty->dev, "%s\n", __func__);
+
+	/* 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. */
+	port->interrupt_in_urb->dev = port->serial->dev;
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result)
+		dev_dbg(tty->dev,
+			"failed submitting interrupt in urb error code=%d\n",
+			result);
+}
+
+static struct usb_driver metrousb_driver = {
+	.name =		"metro-usb",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table
+};
+
+static struct usb_serial_driver metrousb_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"metro-usb",
+	},
+	.description		= "Metrologic USB to serial converter.",
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.open			= metrousb_open,
+	.close			= metrousb_cleanup,
+	.read_int_callback	= metrousb_read_int_callback,
+	.attach			= metrousb_startup,
+	.release		= metrousb_shutdown,
+	.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(metrousb_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Nicastro");
+MODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+/* Module input parameters */
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Print debug info (bool 1=on, 0=off)");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7720.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7720.c
new file mode 100644
index 0000000..4491830
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7720.c
@@ -0,0 +1,2231 @@
+/*
+ * mos7720.c
+ *   Controls the Moschip 7720 usb to dual port serial convertor
+ *
+ * Copyright 2006 Moschip Semiconductor Tech. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * 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/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/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+#include <linux/parport.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "2.1"
+#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 Rotinue 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 async_icount	icount;
+	struct usb_serial_port	*port;	/* loop back to the owner */
+	struct urb		*write_urb_pool[NUM_URBS];
+};
+
+static bool debug;
+
+static struct usb_serial_driver moschip7720_2port_driver;
+
+#define USB_VENDOR_ID_MOSCHIP		0x9710
+#define MOSCHIP_DEVICE_ID_7720		0x7720
+#define MOSCHIP_DEVICE_ID_7715		0x7715
+
+static const struct usb_device_id moschip_port_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, moschip_port_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 {
+	THR,	          /* serial port regs */
+	RHR,
+	IER,
+	FCR,
+	ISR,
+	LCR,
+	MCR,
+	LSR,
+	MSR,
+	SPR,
+	DLL,
+	DLM,
+	DPR,              /* parallel port regs */
+	DSR,
+	DCR,
+	ECR,
+	SP1_REG,          /* device control regs */
+	SP2_REG,          /* serial port 2 (7720 only) */
+	PP_REG,
+	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,		/* THR */
+		0x00,		/* RHR */
+		0x01,		/* IER */
+		0x02,		/* FCR */
+		0x02,		/* ISR */
+		0x03,		/* LCR */
+		0x04,		/* MCR */
+		0x05,		/* LSR */
+		0x06,		/* MSR */
+		0x07,		/* SPR */
+		0x00,		/* DLL */
+		0x01,		/* DLM */
+		0x00,		/* DPR */
+		0x01,		/* DSR */
+		0x02,		/* DCR */
+		0x0a,		/* ECR */
+		0x01,		/* SP1_REG */
+		0x02,		/* SP2_REG (7720 only) */
+		0x04,		/* PP_REG (7715 only) */
+		0x08,		/* 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 >= SP1_REG)	      /* control reg */
+		return 0x0000;
+
+	else if (reg >= 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", 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 if (status < 0)
+		dev_err(&usbdev->dev,
+			"mos7720: usb_control_msg() failed: %d", status);
+	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, 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);
+
+	dbg("%s called", __func__);
+	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;
+	dbg("%s called", __func__);
+	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;
+	struct list_head *cursor, *next;
+
+	dbg("%s called", __func__);
+
+	/* if release function ran, game over */
+	if (unlikely(mos_parport->serial == NULL))
+		return;
+
+	/* try again to get the mutex */
+	if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
+		dbg("%s: rescheduling tasklet", __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);
+		dbg("%s: deferred_urbs list empty", __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(urbtrack, &mos_parport->active_urbs,
+			    urblist_entry) {
+		ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
+		dbg("%s: urb submitted", __func__);
+		if (ret_val) {
+			dev_err(&mos_parport->serial->dev->dev,
+				"usb_submit_urb() failed: %d", 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;
+	dbg("%s called", __func__);
+	if (unlikely(status))
+		dbg("%s - nonzero urb status received: %d", __func__, status);
+
+	/* remove the urbtracker from the active_urbs list */
+	spin_lock(&urbtrack->mos_parport->listlock);
+	list_del(&urbtrack->urblist_entry);
+	spin_unlock(&urbtrack->mos_parport->listlock);
+	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;
+	dbg("%s called", __func__);
+
+	/* create and initialize the control urb and containing urbtracker */
+	urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC);
+	if (urbtrack == NULL) {
+		dev_err(&usbdev->dev, "out of memory");
+		return -ENOMEM;
+	}
+	kref_get(&mos_parport->ref_count);
+	urbtrack->mos_parport = mos_parport;
+	urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (urbtrack->urb == NULL) {
+		dev_err(&usbdev->dev, "out of urbs");
+		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);
+		dbg("tasklet scheduled");
+		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", __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 messgage 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 */
+	INIT_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 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;
+	dbg("%s called: %2.2x", __func__, d);
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, SPP);
+	write_mos_reg(mos_parport->serial, dummy, 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;
+	dbg("%s called", __func__);
+	if (parport_prologue(pp) < 0)
+		return 0;
+	read_mos_reg(mos_parport->serial, dummy, 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;
+	dbg("%s called: %2.2x", __func__, d);
+	if (parport_prologue(pp) < 0)
+		return;
+	data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
+	write_mos_reg(mos_parport->serial, dummy, DCR, data);
+	mos_parport->shadowDCR = data;
+	parport_epilogue(pp);
+}
+
+static unsigned char parport_mos7715_read_control(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	__u8 dcr;
+	dbg("%s called", __func__);
+	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;
+	dbg("%s called", __func__);
+	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, 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 = pp->private_data;
+	dbg("%s called", __func__);
+	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)
+{
+	dbg("%s called", __func__);
+}
+static void parport_mos7715_disable_irq(struct parport *pp)
+{
+	dbg("%s called", __func__);
+}
+
+static void parport_mos7715_data_forward(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	dbg("%s called", __func__);
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, PS2);
+	mos_parport->shadowDCR &=  ~0x20;
+	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
+	parport_epilogue(pp);
+}
+
+static void parport_mos7715_data_reverse(struct parport *pp)
+{
+	struct mos7715_parport *mos_parport = pp->private_data;
+	dbg("%s called", __func__);
+	if (parport_prologue(pp) < 0)
+		return;
+	mos7715_change_mode(mos_parport, PS2);
+	mos_parport->shadowDCR |= 0x20;
+	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
+	parport_epilogue(pp);
+}
+
+static void parport_mos7715_init_state(struct pardevice *dev,
+				       struct parport_state *s)
+{
+	dbg("%s called", __func__);
+	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;
+	dbg("%s called", __func__);
+	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;
+	dbg("%s called", __func__);
+	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, DCR, mos_parport->shadowDCR);
+	write_parport_reg_nonblock(mos_parport, 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;
+	dbg("%s called: %u chars", __func__, (unsigned int)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", 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 == NULL) {
+		dbg("mos7715_parport_init: kzalloc failed");
+		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, PP_REG, (__u8)0x80);
+	write_mos_reg(mos_parport->serial, dummy, PP_REG, (__u8)0x00);
+
+	/* initialize device registers */
+	mos_parport->shadowDCR = DCR_INIT_VAL;
+	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
+	mos_parport->shadowECR = ECR_INIT_VAL;
+	write_mos_reg(mos_parport->serial, dummy, 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;
+	__u8 *data;
+	__u8 sp1;
+	__u8 sp2;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __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)) {
+		dbg("Wrong data !!!");
+		return;
+	}
+
+	sp1 = data[3];
+	sp2 = data[2];
+
+	if ((sp1 | sp2) & 0x01) {
+		/* No Interrupt Pending in both the ports */
+		dbg("No Interrupt !!!");
+	} else {
+		switch (sp1 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dbg("Serial Port 1: Receiver status error or address "
+			    "bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dbg("Serial Port 1: Receiver time out");
+			break;
+		case SERIAL_IIR_MS:
+			/* dbg("Serial Port 1: Modem status change"); */
+			break;
+		}
+
+		switch (sp2 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dbg("Serial Port 2: Receiver status error or address "
+			    "bit detected in 9-bit mode");
+			break;
+		case SERIAL_IIR_CTI:
+			dbg("Serial Port 2: Receiver time out");
+			break;
+		case SERIAL_IIR_MS:
+			/* dbg("Serial Port 2: Modem status change"); */
+			break;
+		}
+	}
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->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;
+	__u8 *data;
+	__u8 iir;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -ENODEV:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __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)) {
+		dbg("Wrong data !!!");
+		return;
+	}
+
+	iir = data[0];
+	if (!(iir & 0x01)) {	/* serial port interrupt pending */
+		switch (iir & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dbg("Serial Port: Receiver status error or address "
+			    "bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dbg("Serial Port: Receiver time out");
+			break;
+		case SERIAL_IIR_MS:
+			/* dbg("Serial Port: Modem status change"); */
+			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(&urb->dev->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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	if (status) {
+		dbg("nonzero read bulk status received: %d", status);
+		return;
+	}
+
+	port = urb->context;
+
+	dbg("Entering...%s", __func__);
+
+	data = urb->transfer_buffer;
+
+	tty = tty_port_tty_get(&port->port);
+	if (tty && urb->actual_length) {
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (retval)
+			dbg("usb_submit_urb(read bulk) failed, retval = %d",
+			    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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	if (status) {
+		dbg("nonzero write bulk status received:%d", status);
+		return;
+	}
+
+	mos7720_port = urb->context;
+	if (!mos7720_port) {
+		dbg("NULL mos7720_port pointer");
+		return ;
+	}
+
+	tty = tty_port_tty_get(&mos7720_port->port->port);
+
+	if (tty && mos7720_port->open)
+		tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+/*
+ * mos77xx_probe
+ *	this function installs the appropriate read interrupt endpoint callback
+ *	depending on whether the device is a 7720 or 7715, thus avoiding costly
+ *	run-time checks in the high-frequency callback routine itself.
+ */
+static int mos77xx_probe(struct usb_serial *serial,
+			 const struct usb_device_id *id)
+{
+	if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
+		moschip7720_2port_driver.read_int_callback =
+			mos7715_interrupt_callback;
+	else
+		moschip7720_2port_driver.read_int_callback =
+			mos7720_interrupt_callback;
+
+	return 0;
+}
+
+static int mos77xx_calc_num_ports(struct usb_serial *serial)
+{
+	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+	if (product == MOSCHIP_DEVICE_ID_7715)
+		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 == NULL) {
+			dev_err(&port->dev, "No more urbs???\n");
+			continue;
+		}
+
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			dev_err(&port->dev,
+				"%s-out of memory for urb buffers.\n",
+				__func__);
+			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 : THR/RHR
+	  * 1 : IER
+	  * 2 : FCR
+	  * 3 : LCR
+	  * 4 : MCR
+	  * 5 : LSR
+	  * 6 : MSR
+	  * 7 : SPR
+	  *
+	  * 0x08 : SP1/2 Control Reg
+	  */
+	port_number = port->number - port->serial->minor;
+	read_mos_reg(serial, port_number, LSR, &data);
+
+	dbg("SS::%p LSR:%x", mos7720_port, data);
+
+	dbg("Check:Sending Command ..........");
+
+	write_mos_reg(serial, dummy, SP1_REG, 0x02);
+	write_mos_reg(serial, dummy, SP2_REG, 0x02);
+
+	write_mos_reg(serial, port_number, IER, 0x00);
+	write_mos_reg(serial, port_number, FCR, 0x00);
+
+	write_mos_reg(serial, port_number, FCR, 0xcf);
+	mos7720_port->shadowLCR = 0x03;
+	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
+
+	write_mos_reg(serial, port_number, SP_CONTROL_REG, 0x00);
+	read_mos_reg(serial, dummy, SP_CONTROL_REG, &data);
+	data = data | (port->number - port->serial->minor + 1);
+	write_mos_reg(serial, dummy, SP_CONTROL_REG, data);
+	mos7720_port->shadowLCR = 0x83;
+	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, THR, 0x0c);
+	write_mos_reg(serial, port_number, IER, 0x00);
+	mos7720_port->shadowLCR = 0x03;
+	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, 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 icount structure */
+	memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount));
+
+	/* 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;
+
+	dbg("%s:entering ...........", __func__);
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("%s:leaving ...........", __func__);
+		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;
+	}
+	dbg("%s - returns %d", __func__, chars);
+	return chars;
+}
+
+static void mos7720_close(struct usb_serial_port *port)
+{
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+	int j;
+
+	dbg("mos7720_close:entering...");
+
+	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   */
+	dbg("Shutdown bulk write");
+	usb_kill_urb(port->write_urb);
+	dbg("Shutdown bulk read");
+	usb_kill_urb(port->read_urb);
+
+	mutex_lock(&serial->disc_mutex);
+	/* these commands must not be issued if the device has
+	 * been disconnected */
+	if (!serial->disconnected) {
+		write_mos_reg(serial, port->number - port->serial->minor,
+			      MCR, 0x00);
+		write_mos_reg(serial, port->number - port->serial->minor,
+			      IER, 0x00);
+	}
+	mutex_unlock(&serial->disc_mutex);
+	mos7720_port->open = 0;
+
+	dbg("Leaving %s", __func__);
+}
+
+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;
+
+	dbg("Entering %s", __func__);
+
+	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->number - port->serial->minor,
+		      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;
+
+	dbg("%s:entering ...........", __func__);
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("%s:leaving ...........", __func__);
+		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;
+	}
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s:entering ...........", __func__);
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("mos7720_port is 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];
+			dbg("URB:%d", i);
+			break;
+		}
+	}
+
+	if (urb == NULL) {
+		dbg("%s - no more free urbs", __func__);
+		goto exit;
+	}
+
+	if (urb->transfer_buffer == NULL) {
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_KERNEL);
+		if (urb->transfer_buffer == NULL) {
+			dev_err_console(port, "%s no more kernel memory...\n",
+				__func__);
+			goto exit;
+		}
+	}
+	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+	memcpy(urb->transfer_buffer, current_position, transfer_size);
+	usb_serial_debug_data(debug, &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;
+
+	dbg("%s- port %d", __func__, port->number);
+
+	mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dbg("port not opened");
+		return;
+	}
+
+	dbg("%s: Entering ..........", __func__);
+
+	/* 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 (tty->termios->c_cflag & CRTSCTS) {
+		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
+		write_mos_reg(port->serial, port->number - port->serial->minor,
+			      MCR, mos7720_port->shadowMCR);
+		if (status != 0)
+			return;
+	}
+}
+
+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) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s: Entering ..........", __func__);
+
+	/* 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 (tty->termios->c_cflag & CRTSCTS) {
+		mos7720_port->shadowMCR |= UART_MCR_RTS;
+		write_mos_reg(port->serial, port->number - port->serial->minor,
+			      MCR, mos7720_port->shadowMCR);
+		if (status != 0)
+			return;
+	}
+}
+
+/* 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
+	 ***********************************************/
+	dbg("Sending Setting Commands ..........");
+	port_number = port->number - port->serial->minor;
+
+	write_mos_reg(serial, port_number, IER, 0x00);
+	write_mos_reg(serial, port_number, FCR, 0x00);
+	write_mos_reg(serial, port_number, FCR, 0xcf);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
+	write_mos_reg(serial, dummy, 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 = SP1_REG;
+	else
+		sp_reg = SP2_REG;
+	write_mos_reg(serial, dummy, sp_reg, baud * 0x10);
+	write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x03);
+	mos7720_port->shadowMCR = 0x2b;
+	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
+
+	/***********************************************
+	 *              Set DLL/DLM
+	 ***********************************************/
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
+	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
+	write_mos_reg(serial, port_number, DLL, 0x01);
+	write_mos_reg(serial, port_number, DLM, 0x00);
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+	write_mos_reg(serial, port_number, 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 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(int baudrate, int *divisor)
+{
+	int i;
+	__u16 custom;
+	__u16 round1;
+	__u16 round;
+
+
+	dbg("%s - %d", __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;
+
+		dbg("Baud %d = %d", baudrate, custom);
+		return 0;
+	}
+
+	dbg("Baud calculation Failed...");
+	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;
+
+	dbg("%s: Entering ..........", __func__);
+
+	number = port->number - port->serial->minor;
+	dbg("%s - port = %d, baud = %d", __func__, port->number, baudrate);
+
+	/* Calculate the Divisor */
+	status = calc_baud_rate_divisor(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, LCR, mos7720_port->shadowLCR);
+
+	/* Write the divisor */
+	write_mos_reg(serial, number, DLL, (__u8)(divisor & 0xff));
+	write_mos_reg(serial, number, DLM, (__u8)((divisor & 0xff00) >> 8));
+
+	/* Disable access to divisor latch */
+	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+	write_mos_reg(serial, number, 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;
+	unsigned iflag;
+	__u8 mask = 0xff;
+	__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->number - port->serial->minor;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (!mos7720_port->open) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s: Entering ..........", __func__);
+
+	lData = UART_LCR_WLEN8;
+	lStop = 0x00;	/* 1 stop bit */
+	lParity = 0x00;	/* No parity */
+
+	cflag = tty->termios->c_cflag;
+	iflag = tty->termios->c_iflag;
+
+	/* Change the number of bits */
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = UART_LCR_WLEN5;
+		mask = 0x1f;
+		break;
+
+	case CS6:
+		lData = UART_LCR_WLEN6;
+		mask = 0x3f;
+		break;
+
+	case CS7:
+		lData = UART_LCR_WLEN7;
+		mask = 0x7f;
+		break;
+	default:
+	case CS8:
+		lData = UART_LCR_WLEN8;
+		break;
+	}
+
+	/* Change the Parity bit */
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			lParity = UART_LCR_PARITY;
+			dbg("%s - parity = odd", __func__);
+		} else {
+			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
+			dbg("%s - parity = even", __func__);
+		}
+
+	} else {
+		dbg("%s - parity = none", __func__);
+	}
+
+	if (cflag & CMSPAR)
+		lParity = lParity | 0x20;
+
+	/* Change the Stop bit */
+	if (cflag & CSTOPB) {
+		lStop = UART_LCR_STOP;
+		dbg("%s - stop bits = 2", __func__);
+	} else {
+		lStop = 0x00;
+		dbg("%s - stop bits = 1", __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, IER, 0x00);
+	write_mos_reg(serial, port_number, FCR, 0x00);
+	write_mos_reg(serial, port_number, FCR, 0xcf);
+
+	/* Send the updated LCR value to the mos7720 */
+	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
+	mos7720_port->shadowMCR = 0x0b;
+	write_mos_reg(serial, port_number, 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, SP_CONTROL_REG, 0x01);
+		else
+			write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x02);
+
+	} else
+		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
+
+	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (!baud) {
+		/* pick a default, any default... */
+		dbg("Picked default baud...");
+		baud = 9600;
+	}
+
+	if (baud >= 230400) {
+		set_higher_rates(mos7720_port, baud);
+		/* Enable Interrupts */
+		write_mos_reg(serial, port_number, IER, 0x0c);
+		return;
+	}
+
+	dbg("%s - baud rate = %d", __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, IER, 0x0c);
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (status)
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    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;
+	unsigned int cflag;
+	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 (!mos7720_port->open) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s\n", "setting termios - ASPIRE");
+
+	cflag = tty->termios->c_cflag;
+
+	dbg("%s - cflag %08x iflag %08x", __func__,
+	    tty->termios->c_cflag,
+	    RELEVANT_IFLAG(tty->termios->c_iflag));
+
+	dbg("%s - old cflag %08x old iflag %08x", __func__,
+	    old_termios->c_cflag,
+	    RELEVANT_IFLAG(old_termios->c_iflag));
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* 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_ATOMIC);
+		if (status)
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    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->number - port->serial->minor;
+	int count;
+
+	count = mos7720_chars_in_buffer(tty);
+	if (count == 0) {
+		read_mos_reg(port->serial, port_number, LSR, &data);
+		if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
+					== (UART_LSR_TEMT | UART_LSR_THRE)) {
+			dbg("%s -- Empty", __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 ;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 */
+
+	dbg("%s -- %x", __func__, result);
+
+	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 ;
+	dbg("%s - port %d", __func__, port->number);
+	dbg("he was at tiocmset");
+
+	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->number - port->serial->minor,
+		      MCR, mos7720_port->shadowMCR);
+
+	return 0;
+}
+
+static int mos7720_get_icount(struct tty_struct *tty,
+				struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7720_port;
+	struct async_icount cnow;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	cnow = mos7720_port->icount;
+
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
+		port->number, icount->rx, icount->tx);
+	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->number - port->serial->minor,
+		      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;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= mos7720_port->port->serial->minor;
+	tmp.port		= mos7720_port->port->number;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	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;
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCSERGETLSR:
+		dbg("%s (%d) TIOCSERGETLSR", __func__,  port->number);
+		return get_lsr_info(tty, mos7720_port,
+					(unsigned int __user *)arg);
+
+	/* FIXME: These should be using the mode methods */
+	case TIOCMBIS:
+	case TIOCMBIC:
+		dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET",
+					__func__, port->number);
+		return set_modem_info(mos7720_port, cmd,
+				      (unsigned int __user *)arg);
+
+	case TIOCGSERIAL:
+		dbg("%s (%d) TIOCGSERIAL", __func__,  port->number);
+		return get_serial_info(mos7720_port,
+				       (struct serial_struct __user *)arg);
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		cprev = mos7720_port->icount;
+		while (1) {
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+			cnow = mos7720_port->icount;
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+		/* NOTREACHED */
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int mos7720_startup(struct usb_serial *serial)
+{
+	struct moschip_port *mos7720_port;
+	struct usb_device *dev;
+	int i;
+	char data;
+	u16 product;
+	int ret_val;
+
+	dbg("%s: Entering ..........", __func__);
+
+	if (!serial) {
+		dbg("Invalid Handler");
+		return -ENODEV;
+	}
+
+	product = le16_to_cpu(serial->dev->descriptor.idProduct);
+	dev = serial->dev;
+
+	/*
+	 * The 7715 uses the first bulk in/out endpoint pair for the parallel
+	 * port, and the second for the serial port.  Because the usbserial core
+	 * assumes both pairs are serial ports, we must engage in a bit of
+	 * subterfuge and swap the pointers for ports 0 and 1 in order to make
+	 * port 0 point to the serial port.  However, both moschip devices use a
+	 * single interrupt-in endpoint for both ports (as mentioned a little
+	 * further down), and this endpoint was assigned to port 0.  So after
+	 * the swap, we must copy the interrupt endpoint elements from port 1
+	 * (as newly assigned) to port 0, and null out port 1 pointers.
+	 */
+	if (product == MOSCHIP_DEVICE_ID_7715) {
+		struct usb_serial_port *tmp = serial->port[0];
+		serial->port[0] = serial->port[1];
+		serial->port[1] = tmp;
+		serial->port[0]->interrupt_in_urb = tmp->interrupt_in_urb;
+		serial->port[0]->interrupt_in_buffer = tmp->interrupt_in_buffer;
+		serial->port[0]->interrupt_in_endpointAddress =
+			tmp->interrupt_in_endpointAddress;
+		serial->port[1]->interrupt_in_urb = NULL;
+		serial->port[1]->interrupt_in_buffer = NULL;
+	}
+
+
+	/* set up serial port private structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
+		if (mos7720_port == NULL) {
+			dev_err(&dev->dev, "%s - Out of memory\n", __func__);
+			return -ENOMEM;
+		}
+
+		/* Initialize all port interrupt end point to port 0 int
+		 * endpoint.  Our device has only one interrupt endpoint
+		 * common to all ports */
+		serial->port[i]->interrupt_in_endpointAddress =
+				serial->port[0]->interrupt_in_endpointAddress;
+
+		mos7720_port->port = serial->port[i];
+		usb_set_serial_port_data(serial->port[i], mos7720_port);
+
+		dbg("port number is %d", serial->port[i]->number);
+		dbg("serial number is %d", serial->minor);
+	}
+
+
+	/* setting configuration feature to one */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
+
+	/* start the interrupt urb */
+	ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+	if (ret_val)
+		dev_err(&dev->dev,
+			"%s - Error %d submitting control urb\n",
+			__func__, ret_val);
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+	if (product == MOSCHIP_DEVICE_ID_7715) {
+		ret_val = mos7715_parport_init(serial);
+		if (ret_val < 0)
+			return ret_val;
+	}
+#endif
+	/* LSR For Port 1 */
+	read_mos_reg(serial, 0, LSR, &data);
+	dbg("LSR:%x", data);
+
+	return 0;
+}
+
+static void mos7720_release(struct usb_serial *serial)
+{
+	int i;
+
+#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);
+
+		kref_put(&mos_parport->ref_count, destroy_mos_parport);
+	}
+#endif
+	/* free private structure allocated for serial port */
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+static struct usb_driver usb_driver = {
+	.name =		"moschip7720",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	moschip_port_id_table,
+};
+
+static struct usb_serial_driver moschip7720_2port_driver = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"moschip7720",
+	},
+	.description		= "Moschip 2 port adapter",
+	.id_table		= moschip_port_id_table,
+	.calc_num_ports		= mos77xx_calc_num_ports,
+	.open			= mos7720_open,
+	.close			= mos7720_close,
+	.throttle		= mos7720_throttle,
+	.unthrottle		= mos7720_unthrottle,
+	.probe			= mos77xx_probe,
+	.attach			= mos7720_startup,
+	.release		= mos7720_release,
+	.ioctl			= mos7720_ioctl,
+	.tiocmget		= mos7720_tiocmget,
+	.tiocmset		= mos7720_tiocmset,
+	.get_icount		= mos7720_get_icount,
+	.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	= NULL  /* dynamically assigned in probe() */
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&moschip7720_2port_driver, NULL
+};
+
+module_usb_serial_driver(usb_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7840.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7840.c
new file mode 100644
index 0000000..947b866
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/mos7840.c
@@ -0,0 +1,2724 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 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/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/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "1.3.2"
+#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 WAIT_FOR_EVER   (HZ * 0)	/* timeout urb is wait for ever */
+#define MOS_WDR_TIMEOUT (HZ * 5)	/* 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
+/* 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 moschip_port_id_table, and in
+ * moschip_id_table_combined
+ */
+#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  */
+
+
+enum mos7840_flag {
+	MOS7840_FLAG_CTRL_BUSY,
+};
+
+static const struct usb_device_id moschip_port_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_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 */
+};
+
+static const struct usb_device_id moschip_id_table_combined[] = {
+	{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_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, moschip_id_table_combined);
+
+/* 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 *write_urb;	/* write URB for this port */
+	struct urb *read_urb;	/* read URB for this port */
+	__u8 shadowLCR;		/* last LCR value received */
+	__u8 shadowMCR;		/* last MCR value received */
+	char open;
+	char open_ports;
+	wait_queue_head_t wait_chase;	/* for handling sleeping while waiting for chase to finish */
+	int delta_msr_cond;
+	struct async_icount icount;
+	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;
+
+	unsigned long flags;
+};
+
+
+static bool debug;
+
+/*
+ * 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;
+	dbg("mos7840_set_reg_sync offset is %x, value %x", 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);
+	*val = buf[0];
+	dbg("mos7840_get_reg_sync offset is %x, return val %x", reg, *val);
+
+	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->number -
+				(__u16) (port->serial->minor)) + 1) << 8;
+		dbg("mos7840_set_uart_reg application number is %x", val);
+	} else {
+		if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+			val |= (((__u16) port->number -
+			      (__u16) (port->serial->minor)) + 1) << 8;
+			dbg("mos7840_set_uart_reg application number is %x",
+			    val);
+		} else {
+			val |=
+			    (((__u16) port->number -
+			      (__u16) (port->serial->minor)) + 2) << 8;
+			dbg("mos7840_set_uart_reg application number is %x",
+			    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;
+
+	/* dbg("application number is %4x",
+	    (((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); */
+	/* Wval  is same as application number */
+	if (port->serial->num_ports == 4) {
+		Wval =
+		    (((__u16) port->number - (__u16) (port->serial->minor)) +
+		     1) << 8;
+		dbg("mos7840_get_uart_reg application number is %x", Wval);
+	} else {
+		if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+			Wval = (((__u16) port->number -
+			      (__u16) (port->serial->minor)) + 1) << 8;
+			dbg("mos7840_get_uart_reg application number is %x",
+			    Wval);
+		} else {
+			Wval = (((__u16) port->number -
+			      (__u16) (port->serial->minor)) + 2) << 8;
+			dbg("mos7840_get_uart_reg application number is %x",
+			    Wval);
+		}
+	}
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+			      MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
+			      MOS_WDR_TIMEOUT);
+	*val = buf[0];
+
+	kfree(buf);
+	return ret;
+}
+
+static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
+{
+
+	dbg("***************************************");
+	dbg("SpRegOffset is %2x", mos7840_port->SpRegOffset);
+	dbg("ControlRegOffset is %2x", mos7840_port->ControlRegOffset);
+	dbg("DCRRegOffset is %2x", mos7840_port->DcrRegOffset);
+	dbg("***************************************");
+
+}
+
+/************************************************************************/
+/************************************************************************/
+/*             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;
+	icount = &mos7840_port->icount;
+	if (new_msr &
+	    (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
+	     MOS_MSR_DELTA_CD)) {
+		icount = &mos7840_port->icount;
+
+		/* update input line counters */
+		if (new_msr & MOS_MSR_DELTA_CTS) {
+			icount->cts++;
+			smp_wmb();
+		}
+		if (new_msr & MOS_MSR_DELTA_DSR) {
+			icount->dsr++;
+			smp_wmb();
+		}
+		if (new_msr & MOS_MSR_DELTA_CD) {
+			icount->dcd++;
+			smp_wmb();
+		}
+		if (new_msr & MOS_MSR_DELTA_RI) {
+			icount->rng++;
+			smp_wmb();
+		}
+
+		mos7840_port->delta_msr_cond = 1;
+		wake_up_interruptible(&port->port->delta_msr_wait);
+	}
+}
+
+static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+{
+	struct async_icount *icount;
+
+	dbg("%s - %02x", __func__, new_lsr);
+
+	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->icount;
+	if (new_lsr & SERIAL_LSR_BI) {
+		icount->brk++;
+		smp_wmb();
+	}
+	if (new_lsr & SERIAL_LSR_OE) {
+		icount->overrun++;
+		smp_wmb();
+	}
+	if (new_lsr & SERIAL_LSR_PE) {
+		icount->parity++;
+		smp_wmb();
+	}
+	if (new_lsr & SERIAL_LSR_FE) {
+		icount->frame++;
+		smp_wmb();
+	}
+}
+
+/************************************************************************/
+/************************************************************************/
+/*            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;
+	__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 */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		goto out;
+	default:
+		dbg("%s - nonzero urb status received: %d", __func__,
+		    status);
+		goto out;
+	}
+
+	dbg("%s urb buffer size is %d", __func__, urb->actual_length);
+	dbg("%s mos7840_port->MsrLsr is %d port %d", __func__,
+	    mos7840_port->MsrLsr, mos7840_port->port_num);
+	data = urb->transfer_buffer;
+	regval = (__u8) data[0];
+	dbg("%s data is %x", __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;
+}
+
+/*****************************************************************************
+ * 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;
+
+	dbg("%s", " : Entering");
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __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 && length > 5) {
+		dbg("%s", "Wrong data !!!");
+		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]->number -
+		      (__u16) (serial->minor)) + 1) << 8;
+		if (mos7840_port->open) {
+			if (sp[i] & 0x01) {
+				dbg("SP%d No Interrupt !!!", i);
+			} else {
+				switch (sp[i] & 0x0f) {
+				case SERIAL_IIR_RLS:
+					dbg("Serial Port %d: Receiver status error or ", i);
+					dbg("address bit detected in 9-bit mode");
+					mos7840_port->MsrLsr = 1;
+					wreg = LINE_STATUS_REGISTER;
+					break;
+				case SERIAL_IIR_MS:
+					dbg("Serial Port %d: Modem status change", 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) {
+		dbg("%s - port == NULL", function);
+		return -1;
+	}
+	if (!port->serial) {
+		dbg("%s - port->serial == NULL", 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) {
+		dbg("%s - serial == NULL", function);
+		return -1;
+	}
+	if (!serial->type) {
+		dbg("%s - serial->type == NULL!", 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;
+	struct tty_struct *tty;
+	int status = urb->status;
+
+	mos7840_port = urb->context;
+	if (!mos7840_port) {
+		dbg("%s", "NULL mos7840_port pointer");
+		return;
+	}
+
+	if (status) {
+		dbg("nonzero read bulk status received: %d", status);
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	port = (struct usb_serial_port *)mos7840_port->port;
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial) {
+		dbg("%s", "Bad serial pointer");
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+	dbg("%s", "Entering... ");
+
+	data = urb->transfer_buffer;
+
+	dbg("%s", "Entering ...........");
+
+	if (urb->actual_length) {
+		tty = tty_port_tty_get(&mos7840_port->port->port);
+		if (tty) {
+			tty_insert_flip_string(tty, data, urb->actual_length);
+			dbg(" %s ", data);
+			tty_flip_buffer_push(tty);
+			tty_kref_put(tty);
+		}
+		mos7840_port->icount.rx += urb->actual_length;
+		smp_wmb();
+		dbg("mos7840_port->icount.rx is %d:",
+		    mos7840_port->icount.rx);
+	}
+
+	if (!mos7840_port->read_urb) {
+		dbg("%s", "URB KILLED !!!");
+		mos7840_port->read_urb_busy = false;
+		return;
+	}
+
+
+	mos7840_port->read_urb_busy = true;
+	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+	if (retval) {
+		dbg("usb_submit_urb(read bulk) failed, retval = %d", 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 tty_struct *tty;
+	int status = urb->status;
+	int i;
+
+	mos7840_port = urb->context;
+	spin_lock(&mos7840_port->pool_lock);
+	for (i = 0; i < NUM_URBS; i++) {
+		if (urb == mos7840_port->write_urb_pool[i]) {
+			mos7840_port->busy[i] = 0;
+			break;
+		}
+	}
+	spin_unlock(&mos7840_port->pool_lock);
+
+	if (status) {
+		dbg("nonzero write bulk status received:%d", status);
+		return;
+	}
+
+	if (mos7840_port_paranoia_check(mos7840_port->port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		return;
+	}
+
+	dbg("%s", "Entering .........");
+
+	tty = tty_port_tty_get(&mos7840_port->port->port);
+	if (tty && mos7840_port->open)
+		tty_wakeup(tty);
+	tty_kref_put(tty);
+
+}
+
+/************************************************************************/
+/*       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       */
+/************************************************************************/
+#ifdef MCSSerialProbe
+static int mos7840_serial_probe(struct usb_serial *serial,
+				const struct usb_device_id *id)
+{
+
+	/*need to implement the mode_reg reading and updating\
+	   structures usb_serial_ device_type\
+	   (i.e num_ports, num_bulkin,bulkout etc) */
+	/* Also we can update the changes  attach */
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * 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;
+
+	dbg ("%s enter", __func__);
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		return -ENODEV;
+	}
+
+	serial = port->serial;
+
+	if (mos7840_serial_paranoia_check(serial, __func__)) {
+		dbg("%s", "Serial Paranoia failed");
+		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 == NULL) {
+			dev_err(&port->dev, "No more urbs???\n");
+			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;
+			dev_err(&port->dev,
+				"%s-out of memory for urb buffers.\n",
+				__func__);
+			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) {
+		dbg("Reading Spreg failed");
+		goto err;
+	}
+	Data |= 0x80;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+	if (status < 0) {
+		dbg("writing Spreg failed");
+		goto err;
+	}
+
+	Data &= ~0x80;
+	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+	if (status < 0) {
+		dbg("writing Spreg failed");
+		goto err;
+	}
+	/* End of block to be checked */
+
+	Data = 0x0;
+	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
+									&Data);
+	if (status < 0) {
+		dbg("Reading Controlreg failed");
+		goto err;
+	}
+	Data |= 0x08;		/* Driver done bit */
+	Data |= 0x20;		/* rx_disable */
+	status = mos7840_set_reg_sync(port,
+				mos7840_port->ControlRegOffset, Data);
+	if (status < 0) {
+		dbg("writing Controlreg failed");
+		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) {
+		dbg("disabling interrupts failed");
+		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) {
+		dbg("Writing FIFO_CONTROL_REGISTER  failed");
+		goto err;
+	}
+
+	Data = 0xcf;
+	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+	if (status < 0) {
+		dbg("Writing FIFO_CONTROL_REGISTER  failed");
+		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) {
+		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               *
+			 * will continue as long as mos7840 is connected  */
+
+			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.)   */
+
+	dbg("port number is %d", port->number);
+	dbg("serial number is %d", port->serial->minor);
+	dbg("Bulkin endpoint is %d", port->bulk_in_endpointAddress);
+	dbg("BulkOut endpoint is %d", port->bulk_out_endpointAddress);
+	dbg("Interrupt endpoint is %d", port->interrupt_in_endpointAddress);
+	dbg("port's number in the device is %d", mos7840_port->port_num);
+	mos7840_port->read_urb = port->read_urb;
+
+	/* set up our bulk in urb */
+	if ((serial->num_ports == 2)
+		&& ((((__u16)port->number -
+			(__u16)(port->serial->minor)) % 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);
+	}
+
+	dbg("mos7840_open: bulkin endpoint is %d",
+	    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 wait queues */
+	init_waitqueue_head(&mos7840_port->wait_chase);
+
+	/* initialize our icount structure */
+	memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount));
+
+	/* 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); */
+	mos7840_port->icount.tx = 0;
+	mos7840_port->icount.rx = 0;
+
+	dbg("usb_serial serial:%p       mos7840_port:%p\n      usb_serial_port port:%p",
+				serial, mos7840_port, port);
+
+	dbg ("%s leave", __func__);
+
+	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;
+
+	dbg("%s", " mos7840_chars_in_buffer:entering ...........");
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		return 0;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL) {
+		dbg("%s", "mos7840_break:leaving ...........");
+		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);
+	dbg("%s - returns %d", __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;
+
+	dbg("%s", "mos7840_close:entering...");
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		return;
+	}
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial) {
+		dbg("%s", "Serial Paranoia failed");
+		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]) {
+			if (mos7840_port->write_urb_pool[j]->transfer_buffer)
+				kfree(mos7840_port->write_urb_pool[j]->
+				      transfer_buffer);
+
+			usb_free_urb(mos7840_port->write_urb_pool[j]);
+		}
+	}
+
+	/* While closing port, shutdown all bulk read, write  *
+	 * and interrupt read if they exists                  */
+	if (serial->dev) {
+		if (mos7840_port->write_urb) {
+			dbg("%s", "Shutdown bulk write");
+			usb_kill_urb(mos7840_port->write_urb);
+		}
+		if (mos7840_port->read_urb) {
+			dbg("%s", "Shutdown bulk read");
+			usb_kill_urb(mos7840_port->read_urb);
+			mos7840_port->read_urb_busy = false;
+		}
+		if ((&mos7840_port->control_urb)) {
+			dbg("%s", "Shutdown control read");
+			/*/      usb_kill_urb (mos7840_port->control_urb); */
+		}
+	}
+/*      if(mos7840_port->ctrl_buf != NULL) */
+/*              kfree(mos7840_port->ctrl_buf); */
+	port0->open_ports--;
+	dbg("mos7840_num_open_ports in close%d:in port%d",
+	    port0->open_ports, port->number);
+	if (port0->open_ports == 0) {
+		if (serial->port[0]->interrupt_in_urb) {
+			dbg("%s", "Shutdown interrupt_in_urb");
+			usb_kill_urb(serial->port[0]->interrupt_in_urb);
+		}
+	}
+
+	if (mos7840_port->write_urb) {
+		/* if this urb had a transfer buffer already (old tx) free it */
+		if (mos7840_port->write_urb->transfer_buffer != NULL)
+			kfree(mos7840_port->write_urb->transfer_buffer);
+		usb_free_urb(mos7840_port->write_urb);
+	}
+
+	Data = 0x0;
+	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+	Data = 0x00;
+	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+	mos7840_port->open = 0;
+
+	dbg("%s", "Leaving ............");
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_chase_response
+ *
+ *	This function will block the close until one of the following:
+ *		1. Response to our Chase comes from mos7840
+ *		2. A timeout of 10 seconds without activity has expired
+ *		   (1K of mos7840 data @ 2400 baud ==> 4 sec to empty)
+ *
+ ************************************************************************/
+
+static void mos7840_block_until_chase_response(struct tty_struct *tty,
+					struct moschip_port *mos7840_port)
+{
+	int timeout = 1 * HZ;
+	int wait = 10;
+	int count;
+
+	while (1) {
+		count = mos7840_chars_in_buffer(tty);
+
+		/* Check for Buffer status */
+		if (count <= 0)
+			return;
+
+		/* Block the thread for a while */
+		interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+					       timeout);
+		/* No activity.. count down section */
+		wait--;
+		if (wait == 0) {
+			dbg("%s - TIMEOUT", __func__);
+			return;
+		} else {
+			/* Reset timeout value back to seconds */
+			wait = 10;
+		}
+	}
+
+}
+
+/*****************************************************************************
+ * 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;
+
+	dbg("%s", "Entering ...........");
+	dbg("mos7840_break: Start");
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		return;
+	}
+
+	serial = mos7840_get_usb_serial(port, __func__);
+	if (!serial) {
+		dbg("%s", "Serial Paranoia failed");
+		return;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (serial->dev)
+		/* flush and block until tx is empty */
+		mos7840_block_until_chase_response(tty, mos7840_port);
+
+	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;
+	dbg("mcs7840_break mos7840_port->shadowLCR is %x",
+	    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;
+
+	dbg("%s", " mos7840_write_room:entering ...........");
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		dbg("%s", " mos7840_write_room:leaving ...........");
+		return -1;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL) {
+		dbg("%s", "mos7840_break:leaving ...........");
+		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;
+	dbg("%s - returns %d", __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;
+	dbg("%s", "entering ...........");
+	/* dbg("mos7840_write: mos7840_port->shadowLCR is %x",
+					mos7840_port->shadowLCR); */
+
+#ifdef NOTMOS7840
+	Data = 0x00;
+	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+	mos7840_port->shadowLCR = Data;
+	dbg("mos7840_write: LINE_CONTROL_REGISTER is %x", Data);
+	dbg("mos7840_write: mos7840_port->shadowLCR is %x",
+	    mos7840_port->shadowLCR);
+
+	/* Data = 0x03; */
+	/* status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); */
+	/* mos7840_port->shadowLCR=Data;//Need to add later */
+
+	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 = 0x00;
+	status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
+	dbg("mos7840_write:DLL value is %x", Data);
+
+	Data = 0x0;
+	status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
+	dbg("mos7840_write:DLM value is %x", Data);
+
+	Data = Data & ~SERIAL_LCR_DLAB;
+	dbg("mos7840_write: mos7840_port->shadowLCR is %x",
+	    mos7840_port->shadowLCR);
+	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+#endif
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Port Paranoia failed");
+		return -1;
+	}
+
+	serial = port->serial;
+	if (mos7840_serial_paranoia_check(serial, __func__)) {
+		dbg("%s", "Serial Paranoia failed");
+		return -1;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+	if (mos7840_port == NULL) {
+		dbg("%s", "mos7840_port is 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];
+			dbg("URB:%d", i);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+
+	if (urb == NULL) {
+		dbg("%s - no more free urbs", __func__);
+		goto exit;
+	}
+
+	if (urb->transfer_buffer == NULL) {
+		urb->transfer_buffer =
+		    kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+
+		if (urb->transfer_buffer == NULL) {
+			dev_err_console(port, "%s no more kernel memory...\n",
+				__func__);
+			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->number -
+			(__u16)(port->serial->minor)) % 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;
+	dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress);
+
+	/* 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;
+	mos7840_port->icount.tx += transfer_size;
+	smp_wmb();
+	dbg("mos7840_port->icount.tx is %d:", mos7840_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__)) {
+		dbg("%s", "Invalid port");
+		return;
+	}
+
+	dbg("- port %d", port->number);
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dbg("%s", "port not opened");
+		return;
+	}
+
+	dbg("%s", "Entering ..........");
+
+	/* 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 (tty->termios->c_cflag & CRTSCTS) {
+		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__)) {
+		dbg("%s", "Invalid port");
+		return;
+	}
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s", "Entering ..........");
+
+	/* 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 (tty->termios->c_cflag & CRTSCTS) {
+		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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (mos7840_port == NULL)
+		return -ENODEV;
+
+	status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+	if (status != 1)
+		return -EIO;
+	status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+	if (status != 1)
+		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);
+
+	dbg("%s - 0x%04X", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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) {
+		dbg("setting MODEM_CONTROL_REGISTER Failed");
+		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(int baudRate, int *divisor,
+					  __u16 *clk_sel_val)
+{
+
+	dbg("%s - %d", __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;
+
+#ifdef NOTMCS7840
+
+	for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) {
+		if (mos7840_divisor_table[i].BaudRate == baudrate) {
+			*divisor = mos7840_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;
+
+		dbg(" Baud %d = %d", baudrate, custom);
+		return 0;
+	}
+
+	dbg("%s", " Baud calculation Failed...");
+	return -1;
+#endif
+}
+
+/*****************************************************************************
+ * 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 = (struct usb_serial_port *)mos7840_port->port;
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		return -1;
+	}
+
+	if (mos7840_serial_paranoia_check(port->serial, __func__)) {
+		dbg("%s", "Invalid Serial");
+		return -1;
+	}
+
+	dbg("%s", "Entering ..........");
+
+	number = mos7840_port->port->number - mos7840_port->port->serial->minor;
+
+	dbg("%s - port = %d, baud = %d", __func__,
+	    mos7840_port->port->number, 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) {
+			dbg("Writing spreg failed in set_serial_baud");
+			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) {
+			dbg("Writing spreg failed in set_serial_baud");
+			return -1;
+		}
+#endif
+
+	}
+
+	if (1) {		/* baudRate <= 115200) */
+		clk_sel_val = 0x0;
+		Data = 0x0;
+		status = mos7840_calc_baud_rate_divisor(baudRate, &divisor,
+						   &clk_sel_val);
+		status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
+								 &Data);
+		if (status < 0) {
+			dbg("reading spreg failed in set_serial_baud");
+			return -1;
+		}
+		Data = (Data & 0x8f) | clk_sel_val;
+		status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
+								Data);
+		if (status < 0) {
+			dbg("Writing spreg failed in set_serial_baud");
+			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);
+		dbg("set_serial_baud Value to write DLL is %x", Data);
+		mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+		Data = (unsigned char)((divisor & 0xff00) >> 8);
+		dbg("set_serial_baud Value to write DLM is %x", 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 = (struct usb_serial_port *)mos7840_port->port;
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		return;
+	}
+
+	if (mos7840_serial_paranoia_check(port->serial, __func__)) {
+		dbg("%s", "Invalid Serial");
+		return;
+	}
+
+	serial = port->serial;
+
+	dbg("%s - port %d", __func__, mos7840_port->port->number);
+
+	if (!mos7840_port->open) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s", "Entering ..........");
+
+	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;
+			dbg("%s - parity = odd", __func__);
+		} else {
+			lParity = LCR_PAR_EVEN;
+			dbg("%s - parity = even", __func__);
+		}
+
+	} else {
+		dbg("%s - parity = none", __func__);
+	}
+
+	if (cflag & CMSPAR)
+		lParity = lParity | 0x20;
+
+	/* Change the Stop bit */
+	if (cflag & CSTOPB) {
+		lStop = LCR_STOP_2;
+		dbg("%s - stop bits = 2", __func__);
+	} else {
+		lStop = LCR_STOP_1;
+		dbg("%s - stop bits = 1", __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);
+
+	dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x",
+	    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... */
+		dbg("%s", "Picked default baud...");
+		baud = 9600;
+	}
+
+	dbg("%s - baud rate = %d", __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 == false) {
+		mos7840_port->read_urb_busy = true;
+		status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+		if (status) {
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    status);
+			mos7840_port->read_urb_busy = false;
+		}
+	}
+	dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x",
+	    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;
+	unsigned int cflag;
+	struct usb_serial *serial;
+	struct moschip_port *mos7840_port;
+	dbg("mos7840_set_termios: START");
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		return;
+	}
+
+	serial = port->serial;
+
+	if (mos7840_serial_paranoia_check(serial, __func__)) {
+		dbg("%s", "Invalid Serial");
+		return;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return;
+
+	if (!mos7840_port->open) {
+		dbg("%s - port not opened", __func__);
+		return;
+	}
+
+	dbg("%s", "setting termios - ");
+
+	cflag = tty->termios->c_cflag;
+
+	dbg("%s - clfag %08x iflag %08x", __func__,
+	    tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag));
+	dbg("%s - old clfag %08x old iflag %08x", __func__,
+	    old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
+	dbg("%s - port %d", __func__, port->number);
+
+	/* change the port settings to the new ones specified */
+
+	mos7840_change_port_settings(tty, mos7840_port, old_termios);
+
+	if (!mos7840_port->read_urb) {
+		dbg("%s", "URB KILLED !!!!!");
+		return;
+	}
+
+	if (mos7840_port->read_urb_busy == false) {
+		mos7840_port->read_urb_busy = true;
+		status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+		if (status) {
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    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) {
+		dbg("%s -- Empty", __func__);
+		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;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type = PORT_16550A;
+	tmp.line = mos7840_port->port->serial->minor;
+	tmp.port = mos7840_port->port->number;
+	tmp.irq = 0;
+	tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	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 mos7840_get_icount(struct tty_struct *tty,
+			struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct moschip_port *mos7840_port;
+	struct async_icount cnow;
+
+	mos7840_port = mos7840_get_port_private(port);
+	cnow = mos7840_port->icount;
+
+	smp_rmb();
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
+		port->number, icount->rx, icount->tx);
+	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;
+
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	if (mos7840_port_paranoia_check(port, __func__)) {
+		dbg("%s", "Invalid port");
+		return -1;
+	}
+
+	mos7840_port = mos7840_get_port_private(port);
+
+	if (mos7840_port == NULL)
+		return -1;
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+		/* return number of bytes available */
+
+	case TIOCSERGETLSR:
+		dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
+		return mos7840_get_lsr_info(tty, argp);
+
+	case TIOCGSERIAL:
+		dbg("%s (%d) TIOCGSERIAL", __func__, port->number);
+		return mos7840_get_serial_info(mos7840_port, argp);
+
+	case TIOCSSERIAL:
+		dbg("%s (%d) TIOCSSERIAL", __func__, port->number);
+		break;
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+		cprev = mos7840_port->icount;
+		while (1) {
+			/* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */
+			mos7840_port->delta_msr_cond = 0;
+			wait_event_interruptible(port->delta_msr_wait,
+						 (port->serial->disconnected ||
+						  mos7840_port->
+						  delta_msr_cond == 1));
+
+			/* see if a signal did it */
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			cnow = mos7840_port->icount;
+			smp_rmb();
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO;	/* no change => error */
+			if (((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))) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+		/* NOTREACHED */
+		break;
+
+	default:
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial)
+{
+	__u16 Data = 0x00;
+	int ret = 0;
+	int mos7840_num_ports;
+
+	ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+		MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &Data,
+		VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
+
+	if ((Data & 0x01) == 0) {
+		mos7840_num_ports = 2;
+		serial->num_bulk_in = 2;
+		serial->num_bulk_out = 2;
+		serial->num_ports = 2;
+	} else {
+		mos7840_num_ports = 4;
+		serial->num_bulk_in = 4;
+		serial->num_bulk_out = 4;
+		serial->num_ports = 4;
+	}
+
+	return mos7840_num_ports;
+}
+
+/****************************************************************************
+ * mos7840_startup
+ ****************************************************************************/
+
+static int mos7840_startup(struct usb_serial *serial)
+{
+	struct moschip_port *mos7840_port;
+	struct usb_device *dev;
+	int i, status;
+
+	__u16 Data;
+	dbg("%s", "mos7840_startup :Entering..........");
+
+	if (!serial) {
+		dbg("%s", "Invalid Handler");
+		return -1;
+	}
+
+	dev = serial->dev;
+
+	dbg("%s", "Entering...");
+	dbg ("mos7840_startup: serial = %p", serial);
+
+	/* we set up the pointers to the endpoints in the mos7840_open *
+	 * function, as the structures aren't created yet.             */
+
+	/* set up port private structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		dbg ("mos7840_startup: configuring port %d............", i);
+		mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
+		if (mos7840_port == NULL) {
+			dev_err(&dev->dev, "%s - Out of memory\n", __func__);
+			status = -ENOMEM;
+			i--; /* don't follow NULL pointer cleaning up */
+			goto error;
+		}
+
+		/* 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 = serial->port[i];
+		mos7840_set_port_private(serial->port[i], 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 = i + 1;
+		dbg ("serial->port[i]->number = %d", serial->port[i]->number);
+		dbg ("serial->port[i]->serial->minor = %d", serial->port[i]->serial->minor);
+		dbg ("mos7840_port->port_num = %d", mos7840_port->port_num);
+		dbg ("serial->minor = %d", serial->minor);
+
+		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(mos7840_port);
+		mos7840_set_port_private(serial->port[i], mos7840_port);
+
+		/* enable rx_disable bit in control register */
+		status = mos7840_get_reg_sync(serial->port[i],
+				 mos7840_port->ControlRegOffset, &Data);
+		if (status < 0) {
+			dbg("Reading ControlReg failed status-0x%x", status);
+			break;
+		} else
+			dbg("ControlReg Reading success val is %x, status%d",
+			    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(serial->port[i],
+					 mos7840_port->ControlRegOffset, Data);
+		if (status < 0) {
+			dbg("Writing ControlReg failed(rx_disable) status-0x%x", status);
+			break;
+		} else
+			dbg("ControlReg Writing success(rx_disable) status%d",
+			    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(serial->port[i],
+			 (__u16) (mos7840_port->DcrRegOffset + 0), Data);
+		if (status < 0) {
+			dbg("Writing DCR0 failed status-0x%x", status);
+			break;
+		} else
+			dbg("DCR0 Writing success status%d", status);
+
+		Data = 0x05;
+		status = mos7840_set_reg_sync(serial->port[i],
+			 (__u16) (mos7840_port->DcrRegOffset + 1), Data);
+		if (status < 0) {
+			dbg("Writing DCR1 failed status-0x%x", status);
+			break;
+		} else
+			dbg("DCR1 Writing success status%d", status);
+
+		Data = 0x24;
+		status = mos7840_set_reg_sync(serial->port[i],
+			 (__u16) (mos7840_port->DcrRegOffset + 2), Data);
+		if (status < 0) {
+			dbg("Writing DCR2 failed status-0x%x", status);
+			break;
+		} else
+			dbg("DCR2 Writing success status%d", status);
+
+		/* write values in clkstart0x0 and clkmulti 0x20 */
+		Data = 0x0;
+		status = mos7840_set_reg_sync(serial->port[i],
+					 CLK_START_VALUE_REGISTER, Data);
+		if (status < 0) {
+			dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x", status);
+			break;
+		} else
+			dbg("CLK_START_VALUE_REGISTER Writing success status%d", status);
+
+		Data = 0x20;
+		status = mos7840_set_reg_sync(serial->port[i],
+					CLK_MULTI_REGISTER, Data);
+		if (status < 0) {
+			dbg("Writing CLK_MULTI_REGISTER failed status-0x%x",
+			    status);
+			goto error;
+		} else
+			dbg("CLK_MULTI_REGISTER Writing success status%d",
+			    status);
+
+		/* write value 0x0 to scratchpad register */
+		Data = 0x00;
+		status = mos7840_set_uart_reg(serial->port[i],
+						SCRATCH_PAD_REGISTER, Data);
+		if (status < 0) {
+			dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x",
+			    status);
+			break;
+		} else
+			dbg("SCRATCH_PAD_REGISTER Writing success status%d",
+			    status);
+
+		/* Zero Length flag register */
+		if ((mos7840_port->port_num != 1)
+		    && (serial->num_ports == 2)) {
+
+			Data = 0xff;
+			status = mos7840_set_reg_sync(serial->port[i],
+				      (__u16) (ZLP_REG1 +
+				      ((__u16)mos7840_port->port_num)), Data);
+			dbg("ZLIP offset %x",
+			    (__u16) (ZLP_REG1 +
+					((__u16) mos7840_port->port_num)));
+			if (status < 0) {
+				dbg("Writing ZLP_REG%d failed status-0x%x",
+				    i + 2, status);
+				break;
+			} else
+				dbg("ZLP_REG%d Writing success status%d",
+				    i + 2, status);
+		} else {
+			Data = 0xff;
+			status = mos7840_set_reg_sync(serial->port[i],
+			      (__u16) (ZLP_REG1 +
+			      ((__u16)mos7840_port->port_num) - 0x1), Data);
+			dbg("ZLIP offset %x",
+			    (__u16) (ZLP_REG1 +
+				     ((__u16) mos7840_port->port_num) - 0x1));
+			if (status < 0) {
+				dbg("Writing ZLP_REG%d failed status-0x%x",
+				    i + 1, status);
+				break;
+			} else
+				dbg("ZLP_REG%d Writing success status%d",
+				    i + 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;
+		}
+	}
+	dbg ("mos7840_startup: all ports configured...........");
+
+	/* Zero Length flag enable */
+	Data = 0x0f;
+	status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
+	if (status < 0) {
+		dbg("Writing ZLP_REG5 failed status-0x%x", status);
+		goto error;
+	} else
+		dbg("ZLP_REG5 Writing success status%d", status);
+
+	/* setting configuration feature to one */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			(__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
+	return 0;
+error:
+	for (/* nothing */; i >= 0; i--) {
+		mos7840_port = mos7840_get_port_private(serial->port[i]);
+
+		kfree(mos7840_port->dr);
+		kfree(mos7840_port->ctrl_buf);
+		usb_free_urb(mos7840_port->control_urb);
+		kfree(mos7840_port);
+	}
+	return status;
+}
+
+/****************************************************************************
+ * mos7840_disconnect
+ *	This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+
+static void mos7840_disconnect(struct usb_serial *serial)
+{
+	int i;
+	struct moschip_port *mos7840_port;
+	dbg("%s", " disconnect :entering..........");
+
+	if (!serial) {
+		dbg("%s", "Invalid Handler");
+		return;
+	}
+
+	/* check for the ports to be closed,close the ports and disconnect */
+
+	/* free private structure allocated for serial port  *
+	 * stop reads and writes on all ports                */
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		mos7840_port = mos7840_get_port_private(serial->port[i]);
+		dbg ("mos7840_port %d = %p", i, mos7840_port);
+		if (mos7840_port) {
+			usb_kill_urb(mos7840_port->control_urb);
+		}
+	}
+
+	dbg("%s", "Thank u :: ");
+
+}
+
+/****************************************************************************
+ * mos7840_release
+ *	This function is called when the usb_serial structure is freed.
+ ****************************************************************************/
+
+static void mos7840_release(struct usb_serial *serial)
+{
+	int i;
+	struct moschip_port *mos7840_port;
+	dbg("%s", " release :entering..........");
+
+	if (!serial) {
+		dbg("%s", "Invalid Handler");
+		return;
+	}
+
+	/* check for the ports to be closed,close the ports and disconnect */
+
+	/* free private structure allocated for serial port  *
+	 * stop reads and writes on all ports                */
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		mos7840_port = mos7840_get_port_private(serial->port[i]);
+		dbg("mos7840_port %d = %p", i, mos7840_port);
+		if (mos7840_port) {
+			usb_free_urb(mos7840_port->control_urb);
+			kfree(mos7840_port->ctrl_buf);
+			kfree(mos7840_port->dr);
+			kfree(mos7840_port);
+		}
+	}
+
+	dbg("%s", "Thank u :: ");
+
+}
+
+static struct usb_driver io_driver = {
+	.name = "mos7840",
+	.probe = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+	.id_table = moschip_id_table_combined,
+};
+
+static struct usb_serial_driver moschip7840_4port_device = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "mos7840",
+		   },
+	.description = DRIVER_DESC,
+	.id_table = moschip_port_id_table,
+	.num_ports = 4,
+	.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,
+#ifdef MCSSerialProbe
+	.probe = mos7840_serial_probe,
+#endif
+	.ioctl = mos7840_ioctl,
+	.set_termios = mos7840_set_termios,
+	.break_ctl = mos7840_break,
+	.tiocmget = mos7840_tiocmget,
+	.tiocmset = mos7840_tiocmset,
+	.get_icount = mos7840_get_icount,
+	.attach = mos7840_startup,
+	.disconnect = mos7840_disconnect,
+	.release = mos7840_release,
+	.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(io_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/moto_modem.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/moto_modem.c
new file mode 100644
index 0000000..3ab6214
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/moto_modem.c
@@ -0,0 +1,55 @@
+/*
+ * Motorola USB Phone driver
+ *
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ * {sigh}
+ * Motorola should be using the CDC ACM USB spec, but instead
+ * they try to just "do their own thing"...  This driver should handle a
+ * few phones in which a basic "dumb serial connection" is needed to be
+ * able to get a connection through to them.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x05c6, 0x3197) },	/* unknown Motorola phone */
+	{ USB_DEVICE(0x0c44, 0x0022) },	/* unknown Mororola phone */
+	{ USB_DEVICE(0x22b8, 0x2a64) },	/* Motorola KRZR K1m */
+	{ USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */
+	{ USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver moto_driver = {
+	.name =		"moto-modem",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver moto_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"moto-modem",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&moto_device, NULL
+};
+
+module_usb_serial_driver(moto_driver, serial_drivers);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/navman.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/navman.c
new file mode 100644
index 0000000..29ab6eb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/navman.c
@@ -0,0 +1,140 @@
+/*
+ * Navman Serial USB driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	version 2 as published by the Free Software Foundation.
+ *
+ * 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/init.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static bool debug;
+
+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 struct usb_driver navman_driver = {
+	.name =		"navman",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static void navman_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	struct tty_struct *tty;
+	int status = urb->status;
+	int result;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+			      urb->actual_length, data);
+
+	tty = tty_port_tty_get(&port->port);
+	if (tty && urb->actual_length) {
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (port->interrupt_in_urb) {
+		dbg("%s - adding interrupt input for treo", __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)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	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)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	/*
+	 * 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(navman_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/omninet.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/omninet.c
new file mode 100644
index 0000000..88dc785
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/omninet.c
@@ -0,0 +1,331 @@
+/*
+ * USB ZyXEL omni.net LCD PLUS driver
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * 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/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/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static bool debug;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1"
+#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 int  omninet_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void omninet_close(struct usb_serial_port *port);
+static void omninet_read_bulk_callback(struct urb *urb);
+static void omninet_write_bulk_callback(struct urb *urb);
+static int  omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
+				const unsigned char *buf, int count);
+static int  omninet_write_room(struct tty_struct *tty);
+static void omninet_disconnect(struct usb_serial *serial);
+static void omninet_release(struct usb_serial *serial);
+static int omninet_attach(struct usb_serial *serial);
+
+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_driver omninet_driver = {
+	.name =		"omninet",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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_ports =		1,
+	.attach =		omninet_attach,
+	.open =			omninet_open,
+	.close =		omninet_close,
+	.write =		omninet_write,
+	.write_room =		omninet_write_room,
+	.read_bulk_callback =	omninet_read_bulk_callback,
+	.write_bulk_callback =	omninet_write_bulk_callback,
+	.disconnect =		omninet_disconnect,
+	.release =		omninet_release,
+};
+
+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, you can see it in the above structure.
+ *
+ * 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 trasmitted 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 trasmitted frames
+ *	0: handshaking, unknown, usually set to 1 in trasmitted 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_attach(struct usb_serial *serial)
+{
+	struct omninet_data *od;
+	struct usb_serial_port *port = serial->port[0];
+
+	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
+	if (!od) {
+		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n",
+			__func__, sizeof(struct omninet_data));
+		return -ENOMEM;
+	}
+	usb_set_serial_port_data(port, od);
+	return 0;
+}
+
+static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial	*serial = port->serial;
+	struct usb_serial_port	*wport;
+	int			result = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	wport = serial->port[1];
+	tty_port_tty_set(&wport->port, tty);
+
+	/* Start reading from the device */
+	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+	if (result)
+		dev_err(&port->dev,
+			"%s - failed submitting read urb, error %d\n",
+			__func__, result);
+	return result;
+}
+
+static void omninet_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+	usb_kill_urb(port->read_urb);
+}
+
+
+#define OMNINET_DATAOFFSET	0x04
+#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
+#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
+
+static void omninet_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port 	*port 	= urb->context;
+	unsigned char 		*data 	= urb->transfer_buffer;
+	struct omninet_header 	*header = (struct omninet_header *) &data[0];
+	int status = urb->status;
+	int result;
+	int i;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __func__, status);
+		return;
+	}
+
+	if (debug && header->oh_xxx != 0x30) {
+		if (urb->actual_length) {
+			printk(KERN_DEBUG "%s: omninet_read %d: ",
+			       __FILE__, header->oh_len);
+			for (i = 0; i < (header->oh_len +
+						OMNINET_HEADERLEN); i++)
+				printk("%.2x ", data[i]);
+			printk("\n");
+		}
+	}
+
+	if (urb->actual_length && header->oh_len) {
+		struct tty_struct *tty = tty_port_tty_get(&port->port);
+		tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
+							header->oh_len);
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+
+	/* Continue trying to always read  */
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&port->dev,
+			"%s - failed resubmitting read urb, error %d\n",
+			__func__, result);
+}
+
+static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
+					const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct usb_serial_port *wport = serial->port[1];
+
+	struct omninet_data *od = usb_get_serial_port_data(port);
+	struct omninet_header *header = (struct omninet_header *)
+					wport->write_urb->transfer_buffer;
+
+	int			result;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __func__);
+		return 0;
+	}
+
+	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
+		dbg("%s - already writing", __func__);
+		return 0;
+	}
+
+	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
+
+	memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
+								buf, count);
+
+	usb_serial_debug_data(debug, &port->dev, __func__, count,
+					wport->write_urb->transfer_buffer);
+
+	header->oh_seq 	= od->od_outseq++;
+	header->oh_len 	= count;
+	header->oh_xxx  = 0x03;
+	header->oh_pad 	= 0x00;
+
+	/* send the data out the bulk port, always 64 bytes */
+	wport->write_urb->transfer_buffer_length = 64;
+
+	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
+	if (result) {
+		set_bit(0, &wport->write_urbs_free);
+		dev_err_console(port,
+			"%s - failed submitting write urb, error %d\n",
+			__func__, result);
+	} else
+		result = count;
+
+	return result;
+}
+
+
+static int omninet_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial 	*serial = port->serial;
+	struct usb_serial_port 	*wport 	= serial->port[1];
+
+	int room = 0; /* Default: no room */
+
+	if (test_bit(0, &wport->write_urbs_free))
+		room = wport->bulk_out_size - OMNINET_HEADERLEN;
+
+	dbg("%s - returns %d", __func__, room);
+
+	return room;
+}
+
+static void omninet_write_bulk_callback(struct urb *urb)
+{
+/*	struct omninet_header	*header = (struct omninet_header  *)
+						urb->transfer_buffer; */
+	struct usb_serial_port 	*port   =  urb->context;
+	int status = urb->status;
+
+	dbg("%s - port %0x", __func__, port->number);
+
+	set_bit(0, &port->write_urbs_free);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __func__, status);
+		return;
+	}
+
+	usb_serial_port_softint(port);
+}
+
+
+static void omninet_disconnect(struct usb_serial *serial)
+{
+	struct usb_serial_port *wport = serial->port[1];
+
+	dbg("%s", __func__);
+
+	usb_kill_urb(wport->write_urb);
+}
+
+
+static void omninet_release(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+
+	dbg("%s", __func__);
+
+	kfree(usb_get_serial_port_data(port));
+}
+
+module_usb_serial_driver(omninet_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/opticon.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/opticon.c
new file mode 100644
index 0000000..58b7cec
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/opticon.c
@@ -0,0 +1,647 @@
+/*
+ * Opticon USB barcode to serial driver
+ *
+ * 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.
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.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 bool debug;
+
+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 {
+	struct usb_device *udev;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	unsigned char *bulk_in_buffer;
+	struct urb *bulk_read_urb;
+	int buffer_size;
+	u8 bulk_address;
+	spinlock_t lock;	/* protects the following flags */
+	bool throttled;
+	bool actually_throttled;
+	bool rts;
+	bool cts;
+	int outstanding_urbs;
+};
+
+
+
+static void opticon_read_bulk_callback(struct urb *urb)
+{
+	struct opticon_private *priv = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	struct usb_serial_port *port = priv->port;
+	int status = urb->status;
+	struct tty_struct *tty;
+	int result;
+	int data_length;
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
+			      data);
+
+	if (urb->actual_length > 2) {
+		data_length = urb->actual_length - 2;
+
+		/*
+		 * 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 ((data[0] == 0x00) && (data[1] == 0x00)) {
+			/* real data, send it to the tty layer */
+			tty = tty_port_tty_get(&port->port);
+			if (tty) {
+				tty_insert_flip_string(tty, data + 2,
+						       data_length);
+				tty_flip_buffer_push(tty);
+				tty_kref_put(tty);
+			}
+		} else {
+			if ((data[0] == 0x00) && (data[1] == 0x01)) {
+				spin_lock_irqsave(&priv->lock, flags);
+				/* CTS status information package */
+				if (data[2] == 0x00)
+					priv->cts = false;
+				else
+					priv->cts = true;
+				spin_unlock_irqrestore(&priv->lock, flags);
+			} else {
+				dev_dbg(&priv->udev->dev,
+					"Unknown data packet received from the device:"
+					" %2x %2x\n",
+					data[0], data[1]);
+			}
+		}
+	} else {
+		dev_dbg(&priv->udev->dev,
+			"Improper amount of data received from the device, "
+			"%d bytes", urb->actual_length);
+	}
+
+exit:
+	spin_lock(&priv->lock);
+
+	/* Continue trying to always read if we should */
+	if (!priv->throttled) {
+		usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
+				  usb_rcvbulkpipe(priv->udev,
+						  priv->bulk_address),
+				  priv->bulk_in_buffer, priv->buffer_size,
+				  opticon_read_bulk_callback, priv);
+		result = usb_submit_urb(priv->bulk_read_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(&priv->lock);
+}
+
+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);
+
+	return retval;
+}
+
+static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+	unsigned long flags;
+	int result = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->throttled = false;
+	priv->actually_throttled = false;
+	priv->port = port;
+	priv->rts = false;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Clear RTS line */
+	send_control_msg(port, CONTROL_RTS, 0);
+
+	/* Setup the read URB and start reading from the device */
+	usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
+			  usb_rcvbulkpipe(priv->udev,
+					  priv->bulk_address),
+			  priv->bulk_in_buffer, priv->buffer_size,
+			  opticon_read_bulk_callback, priv);
+
+	/* clear the halt status of the enpoint */
+	usb_clear_halt(priv->udev, priv->bulk_read_urb->pipe);
+
+	result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL);
+	if (result)
+		dev_err(&port->dev,
+			"%s - failed resubmitting read urb, error %d\n",
+			__func__, result);
+	/* Request CTS line state, sometimes during opening the current
+	 * CTS state can be missed. */
+	send_control_msg(port, RESEND_CTS_STATE, 1);
+	return result;
+}
+
+static void opticon_close(struct usb_serial_port *port)
+{
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* shutdown our urbs */
+	usb_kill_urb(priv->bulk_read_urb);
+}
+
+static void opticon_write_control_callback(struct urb *urb)
+{
+	struct opticon_private *priv = urb->context;
+	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)
+		dbg("%s - nonzero write bulk status received: %d",
+		    __func__, status);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	--priv->outstanding_urbs;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	usb_serial_port_softint(priv->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_data(port->serial);
+	struct usb_serial *serial = port->serial;
+	struct urb *urb;
+	unsigned char *buffer;
+	unsigned long flags;
+	int status;
+	struct usb_ctrlrequest *dr;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		dbg("%s - write limit hit", __func__);
+		return 0;
+	}
+	priv->outstanding_urbs++;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	buffer = kmalloc(count, GFP_ATOMIC);
+	if (!buffer) {
+		dev_err(&port->dev, "out of memory\n");
+		count = -ENOMEM;
+
+		goto error_no_buffer;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(&port->dev, "no more free urbs\n");
+		count = -ENOMEM;
+		goto error_no_urb;
+	}
+
+	memcpy(buffer, buf, count);
+
+	usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+
+	/* The conncected 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) {
+		dev_err(&port->dev, "out of memory\n");
+		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, priv);
+
+	/* 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_data(port->serial);
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/*
+	 * 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);
+		dbg("%s - write limit hit", __func__);
+		return 0;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 2048;
+}
+
+static void opticon_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->throttled = true;
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
+static void opticon_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+	unsigned long flags;
+	int result, was_throttled;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->throttled = false;
+	was_throttled = priv->actually_throttled;
+	priv->actually_throttled = false;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (was_throttled) {
+		result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC);
+		if (result)
+			dev_err(&port->dev,
+				"%s - failed submitting read urb, error %d\n",
+							__func__, result);
+	}
+}
+
+static int opticon_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+	unsigned long flags;
+	int result = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->rts)
+		result |= TIOCM_RTS;
+	if (priv->cts)
+		result |= TIOCM_CTS;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s - %x", __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_data(port->serial);
+	unsigned long flags;
+	bool rts;
+	bool changed = false;
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+	/* 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;
+
+	/* Send the new RTS state to the connected device */
+	return send_control_msg(port, CONTROL_RTS, !rts);
+}
+
+static int get_serial_info(struct opticon_private *priv,
+			   struct serial_struct __user *serial)
+{
+	struct serial_struct tmp;
+
+	if (!serial)
+		return -EFAULT;
+
+	memset(&tmp, 0x00, sizeof(tmp));
+
+	/* fake emulate a 16550 uart to make userspace code happy */
+	tmp.type		= PORT_16550A;
+	tmp.line		= priv->serial->minor;
+	tmp.port		= 0;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	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;
+	struct opticon_private *priv = usb_get_serial_data(port->serial);
+
+	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(priv,
+				       (struct serial_struct __user *)arg);
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int opticon_startup(struct usb_serial *serial)
+{
+	struct opticon_private *priv;
+	struct usb_host_interface *intf;
+	int i;
+	int retval = -ENOMEM;
+	bool bulk_in_found = false;
+
+	/* create our private serial structure */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	spin_lock_init(&priv->lock);
+	priv->serial = serial;
+	priv->port = serial->port[0];
+	priv->udev = serial->dev;
+	priv->outstanding_urbs = 0;	/* Init the outstanding urbs */
+
+	/* find our bulk endpoint */
+	intf = serial->interface->altsetting;
+	for (i = 0; i < intf->desc.bNumEndpoints; ++i) {
+		struct usb_endpoint_descriptor *endpoint;
+
+		endpoint = &intf->endpoint[i].desc;
+		if (!usb_endpoint_is_bulk_in(endpoint))
+			continue;
+
+		priv->bulk_read_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!priv->bulk_read_urb) {
+			dev_err(&priv->udev->dev, "out of memory\n");
+			goto error;
+		}
+
+		priv->buffer_size = usb_endpoint_maxp(endpoint) * 2;
+		priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
+		if (!priv->bulk_in_buffer) {
+			dev_err(&priv->udev->dev, "out of memory\n");
+			goto error;
+		}
+
+		priv->bulk_address = endpoint->bEndpointAddress;
+
+		bulk_in_found = true;
+		break;
+		}
+
+	if (!bulk_in_found) {
+		dev_err(&priv->udev->dev,
+			"Error - the proper endpoints were not found!\n");
+		goto error;
+	}
+
+	usb_set_serial_data(serial, priv);
+	return 0;
+
+error:
+	usb_free_urb(priv->bulk_read_urb);
+	kfree(priv->bulk_in_buffer);
+	kfree(priv);
+	return retval;
+}
+
+static void opticon_disconnect(struct usb_serial *serial)
+{
+	struct opticon_private *priv = usb_get_serial_data(serial);
+
+	dbg("%s", __func__);
+
+	usb_kill_urb(priv->bulk_read_urb);
+	usb_free_urb(priv->bulk_read_urb);
+}
+
+static void opticon_release(struct usb_serial *serial)
+{
+	struct opticon_private *priv = usb_get_serial_data(serial);
+
+	dbg("%s", __func__);
+
+	kfree(priv->bulk_in_buffer);
+	kfree(priv);
+}
+
+static int opticon_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	struct opticon_private *priv = usb_get_serial_data(serial);
+
+	usb_kill_urb(priv->bulk_read_urb);
+	return 0;
+}
+
+static int opticon_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	struct opticon_private *priv = usb_get_serial_data(serial);
+	struct usb_serial_port *port = serial->port[0];
+	int result;
+
+	mutex_lock(&port->port.mutex);
+	/* This is protected by the port mutex against close/open */
+	if (test_bit(ASYNCB_INITIALIZED, &port->port.flags))
+		result = usb_submit_urb(priv->bulk_read_urb, GFP_NOIO);
+	else
+		result = 0;
+	mutex_unlock(&port->port.mutex);
+	return result;
+}
+
+static struct usb_driver opticon_driver = {
+	.name =		"opticon",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.suspend =	opticon_suspend,
+	.resume =	opticon_resume,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver opticon_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"opticon",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.attach =		opticon_startup,
+	.open =			opticon_open,
+	.close =		opticon_close,
+	.write =		opticon_write,
+	.write_room = 		opticon_write_room,
+	.disconnect =		opticon_disconnect,
+	.release =		opticon_release,
+	.throttle = 		opticon_throttle,
+	.unthrottle =		opticon_unthrottle,
+	.ioctl =		opticon_ioctl,
+	.tiocmget =		opticon_tiocmget,
+	.tiocmset =		opticon_tiocmset,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&opticon_device, NULL
+};
+
+module_usb_serial_driver(opticon_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/option.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/option.c
new file mode 100644
index 0000000..7c6c2ee
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/option.c
@@ -0,0 +1,2014 @@
+/*
+  USB Driver for GSM modems
+
+  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
+
+  This driver is free software; you can redistribute it and/or modify
+  it under the terms of Version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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_VERSION "v0.7.2"
+#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 void option_release(struct usb_serial *serial);
+static int option_send_setup(struct usb_serial_port *port);
+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_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 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
+
+#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_LE920			0x1200
+#define TELIT_PRODUCT_LE910			0x1201
+
+/* 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_CDMA_TECH			0xfffe
+#define ZTE_PRODUCT_AC8710			0xfff1
+#define ZTE_PRODUCT_AC2726			0xfff5
+#define ZTE_PRODUCT_AC8710T			0xffff
+#define ZTE_PRODUCT_MC2718			0xffe8
+#define ZTE_PRODUCT_AD3812			0xffeb
+#define ZTE_PRODUCT_MC2716			0xffed
+
+/* added by gaowei, in order to support MF80S2 serial port */
+#define ZTE_PRODUCT_MF80S2			0x0199
+
+
+#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 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
+
+/* 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
+
+/* Cinterion (formerly Siemens) products */
+#define SIEMENS_VENDOR_ID				0x0681
+#define CINTERION_VENDOR_ID				0x1e2d
+#define CINTERION_PRODUCT_HC25_MDM		0x0047
+#define CINTERION_PRODUCT_HC25_MDMNET	0x0040
+#define CINTERION_PRODUCT_HC28_MDM		0x004C
+#define CINTERION_PRODUCT_HC28_MDMNET	0x004A /* same for HC28J */
+#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
+
+/* 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_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
+
+/* some devices interfaces need special handling due to a number of reasons */
+enum option_blacklist_reason {
+		OPTION_BLACKLIST_NONE = 0,
+		OPTION_BLACKLIST_SENDSETUP = 1,
+		OPTION_BLACKLIST_RESERVED_IF = 2
+};
+
+#define MAX_BL_NUM  8
+struct option_blacklist_info {
+	/* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */
+	const unsigned long sendsetup;
+	/* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */
+	const unsigned long reserved;
+};
+
+static const struct option_blacklist_info four_g_w14_blacklist = {
+	.sendsetup = BIT(0) | BIT(1),
+};
+
+static const struct option_blacklist_info alcatel_x200_blacklist = {
+	.sendsetup = BIT(0) | BIT(1),
+	.reserved = BIT(4),
+};
+
+static const struct option_blacklist_info zte_0037_blacklist = {
+	.sendsetup = BIT(0) | BIT(1),
+};
+
+static const struct option_blacklist_info zte_k3765_z_blacklist = {
+	.sendsetup = BIT(0) | BIT(1) | BIT(2),
+	.reserved = BIT(4),
+};
+
+static const struct option_blacklist_info zte_ad3812_z_blacklist = {
+	.sendsetup = BIT(0) | BIT(1) | BIT(2),
+};
+
+static const struct option_blacklist_info zte_mc2718_z_blacklist = {
+	.sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4),
+};
+
+static const struct option_blacklist_info zte_mc2716_z_blacklist = {
+	.sendsetup = BIT(1) | BIT(2) | BIT(3),
+};
+
+static const struct option_blacklist_info huawei_cdc12_blacklist = {
+	.reserved = BIT(1) | BIT(2),
+};
+
+static const struct option_blacklist_info net_intf0_blacklist = {
+	.reserved = BIT(0),
+};
+
+static const struct option_blacklist_info net_intf1_blacklist = {
+	.reserved = BIT(1),
+};
+
+static const struct option_blacklist_info net_intf2_blacklist = {
+	.reserved = BIT(2),
+};
+
+static const struct option_blacklist_info net_intf3_blacklist = {
+	.reserved = BIT(3),
+};
+
+static const struct option_blacklist_info net_intf4_blacklist = {
+	.reserved = BIT(4),
+};
+
+static const struct option_blacklist_info net_intf5_blacklist = {
+	.reserved = BIT(5),
+};
+
+static const struct option_blacklist_info net_intf6_blacklist = {
+	.reserved = BIT(6),
+};
+
+static const struct option_blacklist_info zte_mf626_blacklist = {
+	.sendsetup = BIT(0) | BIT(1),
+	.reserved = BIT(4),
+};
+
+static const struct option_blacklist_info zte_1255_blacklist = {
+	.reserved = BIT(3) | BIT(4),
+};
+
+static const struct option_blacklist_info telit_le910_blacklist = {
+	.sendsetup = BIT(0),
+	.reserved = BIT(1) | BIT(2),
+};
+
+static const struct option_blacklist_info telit_le920_blacklist = {
+	.sendsetup = BIT(0),
+	.reserved = BIT(1) | BIT(5),
+};
+
+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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t) &net_intf1_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S6, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t) &net_intf2_blacklist },
+	{ 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 = (kernel_ulong_t) &huawei_cdc12_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x14ac, 0xff, 0xff, 0xff),	/* Huawei E1820 */
+		.driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+	{ 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(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(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(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1005) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1006) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1007) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1008) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1009) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100A) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100B) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100C) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100D) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100E) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100F) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1010) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1011) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012) },
+	{ 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(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 */
+	{ 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 = (kernel_ulong_t)&net_intf0_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_301),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7002),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629K),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7004),
+	  .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7005) },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_629),
+	  .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629S),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720I),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7212),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7213),
+	  .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7251),
+	  .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7252),
+	  .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+	{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7253),
+	  .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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_LE910),
+		.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
+		.driver_info = (kernel_ulong_t)&telit_le920_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf3_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf3_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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 = (kernel_ulong_t)&zte_mf626_blacklist },
+	{ 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 = (kernel_ulong_t)&zte_0037_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf5_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf5_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf2_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&zte_1255_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf2_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff),  /* ZTE MF91 */
+		.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff),  /* Telewell TW-LTE 4G v2 */
+		.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ 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 = (kernel_ulong_t)&zte_k3765_z_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf1_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 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, 0x0542, 0xff, 0xff, 0xff) },
+
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0536, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0534, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0579, 0xff, 0xff, 0xff) },
+	/* added by gaowei*/
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF80S2, 0xff, 0xff, 0xff) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196,             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_AC8710, 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 = (kernel_ulong_t)&zte_mc2718_z_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff),
+	 .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
+	{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
+	 .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+	{ 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(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
+	  .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist
+	},
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
+	  .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x0052),
+	  .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b6),
+	  .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b7),
+	  .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L800MA),
+	  .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+	{ 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 = (kernel_ulong_t)&four_g_w14_blacklist
+  	},
+	{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
+	{ 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(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_C100_1)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_C100_2)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1004)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1005)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1006)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1007)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1008)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1009)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100A)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100B) },
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100C) },
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100D) },
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100E) },
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_100F) },
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1011)},
+	{ USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1012)},
+	/* 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX) },
+	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
+		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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 = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120),
+		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD140),
+		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD155),
+		.driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
+		.driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD160),
+		.driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+	{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD500),
+		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ 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(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000),					/* TP-Link MA260 */
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x02, 0x01) },	/* D-Link DWM-156 (variant) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x00, 0x00) },	/* D-Link DWM-156 (variant) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+	{ USB_DEVICE_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(INOVIA_VENDOR_ID, INOVIA_SEW858) },
+	{ USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) },
+	{ } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, option_ids);
+
+static struct usb_driver option_driver = {
+	.name       = "option",
+	.probe      = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+#ifdef CONFIG_PM
+	.suspend    = usb_serial_suspend,
+	.resume     = usb_serial_resume,
+	.supports_autosuspend =	1,
+#endif
+	.id_table   = 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,
+	.set_termios       = usb_wwan_set_termios,
+	.tiocmget          = usb_wwan_tiocmget,
+	.tiocmset          = usb_wwan_tiocmset,
+	.ioctl             = usb_wwan_ioctl,
+	.attach            = usb_wwan_startup,
+	.disconnect        = usb_wwan_disconnect,
+	.release           = option_release,
+	.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
+};
+
+static bool debug;
+
+module_usb_serial_driver(option_driver, serial_drivers);
+
+static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason,
+			   const struct option_blacklist_info *blacklist)
+{
+	unsigned long num;
+	const unsigned long *intf_list;
+
+	if (blacklist) {
+		if (reason == OPTION_BLACKLIST_SENDSETUP)
+			intf_list = &blacklist->sendsetup;
+		else if (reason == OPTION_BLACKLIST_RESERVED_IF)
+			intf_list = &blacklist->reserved;
+		else {
+			BUG_ON(reason);
+			return false;
+		}
+
+		for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) {
+			if (num == ifnum)
+				return true;
+		}
+	}
+	return false;
+}
+
+static int option_probe(struct usb_serial *serial,
+			const struct usb_device_id *id)
+{
+	struct usb_wwan_intf_private *data;
+
+	/* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
+	if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
+		serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
+		serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
+		return -ENODEV;
+
+	/* Bandrich modem and AT command interface is 0xff */
+	if ((serial->dev->descriptor.idVendor == BANDRICH_VENDOR_ID ||
+		serial->dev->descriptor.idVendor == PIRELLI_VENDOR_ID) &&
+		serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
+		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 (is_blacklisted(
+		serial->interface->cur_altsetting->desc.bInterfaceNumber,
+		OPTION_BLACKLIST_RESERVED_IF,
+		(const struct option_blacklist_info *) id->driver_info))
+		return -ENODEV;
+	if (serial->dev->descriptor.idVendor == ZTE_VENDOR_ID &&
+		serial->dev->descriptor.idProduct == 0x0579 &&
+		serial->interface->cur_altsetting->desc.bInterfaceClass == 0x6)
+		return -ENODEV;
+	/* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */
+	if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID &&
+		serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 &&
+		serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA)
+		return -ENODEV;
+
+	data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->send_setup = option_send_setup;
+	spin_lock_init(&data->susp_lock);
+	data->private = (void *)id->driver_info;
+	return 0;
+}
+
+static void option_release(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+	usb_wwan_release(serial);
+
+	kfree(priv);
+}
+
+static void option_instat_callback(struct urb *urb)
+{
+	int err;
+	int status = urb->status;
+	struct usb_serial_port *port =  urb->context;
+	struct usb_wwan_port_private *portdata =
+					usb_get_serial_port_data(port);
+
+	dbg("%s", __func__);
+	dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata);
+
+	if (status == 0) {
+		struct usb_ctrlrequest *req_pkt =
+				(struct usb_ctrlrequest *)urb->transfer_buffer;
+
+		if (!req_pkt) {
+			dbg("%s: NULL req_pkt", __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));
+
+			dbg("%s: signal x%x", __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) {
+				struct tty_struct *tty =
+						tty_port_tty_get(&port->port);
+				if (tty && !C_CLOCAL(tty))
+					tty_hangup(tty);
+				tty_kref_put(tty);
+			}
+		} else {
+			dbg("%s: type %x req %x", __func__,
+				req_pkt->bRequestType, req_pkt->bRequest);
+		}
+	} else
+		err("%s: error %d", __func__, status);
+
+	/* Resubmit urb so we continue receiving IRQ data */
+	if (status != -ESHUTDOWN && status != -ENOENT) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err)
+			dbg("%s: resubmit intr urb failed. (%d)",
+				__func__, err);
+	}
+}
+
+/** send RTS/DTR state to the port.
+ *
+ * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
+ * CDC.
+*/
+static int option_send_setup(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct usb_wwan_intf_private *intfdata =
+		(struct usb_wwan_intf_private *) serial->private;
+	struct usb_wwan_port_private *portdata;
+	int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+	int val = 0;
+	int res;
+	dbg("%s", __func__);
+
+	if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP,
+			(struct option_blacklist_info *) intfdata->private)) {
+		dbg("No send_setup on blacklisted interface #%d\n", ifNum);
+		return -EIO;
+	}
+
+	portdata = usb_get_serial_port_data(port);
+
+	if (portdata->dtr_state)
+		val |= 0x01;
+	if (portdata->rts_state)
+		val |= 0x02;
+
+	res = usb_autopm_get_interface(serial->interface);
+	if (res)
+		return res;
+
+	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				0x22, 0x21, val, ifNum, NULL,
+				0, USB_CTRL_SET_TIMEOUT);
+
+	usb_autopm_put_interface(serial->interface);
+
+	return res;
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.c
new file mode 100644
index 0000000..bddc9f1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.c
@@ -0,0 +1,972 @@
+/*
+ * 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!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * 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/init.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>"
+#define OTI6858_VERSION "0.2"
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver oti6858_driver = {
+	.name =		"oti6858",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static bool debug;
+
+/* 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_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 int oti6858_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg);
+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_startup(struct usb_serial *serial);
+static void oti6858_release(struct usb_serial *serial);
+
+/* device info */
+static struct usb_serial_driver oti6858_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"oti6858",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.open =			oti6858_open,
+	.close =		oti6858_close,
+	.write =		oti6858_write,
+	.ioctl =		oti6858_ioctl,
+	.set_termios =		oti6858_set_termios,
+	.init_termios = 	oti6858_init_termios,
+	.tiocmget =		oti6858_tiocmget,
+	.tiocmset =		oti6858_tiocmset,
+	.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,
+	.attach =		oti6858_startup,
+	.release =		oti6858_release,
+};
+
+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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL);
+	if (new_setup == NULL) {
+		dev_err(&port->dev, "%s(): out of memory!\n", __func__);
+		/* 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);
+
+	dbg("%s(): submitting interrupt urb", __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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	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) {
+			dev_err_console(port, "%s(): kmalloc failed\n",
+					__func__);
+			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;
+
+		dbg("%s(): submitting interrupt urb", __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_startup(struct usb_serial *serial)
+{
+	struct usb_serial_port *port = serial->port[0];
+	struct oti6858_private *priv;
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL);
+		if (!priv)
+			break;
+
+		spin_lock_init(&priv->lock);
+/*		INIT_WORK(&priv->setup_work, setup_line, serial->port[i]); */
+/*		INIT_WORK(&priv->write_work, send_data, serial->port[i]); */
+		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(serial->port[i], priv);
+	}
+	if (i == serial->num_ports)
+		return 0;
+
+	for (--i; i >= 0; --i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
+}
+
+static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	dbg("%s(port = %d, count = %d)", __func__, port->number, 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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	if (!tty) {
+		dbg("%s(): no tty structures", __func__);
+		return;
+	}
+
+	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 ktermios tmp_termios;
+	struct usb_serial *serial = port->serial;
+	struct oti6858_control_pkt *buf;
+	unsigned long flags;
+	int result;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	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 == NULL) {
+		dev_err(&port->dev, "%s(): out of memory!\n", __func__);
+		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);
+
+	dbg("%s(): submitting interrupt urb", __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, &tmp_termios);
+	port->port.drain_delay = 256;	/* FIXME: check the FIFO length */
+	return 0;
+}
+
+static void oti6858_close(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	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);
+
+	dbg("%s(): after buf_clear()", __func__);
+
+	/* cancel scheduled setup */
+	cancel_delayed_work_sync(&priv->delayed_setup_work);
+	cancel_delayed_work_sync(&priv->delayed_write_work);
+
+	/* shutdown our urbs */
+	dbg("%s(): shutting down urbs", __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;
+
+	dbg("%s(port = %d, set = 0x%08x, clear = 0x%08x)",
+				__func__, port->number, set, clear);
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	/* 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;
+
+	dbg("%s(port = %d)", __func__, port->number);
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	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;
+
+	dbg("%s() = 0x%08x", __func__, result);
+
+	return result;
+}
+
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int prev, status;
+	unsigned int changed;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prev = priv->status.pin_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (1) {
+		wait_event_interruptible(port->delta_msr_wait,
+					port->serial->disconnected ||
+					priv->status.pin_state != prev);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		if (port->serial->disconnected)
+			return -EIO;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->status.pin_state & PIN_MASK;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prev ^ status;
+		/* FIXME: check if this is correct (active high/low) */
+		if (((arg & TIOCM_RNG) && (changed & PIN_RI)) ||
+		    ((arg & TIOCM_DSR) && (changed & PIN_DSR)) ||
+		    ((arg & TIOCM_CD)  && (changed & PIN_DCD)) ||
+		    ((arg & TIOCM_CTS) && (changed & PIN_CTS)))
+			return 0;
+		prev = status;
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+
+static int oti6858_ioctl(struct tty_struct *tty,
+			unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)",
+				__func__, port->number, cmd, arg);
+
+	switch (cmd) {
+	case TIOCMIWAIT:
+		dbg("%s(): TIOCMIWAIT", __func__);
+		return wait_modem_info(port, arg);
+	default:
+		dbg("%s(): 0x%04x not supported", __func__, cmd);
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+
+static void oti6858_release(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s()", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+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;
+
+	dbg("%s(port = %d, status = %d)",
+				__func__, port->number, status);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s(): urb shutting down with status: %d",
+					__func__, status);
+		return;
+	default:
+		dbg("%s(): nonzero urb status received: %d",
+					__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;
+					dbg("%s(): scheduling setup_line()",
+					    __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;
+					dbg("%s(): scheduling setup_line()",
+					    __func__);
+					schedule_delayed_work(&priv->delayed_setup_work, 0);
+				}
+			}
+		}
+
+		if (!priv->transient) {
+			if (xs->pin_state != priv->status.pin_state)
+				wake_up_interruptible(&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;
+
+/*		dbg("%s(): submitting interrupt urb", __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);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	int status = urb->status;
+	int result;
+
+	dbg("%s(port = %d, status = %d)",
+				__func__, port->number, status);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->flags.read_urb_in_use = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (status != 0) {
+		dbg("%s(): unable to handle the error, exiting", __func__);
+		return;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	if (tty != NULL && urb->actual_length > 0) {
+		tty_insert_flip_string(tty, data, urb->actual_length);
+		tty_flip_buffer_push(tty);
+	}
+	tty_kref_put(tty);
+
+	/* 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;
+
+	dbg("%s(port = %d, status = %d)",
+				__func__, port->number, status);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s(): urb shutting down with status: %d",
+					__func__, status);
+		priv->flags.write_urb_in_use = 0;
+		return;
+	default:
+		/* error in the urb, so we have to resubmit it */
+		dbg("%s(): nonzero write bulk status received: %d",
+					__func__, status);
+		dbg("%s(): overflow in write", __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 */
+	dbg("%s(): submitting interrupt urb", __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(oti6858_driver, serial_drivers);
+
+MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
+MODULE_AUTHOR(OTI6858_AUTHOR);
+MODULE_VERSION(OTI6858_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "enable debug output");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.h
new file mode 100644
index 0000000..704ac3a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/oti6858.h
@@ -0,0 +1,15 @@
+/*
+ * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#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/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.c
new file mode 100644
index 0000000..723ed87
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.c
@@ -0,0 +1,867 @@
+/*
+ * 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
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * 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/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 "pl2303.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
+
+static bool debug;
+
+#define PL2303_CLOSING_WAIT	(30*HZ)
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
+	{ 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_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) },
+	{ 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) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) },
+	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) },
+	{ 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) },
+	{ 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);
+
+static struct usb_driver pl2303_driver = {
+	.name =		"pl2303",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+	.suspend =      usb_serial_suspend,
+	.resume =       usb_serial_resume,
+	.supports_autosuspend =	1,
+};
+
+#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			0x08
+#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
+
+
+enum pl2303_type {
+	type_0,		/* don't know the difference between type 0 and */
+	type_1,		/* type 1, until someone from prolific tells us... */
+	HX,		/* HX version of the pl2303 chip */
+};
+
+struct pl2303_private {
+	spinlock_t lock;
+	u8 line_control;
+	u8 line_status;
+	enum pl2303_type type;
+};
+
+static int pl2303_vendor_read(__u16 value, __u16 index,
+		struct usb_serial *serial, unsigned char *buf)
+{
+	int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+			value, index, buf, 1, 100);
+	dbg("0x%x:0x%x:0x%x:0x%x  %d - %x", VENDOR_READ_REQUEST_TYPE,
+			VENDOR_READ_REQUEST, value, index, res, buf[0]);
+	return res;
+}
+
+static int pl2303_vendor_write(__u16 value, __u16 index,
+		struct usb_serial *serial)
+{
+	int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+			value, index, NULL, 0, 100);
+	dbg("0x%x:0x%x:0x%x:0x%x  %d", VENDOR_WRITE_REQUEST_TYPE,
+			VENDOR_WRITE_REQUEST, value, index, res);
+	return res;
+}
+
+static int pl2303_startup(struct usb_serial *serial)
+{
+	struct pl2303_private *priv;
+	enum pl2303_type type = type_0;
+	unsigned char *buf;
+	int i;
+
+	buf = kmalloc(10, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	if (serial->dev->descriptor.bDeviceClass == 0x02)
+		type = type_0;
+	else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
+		type = HX;
+	else if (serial->dev->descriptor.bDeviceClass == 0x00)
+		type = type_1;
+	else if (serial->dev->descriptor.bDeviceClass == 0xFF)
+		type = type_1;
+	dbg("device type: %d", type);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL);
+		if (!priv)
+			goto cleanup;
+		spin_lock_init(&priv->lock);
+		priv->type = type;
+		usb_set_serial_port_data(serial->port[i], priv);
+	}
+
+	pl2303_vendor_read(0x8484, 0, serial, buf);
+	pl2303_vendor_write(0x0404, 0, serial);
+	pl2303_vendor_read(0x8484, 0, serial, buf);
+	pl2303_vendor_read(0x8383, 0, serial, buf);
+	pl2303_vendor_read(0x8484, 0, serial, buf);
+	pl2303_vendor_write(0x0404, 1, serial);
+	pl2303_vendor_read(0x8484, 0, serial, buf);
+	pl2303_vendor_read(0x8383, 0, serial, buf);
+	pl2303_vendor_write(0, 1, serial);
+	pl2303_vendor_write(1, 0, serial);
+	if (type == HX)
+		pl2303_vendor_write(2, 0x44, serial);
+	else
+		pl2303_vendor_write(2, 0x24, serial);
+
+	kfree(buf);
+	return 0;
+
+cleanup:
+	kfree(buf);
+	for (--i; i >= 0; --i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
+}
+
+static int set_control_lines(struct usb_device *dev, u8 value)
+{
+	int retval;
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+				 value, 0, NULL, 0, 100);
+	dbg("%s - value = %d, retval = %d", __func__, value, retval);
+	return retval;
+}
+
+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_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int cflag;
+	unsigned char *buf;
+	int baud;
+	int i;
+	u8 control;
+	const int 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 };
+	int baud_floor, baud_ceil;
+	int k;
+
+	dbg("%s -  port %d", __func__, port->number);
+
+	/* The PL2303 is reported 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 */
+
+	if (old_termios && !tty_termios_hw_change(tty->termios, old_termios))
+		return;
+
+	cflag = tty->termios->c_cflag;
+
+	buf = kzalloc(7, GFP_KERNEL);
+	if (!buf) {
+		dev_err(&port->dev, "%s - out of memory.\n", __func__);
+		/* Report back no change occurred */
+		if (old_termios)
+			*tty->termios = *old_termios;
+		return;
+	}
+
+	i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			    GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+			    0, 0, buf, 7, 100);
+	dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
+	    buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		buf[6] = 5;
+		break;
+	case CS6:
+		buf[6] = 6;
+		break;
+	case CS7:
+		buf[6] = 7;
+		break;
+	default:
+	case CS8:
+		buf[6] = 8;
+		break;
+	}
+	dbg("%s - data bits = %d", __func__, buf[6]);
+
+	/* For reference buf[0]:buf[3] baud rate value */
+	/* NOTE: Only the values defined in baud_sup are supported !
+	 *       => if unsupported values are set, the PL2303 seems to use
+	 *          9600 baud (at least my PL2303X always does)
+	 */
+	baud = tty_get_baud_rate(tty);
+	dbg("%s - baud requested = %d", __func__, baud);
+	if (baud) {
+		/* Set baudrate to nearest supported value */
+		for (k=0; k<ARRAY_SIZE(baud_sup); k++) {
+			if (baud_sup[k] / baud) {
+				baud_ceil = baud_sup[k];
+				if (k==0) {
+					baud = baud_ceil;
+				} else {
+					baud_floor = baud_sup[k-1];
+					if ((baud_ceil % baud)
+					    > (baud % baud_floor))
+						baud = baud_floor;
+					else
+						baud = baud_ceil;
+				}
+				break;
+			}
+		}
+		if (baud > 1228800) {
+			/* type_0, type_1 only support up to 1228800 baud */
+			if (priv->type != HX)
+				baud = 1228800;
+			else if (baud > 6000000)
+				baud = 6000000;
+		}
+		dbg("%s - baud set = %d", __func__, baud);
+		if (baud <= 115200) {
+			buf[0] = baud & 0xff;
+			buf[1] = (baud >> 8) & 0xff;
+			buf[2] = (baud >> 16) & 0xff;
+			buf[3] = (baud >> 24) & 0xff;
+		} else {
+			/* apparently the formula for higher speeds is:
+			 * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
+			 */
+			unsigned tmp = 12*1000*1000*32 / baud;
+			buf[3] = 0x80;
+			buf[2] = 0;
+			buf[1] = (tmp >= 256);
+			while (tmp >= 256) {
+				tmp >>= 2;
+				buf[1] <<= 1;
+			}
+			buf[0] = tmp;
+		}
+	}
+
+	/* 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 (cflag & CSTOPB) {
+		/* NOTE: Comply with "real" UARTs / RS232:
+		 *       use 1.5 instead of 2 stop bits with 5 data bits
+		 */
+		if ((cflag & CSIZE) == CS5) {
+			buf[4] = 1;
+			dbg("%s - stop bits = 1.5", __func__);
+		} else {
+			buf[4] = 2;
+			dbg("%s - stop bits = 2", __func__);
+		}
+	} else {
+		buf[4] = 0;
+		dbg("%s - stop bits = 1", __func__);
+	}
+
+	if (cflag & PARENB) {
+		/* 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 (cflag & PARODD) {
+			if (cflag & CMSPAR) {
+				buf[5] = 3;
+				dbg("%s - parity = mark", __func__);
+			} else {
+				buf[5] = 1;
+				dbg("%s - parity = odd", __func__);
+			}
+		} else {
+			if (cflag & CMSPAR) {
+				buf[5] = 4;
+				dbg("%s - parity = space", __func__);
+			} else {
+				buf[5] = 2;
+				dbg("%s - parity = even", __func__);
+			}
+		}
+	} else {
+		buf[5] = 0;
+		dbg("%s - parity = none", __func__);
+	}
+
+	i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
+			    0, 0, buf, 7, 100);
+	dbg("0x21:0x20:0:0  %d", i);
+
+	/* change control lines if we are switching to or from B0 */
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	if ((cflag & CBAUD) == 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);
+		set_control_lines(serial->dev, control);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
+
+	i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			    GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+			    0, 0, buf, 7, 100);
+	dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
+	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+	if (cflag & CRTSCTS) {
+		if (priv->type == HX)
+			pl2303_vendor_write(0x0, 0x61, serial);
+		else
+			pl2303_vendor_write(0x0, 0x41, serial);
+	} else {
+		pl2303_vendor_write(0x0, 0x0, serial);
+	}
+
+	/* Save resulting baud rate */
+	if (baud)
+		tty_encode_baud_rate(tty, baud, baud);
+
+	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);
+	/* Change DTR and RTS */
+	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);
+	set_control_lines(port->serial->dev, control);
+}
+
+static void pl2303_close(struct usb_serial_port *port)
+{
+	dbg("%s - port %d", __func__, port->number);
+
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	int result;
+
+	dbg("%s -  port %d", __func__, port->number);
+
+	if (priv->type != HX) {
+		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(8, 0, serial);
+		pl2303_vendor_write(9, 0, serial);
+	}
+
+	/* Setup termios */
+	if (tty)
+		pl2303_set_termios(tty, port, NULL);
+
+	dbg("%s - submitting interrupt urb", __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;
+	}
+
+	result = usb_serial_generic_open(tty, port);
+	if (result) {
+		usb_kill_urb(port->interrupt_in_urb);
+		return result;
+	}
+
+	port->port.drain_delay = 256;
+	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;
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	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);
+
+	return set_control_lines(port->serial->dev, control);
+}
+
+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;
+
+	dbg("%s (%d)", __func__, port->number);
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	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);
+
+	dbg("%s - result = %x", __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 wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int prevstatus;
+	unsigned int status;
+	unsigned int changed;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prevstatus = priv->line_status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (1) {
+		interruptible_sleep_on(&port->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		if (port->serial->disconnected)
+			return -EIO;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->line_status;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prevstatus ^ status;
+
+		if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+		    ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+		    ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
+		    ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
+			return 0;
+		}
+		prevstatus = status;
+	}
+	/* NOTREACHED */
+	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;
+	dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		memset(&ser, 0, sizeof ser);
+		ser.type = PORT_16654;
+		ser.line = port->serial->minor;
+		ser.port = port->number;
+		ser.baud_base = 460800;
+
+		if (copy_to_user((void __user *)arg, &ser, sizeof ser))
+			return -EFAULT;
+
+		return 0;
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		return wait_modem_info(port, arg);
+	default:
+		dbg("%s not supported = 0x%04x", __func__, cmd);
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	u16 state;
+	int result;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (break_state == 0)
+		state = BREAK_OFF;
+	else
+		state = BREAK_ON;
+	dbg("%s - turning break %s", __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)
+		dbg("%s - error sending break = %d", __func__, result);
+}
+
+static void pl2303_release(struct usb_serial *serial)
+{
+	int i;
+	struct pl2303_private *priv;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+	}
+}
+
+static void pl2303_update_line_status(struct usb_serial_port *port,
+				      unsigned char *data,
+				      unsigned int actual_length)
+{
+
+	struct pl2303_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	unsigned long flags;
+	u8 status_idx = UART_STATE;
+	u8 length = UART_STATE + 1;
+	u8 prev_line_status;
+	u16 idv, idp;
+
+	idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
+	idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);
+
+
+	if (idv == SIEMENS_VENDOR_ID) {
+		if (idp == SIEMENS_PRODUCT_ID_X65 ||
+		    idp == SIEMENS_PRODUCT_ID_SX1 ||
+		    idp == SIEMENS_PRODUCT_ID_X75) {
+
+			length = 1;
+			status_idx = 0;
+		}
+	}
+
+	if (actual_length < length)
+		return;
+
+	/* Save off the uart status for others to look at */
+	spin_lock_irqsave(&priv->lock, flags);
+	prev_line_status = priv->line_status;
+	priv->line_status = data[status_idx];
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (priv->line_status & UART_BREAK_ERROR)
+		usb_serial_handle_break(port);
+	wake_up_interruptible(&port->delta_msr_wait);
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+	if ((priv->line_status ^ prev_line_status) & UART_DCD)
+		usb_serial_handle_dcd_change(port, tty,
+				priv->line_status & UART_DCD);
+	tty_kref_put(tty);
+}
+
+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;
+
+	dbg("%s (%d)", __func__, port->number);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __func__,
+		    status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __func__,
+		    status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &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(&urb->dev->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);
+	struct tty_struct *tty;
+	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);
+	wake_up_interruptible(&port->delta_msr_wait);
+
+	if (!urb->actual_length)
+		return;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		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;
+	dbg("%s - tty_flag = %d", __func__, tty_flag);
+
+	/* overrun is special, not associated with a char */
+	if (line_status & UART_OVERRUN_ERROR)
+		tty_insert_flip_char(tty, 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(tty, data[i], tty_flag);
+	} else {
+		tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+							urb->actual_length);
+	}
+
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+/* All of the device info needed for the PL2303 SIO serial converter */
+static struct usb_serial_driver pl2303_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"pl2303",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.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,
+	.process_read_urb =	pl2303_process_read_urb,
+	.read_int_callback =	pl2303_read_int_callback,
+	.attach =		pl2303_startup,
+	.release =		pl2303_release,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&pl2303_device, NULL
+};
+
+module_usb_serial_driver(pl2303_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.h
new file mode 100644
index 0000000..e3b7af8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/pl2303.h
@@ -0,0 +1,151 @@
+/*
+ * Prolific PL2303 USB to serial adaptor driver header file
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ */
+
+#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_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 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcaux.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcaux.c
new file mode 100644
index 0000000..018f103
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcaux.c
@@ -0,0 +1,98 @@
+/*
+ * Qualcomm USB Auxiliary Serial Port driver
+ *
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ * 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/init.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 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_driver qcaux_driver = {
+	.name =		"qcaux",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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(qcaux_driver, serial_drivers);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcserial.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcserial.c
new file mode 100644
index 0000000..9280e74
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/qcserial.c
@@ -0,0 +1,309 @@
+/*
+ * Qualcomm Serial USB driver
+ *
+ *	Copyright (c) 2008 QUALCOMM Incorporated.
+ *	Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ *	Copyright (c) 2009 Novell Inc.
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ */
+
+#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"
+
+static bool debug;
+
+#define DEVICE_G1K(v, p) \
+	USB_DEVICE(v, p), .driver_info = 1
+
+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(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(0x12D1, 0x14F0)},	/* Sony Gobi 3000 QDL */
+	{USB_DEVICE(0x12D1, 0x14F1)},	/* Sony Gobi 3000 Composite */
+	{USB_DEVICE(0x0AF0, 0x8120)},	/* Option GTM681W */
+	{ }				/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver qcdriver = {
+	.name			= "qcserial",
+	.probe			= usb_serial_probe,
+	.disconnect		= usb_serial_disconnect,
+	.id_table		= id_table,
+	.suspend		= usb_serial_suspend,
+	.resume			= usb_serial_resume,
+	.supports_autosuspend	= true,
+};
+
+static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+	struct usb_wwan_intf_private *data;
+	struct usb_host_interface *intf = serial->interface->cur_altsetting;
+	int retval = -ENODEV;
+	__u8 nintf;
+	__u8 ifnum;
+	bool is_gobi1k = id->driver_info ? true : false;
+
+	dbg("%s", __func__);
+	dbg("Is Gobi 1000 = %d", is_gobi1k);
+
+	nintf = serial->dev->actconfig->desc.bNumInterfaces;
+	dbg("Num Interfaces = %d", nintf);
+	ifnum = intf->desc.bInterfaceNumber;
+	dbg("This Interface = %d", ifnum);
+
+	data = kzalloc(sizeof(struct usb_wwan_intf_private),
+					 GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	spin_lock_init(&data->susp_lock);
+
+	switch (nintf) {
+	case 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)
+			break;
+
+		if (intf->desc.bNumEndpoints == 2 &&
+		    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
+		    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
+			dbg("QDL port found");
+
+			if (serial->interface->num_altsetting == 1) {
+				retval = 0; /* Success */
+				break;
+			}
+
+			retval = usb_set_interface(serial->dev, ifnum, 1);
+			if (retval < 0) {
+				dev_err(&serial->dev->dev,
+					"Could not set interface, error %d\n",
+					retval);
+				retval = -ENODEV;
+				kfree(data);
+			}
+		}
+		break;
+
+	case 3:
+	case 4:
+		/* Composite mode; don't bind to the QMI/net interface as that
+		 * gets handled by other drivers.
+		 */
+
+		/* Gobi 1K USB layout:
+		 * 0: serial port (doesn't respond)
+		 * 1: serial port (doesn't respond)
+		 * 2: AT-capable modem port
+		 * 3: QMI/net
+		 *
+		 * Gobi 2K+ USB layout:
+		 * 0: QMI/net
+		 * 1: DM/DIAG (use libqcdm from ModemManager for communication)
+		 * 2: AT-capable modem port
+		 * 3: NMEA
+		 */
+
+		if (ifnum == 1 && !is_gobi1k) {
+			dbg("Gobi 2K+ DM/DIAG interface found");
+			retval = usb_set_interface(serial->dev, ifnum, 0);
+			if (retval < 0) {
+				dev_err(&serial->dev->dev,
+					"Could not set interface, error %d\n",
+					retval);
+				retval = -ENODEV;
+				kfree(data);
+			}
+		} else if (ifnum == 2) {
+			dbg("Modem port found");
+			retval = usb_set_interface(serial->dev, ifnum, 0);
+			if (retval < 0) {
+				dev_err(&serial->dev->dev,
+					"Could not set interface, error %d\n",
+					retval);
+				retval = -ENODEV;
+				kfree(data);
+			}
+		} else if (ifnum==3 && !is_gobi1k) {
+			/*
+			 * NMEA (serial line 9600 8N1)
+			 * # echo "\$GPS_START" > /dev/ttyUSBx
+			 * # echo "\$GPS_STOP"  > /dev/ttyUSBx
+			 */
+			dbg("Gobi 2K+ NMEA GPS interface found");
+			retval = usb_set_interface(serial->dev, ifnum, 0);
+			if (retval < 0) {
+				dev_err(&serial->dev->dev,
+					"Could not set interface, error %d\n",
+					retval);
+				retval = -ENODEV;
+				kfree(data);
+			}
+		}
+		break;
+
+	default:
+		dev_err(&serial->dev->dev,
+			"unknown number of interfaces: %d\n", nintf);
+		kfree(data);
+		retval = -ENODEV;
+	}
+
+	/* Set serial->private if not returning -ENODEV */
+	if (retval != -ENODEV)
+		usb_set_serial_data(serial, data);
+	return retval;
+}
+
+static void qc_release(struct usb_serial *serial)
+{
+	struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+	dbg("%s", __func__);
+
+	/* Call usb_wwan release & free the private data allocated in qcprobe */
+	usb_wwan_release(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,
+	.write		     = usb_wwan_write,
+	.write_room	     = usb_wwan_write_room,
+	.chars_in_buffer     = usb_wwan_chars_in_buffer,
+	.attach		     = usb_wwan_startup,
+	.disconnect	     = usb_wwan_disconnect,
+	.release	     = qc_release,
+#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(qcdriver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/safe_serial.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/safe_serial.c
new file mode 100644
index 0000000..ae4ee30
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/safe_serial.c
@@ -0,0 +1,351 @@
+/*
+ * Safe Encapsulated USB Serial Driver
+ *
+ *      Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
+ *      Copyright (C) 2001 Lineo
+ *      Copyright (C) 2001 Hewlett-Packard
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/init.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>
+
+
+#ifndef CONFIG_USB_SERIAL_SAFE_PADDED
+#define CONFIG_USB_SERIAL_SAFE_PADDED 0
+#endif
+
+static bool debug;
+static bool safe = 1;
+static bool padded = CONFIG_USB_SERIAL_SAFE_PADDED;
+
+#define DRIVER_VERSION "v0.1"
+#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");
+
+static __u16 vendor;		/* no default */
+static __u16 product;		/* no default */
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)");
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "User specified USB idProduct (required)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+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 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 */
+	/* extra null entry for module vendor/produc parameters */
+	{MY_USB_DEVICE(0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},
+	{}			/* terminating entry  */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver safe_driver = {
+	.name =		"safe_serial",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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 __u16 __inline__ 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;
+	struct tty_struct *tty;
+	__u16 fcs;
+
+	if (!length)
+		return;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	if (!safe)
+		goto out;
+
+	fcs = fcs_compute10(data, length, CRC10_INITFCS);
+	if (fcs) {
+		dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
+		goto err;
+	}
+
+	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);
+		goto err;
+	}
+	dev_info(&urb->dev->dev, "%s - actual: %d\n", __func__, actual_length);
+	length = actual_length;
+out:
+	tty_insert_flip_string(tty, data, length);
+	tty_flip_buffer_push(tty);
+err:
+	tty_kref_put(tty);
+}
+
+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)
+{
+	switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) {
+	case LINEO_SAFESERIAL_CRC:
+		break;
+	case LINEO_SAFESERIAL_CRC_PADDED:
+		padded = 1;
+		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
+};
+
+static int __init safe_init(void)
+{
+	int i;
+
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+	       DRIVER_DESC "\n");
+
+	/* if we have vendor / product parameters patch them into id list */
+	if (vendor || product) {
+		printk(KERN_INFO KBUILD_MODNAME ": vendor: %x product: %x\n",
+		       vendor, product);
+
+		for (i = 0; i < ARRAY_SIZE(id_table); i++) {
+			if (!id_table[i].idVendor && !id_table[i].idProduct) {
+				id_table[i].idVendor = vendor;
+				id_table[i].idProduct = product;
+				break;
+			}
+		}
+	}
+
+	return usb_serial_register_drivers(&safe_driver, serial_drivers);
+}
+
+static void __exit safe_exit(void)
+{
+	usb_serial_deregister_drivers(&safe_driver, serial_drivers);
+}
+
+module_init(safe_init);
+module_exit(safe_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/siemens_mpi.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/siemens_mpi.c
new file mode 100644
index 0000000..46c0430
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/siemens_mpi.c
@@ -0,0 +1,56 @@
+/*
+ * Siemens USB-MPI Serial USB driver
+ *
+ * Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de>
+ * Copyright (C) 2005,2008 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* Version Information */
+#define DRIVER_VERSION "Version 0.1 09/26/2005"
+#define DRIVER_AUTHOR "Thomas Hergenhahn@web.de http://libnodave.sf.net"
+#define DRIVER_DESC "Driver for Siemens USB/MPI adapter"
+
+
+static const struct usb_device_id id_table[] = {
+	/* Vendor and product id for 6ES7-972-0CB20-0XA0 */
+	{ USB_DEVICE(0x908, 0x0004) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver siemens_usb_mpi_driver = {
+	.name =		"siemens_mpi",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver siemens_usb_mpi_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"siemens_mpi",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&siemens_usb_mpi_device, NULL
+};
+
+module_usb_serial_driver(siemens_usb_mpi_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/sierra.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/sierra.c
new file mode 100644
index 0000000..e3ddec0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/sierra.c
@@ -0,0 +1,1176 @@
+/*
+  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.
+
+  This driver is free software; you can redistribute it and/or modify
+  it under the terms of Version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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_VERSION "v.1.7.16"
+#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 debug;
+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)
+{
+	int result;
+	dev_dbg(&udev->dev, "%s\n", __func__);
+	result = 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 	     */
+	return result;
+}
+
+static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
+{
+	int result;
+	dev_dbg(&udev->dev, "%s\n", __func__);
+	result = 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       */
+	return result;
+}
+
+static int sierra_calc_num_ports(struct usb_serial *serial)
+{
+	int num_ports = 0;
+	u8 ifnum, numendpoints;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	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 int sierra_calc_interface(struct usb_serial *serial)
+{
+	int interface;
+	struct usb_interface *p_interface;
+	struct usb_host_interface *p_host_interface;
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	/* Get the interface structure pointer from the serial struct */
+	p_interface = serial->interface;
+
+	/* Get a pointer to the host interface structure */
+	p_host_interface = p_interface->cur_altsetting;
+
+	/* read the interface descriptor for this active altsetting
+	 * to find out the interface number we are on
+	*/
+	interface = p_host_interface->desc.bInterfaceNumber;
+
+	return interface;
+}
+
+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;
+	dev_dbg(&udev->dev, "%s\n", __func__);
+
+	ifnum = sierra_calc_interface(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);
+	}
+
+	/* ifnum could have changed - by calling usb_set_interface */
+	ifnum = sierra_calc_interface(serial);
+
+	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 */
+	{ USB_DEVICE(0x1199, 0x68A2),   /* Sierra Wireless MC77xx in QMI mode */
+	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+	},
+	/* 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
+	},
+	/* 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;
+	unsigned int opened:1;
+};
+
+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;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	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_calc_interface(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_rcvctrlpipe(serial->dev, 0),
+		0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	usb_autopm_put_interface(serial->interface);
+
+	return retval;
+}
+
+static void sierra_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios)
+{
+	dev_dbg(&port->dev, "%s\n", __func__);
+	tty_termios_copy_hw(tty->termios, old_termios);
+	sierra_send_setup(port);
+}
+
+static int sierra_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	unsigned int value;
+	struct sierra_port_private *portdata;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+	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)
+{
+	struct usb_serial_port *port;
+	if (urb) {
+		port =  urb->context;
+		dev_dbg(&port->dev, "%s: %p\n", __func__, 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;
+
+	dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
+	intfdata = port->serial->private;
+
+	/* 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(&portdata->lock);
+	--portdata->outstanding_urbs;
+	spin_unlock(&portdata->lock);
+	spin_lock(&intfdata->susp_lock);
+	--intfdata->in_flight;
+	spin_unlock(&intfdata->susp_lock);
+
+	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 = serial->private;
+
+	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) {
+		dev_err(&port->dev, "out of memory\n");
+		retval = -ENOMEM;
+		goto error_no_buffer;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(&port->dev, "no more free urbs\n");
+		retval = -ENOMEM;
+		goto error_no_urb;
+	}
+
+	memcpy(buffer, buf, writesize);
+
+	usb_serial_debug_data(debug, &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;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+	port = urb->context;
+
+	dev_dbg(&port->dev, "%s: %p\n", __func__, urb);
+
+	if (status) {
+		dev_dbg(&port->dev, "%s: nonzero status: %d on"
+			" endpoint %02x\n", __func__, status, endpoint);
+	} else {
+		if (urb->actual_length) {
+			tty = tty_port_tty_get(&port->port);
+			if (tty) {
+				tty_insert_flip_string(tty, data,
+					urb->actual_length);
+				tty_flip_buffer_push(tty);
+
+				tty_kref_put(tty);
+				usb_serial_debug_data(debug, &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));
+			struct tty_struct *tty;
+
+			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);
+
+			tty = tty_port_tty_get(&port->port);
+			if (tty && !C_CLOCAL(tty) &&
+					old_dcd_state && !portdata->dcd_state)
+				tty_hangup(tty);
+			tty_kref_put(tty);
+		} 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;
+
+	dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
+
+	/* 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 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;
+
+	if (endpoint == -1)
+		return NULL;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (urb == NULL) {
+		dev_dbg(&serial->dev->dev, "%s: alloc for endpoint %d failed\n",
+			__func__, endpoint);
+		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);
+
+		/* debug */
+		dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
+				dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
+	} else {
+		dev_dbg(&serial->dev->dev, "%s %c u:%p d:%p\n", __func__,
+				dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
+
+		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 = port->serial->private;
+	struct urb *urb;
+
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+	portdata = usb_get_serial_port_data(port);
+
+	portdata->rts_state = 0;
+	portdata->dtr_state = 0;
+
+	if (serial->dev) {
+		mutex_lock(&serial->disc_mutex);
+		if (!serial->disconnected) {
+			/* odd error handling due to pm counters */
+			if (!usb_autopm_get_interface(serial->interface))
+				sierra_send_setup(port);
+			else
+				usb_autopm_get_interface_no_resume(serial->interface);
+				
+		}
+		mutex_unlock(&serial->disc_mutex);
+		spin_lock_irq(&intfdata->susp_lock);
+		portdata->opened = 0;
+		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(&portdata->lock);
+			portdata->outstanding_urbs--;
+			spin_unlock(&portdata->lock);
+		}
+
+		/* Stop reading urbs */
+		sierra_stop_rx_urbs(port);
+		/* .. and release them */
+		for (i = 0; i < portdata->num_in_urbs; i++) {
+			sierra_release_urb(portdata->in_urbs[i]);
+			portdata->in_urbs[i] = NULL;
+		}
+	}
+}
+
+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 = serial->private;
+	int i;
+	int err;
+	int endpoint;
+	struct urb *urb;
+
+	portdata = usb_get_serial_port_data(port);
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	/* Set some sane defaults */
+	portdata->rts_state = 1;
+	portdata->dtr_state = 1;
+
+
+	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;
+
+	sierra_send_setup(port);
+
+	spin_lock_irq(&intfdata->susp_lock);
+	portdata->opened = 1;
+	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 usb_serial_port *port;
+	struct sierra_intf_private *intfdata;
+	struct sierra_port_private *portdata;
+	struct sierra_iface_info *himemoryp = NULL;
+	int i;
+	u8 ifnum;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	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);
+
+	/* Now setup per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+		if (!portdata) {
+			dev_dbg(&port->dev, "%s: kmalloc for "
+				"sierra_port_private (%d) failed!\n",
+				__func__, i);
+			goto err;
+		}
+		spin_lock_init(&portdata->lock);
+		init_usb_anchor(&portdata->active);
+		init_usb_anchor(&portdata->delayed);
+		ifnum = i;
+		/* 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_calc_interface(serial);
+			himemoryp =
+			    (struct sierra_iface_info *)&typeB_interface_list;
+			if (is_himemory(ifnum, himemoryp)) {
+				portdata->num_out_urbs = N_OUT_URB_HM;
+				portdata->num_in_urbs  = N_IN_URB_HM;
+			}
+		}
+		else {
+			himemoryp =
+			    (struct sierra_iface_info *)&typeA_interface_list;
+			if (is_himemory(i, himemoryp)) {
+				portdata->num_out_urbs = N_OUT_URB_HM;
+				portdata->num_in_urbs  = N_IN_URB_HM;
+			}
+		}
+		dev_dbg(&serial->dev->dev,
+			"Memory usage (urbs) interface #%d, in=%d, out=%d\n",
+			ifnum,portdata->num_in_urbs, portdata->num_out_urbs );
+		/* Set the port private data pointer */
+		usb_set_serial_port_data(port, portdata);
+	}
+
+	return 0;
+err:
+	for (--i; i >= 0; --i) {
+		portdata = usb_get_serial_port_data(serial->port[i]);
+		kfree(portdata);
+	}
+	kfree(intfdata);
+
+	return -ENOMEM;
+}
+
+static void sierra_release(struct usb_serial *serial)
+{
+	int i;
+	struct usb_serial_port *port;
+	struct sierra_port_private *portdata;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		if (!port)
+			continue;
+		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+		usb_set_serial_port_data(port, NULL);
+		kfree(portdata);
+	}
+	kfree(serial->private);
+}
+
+#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;
+	int b;
+
+	if (PMSG_IS_AUTO(message)) {
+		intfdata = serial->private;
+		spin_lock_irq(&intfdata->susp_lock);
+		b = intfdata->in_flight;
+
+		if (b) {
+			spin_unlock_irq(&intfdata->susp_lock);
+			return -EBUSY;
+		} else {
+			intfdata->suspended = 1;
+			spin_unlock_irq(&intfdata->susp_lock);
+		}
+	}
+	stop_read_write_urbs(serial);
+
+	return 0;
+}
+
+static int sierra_resume(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	struct sierra_intf_private *intfdata = serial->private;
+	struct sierra_port_private *portdata;
+	struct urb *urb;
+	int ec = 0;
+	int i, err;
+
+	spin_lock_irq(&intfdata->susp_lock);
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+
+		if (!portdata)
+			continue;
+
+		while ((urb = usb_get_from_anchor(&portdata->delayed))) {
+			usb_anchor_urb(urb, &portdata->active);
+			intfdata->in_flight++;
+			err = usb_submit_urb(urb, GFP_ATOMIC);
+			if (err < 0) {
+				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);
+				continue;
+			}
+		}
+
+		if (portdata->opened) {
+			err = sierra_submit_rx_urbs(port, GFP_ATOMIC);
+			if (err)
+				ec++;
+		}
+	}
+	intfdata->suspended = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
+	return ec ? -EIO : 0;
+}
+
+static int sierra_reset_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	dev_err(&serial->dev->dev, "%s\n", __func__);
+	return usb_serial_resume(intf);
+}
+#else
+#define sierra_suspend NULL
+#define sierra_resume NULL
+#define sierra_reset_resume NULL
+#endif
+
+static struct usb_driver sierra_driver = {
+	.name       = "sierra",
+	.probe      = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+	.suspend    = usb_serial_suspend,
+	.resume     = usb_serial_resume,
+	.reset_resume = sierra_reset_resume,
+	.id_table   = id_table,
+	.supports_autosuspend =	1,
+};
+
+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,
+	.set_termios       = sierra_set_termios,
+	.tiocmget          = sierra_tiocmget,
+	.tiocmset          = sierra_tiocmset,
+	.attach            = sierra_startup,
+	.release           = sierra_release,
+	.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(sierra_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(nmea, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nmea, "NMEA streaming");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/spcp8x5.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/spcp8x5.c
new file mode 100644
index 0000000..692da69
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/spcp8x5.c
@@ -0,0 +1,673 @@
+/*
+ * spcp8x5 USB to serial adaptor driver
+ *
+ * Copyright (C) 2010 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>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *
+ */
+#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/usb.h>
+#include <linux/usb/serial.h>
+
+
+/* Version Information */
+#define DRIVER_VERSION	"v0.10"
+#define DRIVER_DESC 	"SPCP8x5 USB to serial adaptor driver"
+
+static bool debug;
+
+#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)},
+	{ }					/* 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
+
+enum spcp8x5_type {
+	SPCP825_007_TYPE,
+	SPCP825_008_TYPE,
+	SPCP825_PHILIP_TYPE,
+	SPCP825_INTERMATIC_TYPE,
+	SPCP835_TYPE,
+};
+
+static struct usb_driver spcp8x5_driver = {
+	.name =			"spcp8x5",
+	.probe =		usb_serial_probe,
+	.disconnect =		usb_serial_disconnect,
+	.id_table =		id_table,
+};
+
+
+struct spcp8x5_private {
+	spinlock_t 	lock;
+	enum spcp8x5_type	type;
+	u8 			line_control;
+	u8 			line_status;
+};
+
+/* desc : when device plug in,this function would be called.
+ * thanks to usb_serial subsystem,then do almost every things for us. And what
+ * we should do just alloc the buffer */
+static int spcp8x5_startup(struct usb_serial *serial)
+{
+	struct spcp8x5_private *priv;
+	int i;
+	enum spcp8x5_type type = SPCP825_007_TYPE;
+	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+
+	if (product == 0x0201)
+		type = SPCP825_007_TYPE;
+	else if (product == 0x0231)
+		type = SPCP835_TYPE;
+	else if (product == 0x0235)
+		type = SPCP825_008_TYPE;
+	else if (product == 0x0204)
+		type = SPCP825_INTERMATIC_TYPE;
+	else if (product == 0x0471 &&
+		 serial->dev->descriptor.idVendor == cpu_to_le16(0x081e))
+		type = SPCP825_PHILIP_TYPE;
+	dev_dbg(&serial->dev->dev, "device type = %d\n", (int)type);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kzalloc(sizeof(struct spcp8x5_private), GFP_KERNEL);
+		if (!priv)
+			goto cleanup;
+
+		spin_lock_init(&priv->lock);
+		priv->type = type;
+		usb_set_serial_port_data(serial->port[i] , priv);
+	}
+
+	return 0;
+cleanup:
+	for (--i; i >= 0; --i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i] , NULL);
+	}
+	return -ENOMEM;
+}
+
+/* call when the device plug out. free all the memory alloced by probe */
+static void spcp8x5_release(struct usb_serial *serial)
+{
+	int i;
+
+	for (i = 0; i < serial->num_ports; i++)
+		kfree(usb_get_serial_port_data(serial->port[i]));
+}
+
+/* set the modem control line of the device.
+ * NOTE spcp825-007 not supported this */
+static int spcp8x5_set_ctrlLine(struct usb_device *dev, u8 value,
+				enum spcp8x5_type type)
+{
+	int retval;
+	u8 mcr = 0 ;
+
+	if (type == SPCP825_007_TYPE)
+		return -EPERM;
+
+	mcr = (unsigned short)value;
+	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_dbg(&dev->dev, "usb_control_msg return %#x\n", retval);
+	return retval;
+}
+
+/* get the modem status register of the device
+ * NOTE spcp825-007 not supported this */
+static int spcp8x5_get_msr(struct usb_device *dev, u8 *status,
+			   enum spcp8x5_type type)
+{
+	u8 *status_buffer;
+	int ret;
+
+	/* I return Permited not support here but seem inval device
+	 * is more fix */
+	if (type == SPCP825_007_TYPE)
+		return -EPERM;
+	if (status == NULL)
+		return -EINVAL;
+
+	status_buffer = kmalloc(1, GFP_KERNEL);
+	if (!status_buffer)
+		return -ENOMEM;
+	status_buffer[0] = status[0];
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			      GET_UART_STATUS, GET_UART_STATUS_TYPE,
+			      0, GET_UART_STATUS_MSR, status_buffer, 1, 100);
+	if (ret < 0)
+		dev_dbg(&dev->dev, "Get MSR = 0x%p failed (error = %d)",
+			status_buffer, ret);
+
+	dev_dbg(&dev->dev, "0xc0:0x22:0:6  %d - 0x%p ", ret, status_buffer);
+	status[0] = status_buffer[0];
+	kfree(status_buffer);
+
+	return ret;
+}
+
+/* select the work mode.
+ * NOTE this function not supported by spcp825-007 */
+static void spcp8x5_set_workMode(struct usb_device *dev, u16 value,
+				 u16 index, enum spcp8x5_type type)
+{
+	int ret;
+
+	/* I return Permited not support here but seem inval device
+	 * is more fix */
+	if (type == SPCP825_007_TYPE)
+		return;
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			      SET_WORKING_MODE_TYPE, SET_WORKING_MODE,
+			      value, index, NULL, 0, 100);
+	dev_dbg(&dev->dev, "value = %#x , index = %#x\n", value, index);
+	if (ret < 0)
+		dev_dbg(&dev->dev,
+			"RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret);
+}
+
+static int spcp8x5_carrier_raised(struct usb_serial_port *port)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	if (priv->line_status & 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_ctrlLine(port->serial->dev, control , priv->type);
+}
+
+static void spcp8x5_init_termios(struct tty_struct *tty)
+{
+	/* for the 1st time call this function */
+	*(tty->termios) = tty_std_termios;
+	tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty->termios->c_ispeed = 115200;
+	tty->termios->c_ospeed = 115200;
+}
+
+/* set the serial param for transfer. we should check if we really need to
+ * transfer. if we set flow control we should do this too. */
+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_ctrlLine(serial->dev, control , priv->type);
+	} 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, "spcp825 driver does not support the "
+			"baudrate requested, using default of 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);
+	dbg("0x21:0x40:0:0  %d", i);
+
+	if (cflag & CRTSCTS) {
+		/* enable hardware flow control */
+		spcp8x5_set_workMode(serial->dev, 0x000a,
+				     SET_WORKING_MODE_U2C, priv->type);
+	}
+}
+
+/* open the serial port. do some usb system call. set termios and get the line
+ * status of the device. */
+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;
+	unsigned long flags;
+	u8 status = 0x30;
+	/* status 0x30 means DSR and CTS = 1 other CDC RI and delta = 0 */
+
+	dbg("%s -  port %d", __func__, port->number);
+
+	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_ctrlLine(serial->dev, priv->line_control , priv->type);
+
+	/* Setup termios */
+	if (tty)
+		spcp8x5_set_termios(tty, port, NULL);
+
+	spcp8x5_get_msr(serial->dev, &status, priv->type);
+
+	/* may be we should update uart status here but now we did not do */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->line_status = status & 0xf0 ;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	port->port.drain_delay = 256;
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static void spcp8x5_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	u8 status;
+	char tty_flag;
+
+	/* get tty_flag from status */
+	tty_flag = TTY_NORMAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	status = priv->line_status;
+	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	/* wake up the wait for termios */
+	wake_up_interruptible(&port->delta_msr_wait);
+
+	if (!urb->actual_length)
+		return;
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	if (status & UART_STATE_TRANSIENT_MASK) {
+		/* break takes precedence over parity, which takes precedence
+		 * over framing errors */
+		if (status & UART_BREAK_ERROR)
+			tty_flag = TTY_BREAK;
+		else if (status & UART_PARITY_ERROR)
+			tty_flag = TTY_PARITY;
+		else if (status & UART_FRAME_ERROR)
+			tty_flag = TTY_FRAME;
+		dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag);
+
+		/* overrun is special, not associated with a char */
+		if (status & UART_OVERRUN_ERROR)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+		if (status & UART_DCD)
+			usb_serial_handle_dcd_change(port, tty,
+				   priv->line_status & MSR_STATUS_LINE_DCD);
+	}
+
+	tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+							urb->actual_length);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static int spcp8x5_wait_modem_info(struct usb_serial_port *port,
+				   unsigned int arg)
+{
+	struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int prevstatus;
+	unsigned int status;
+	unsigned int changed;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prevstatus = priv->line_status;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (1) {
+		/* wake up in bulk read */
+		interruptible_sleep_on(&port->delta_msr_wait);
+
+		/* see if a signal did it */
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		if (port->serial->disconnected)
+			return -EIO;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->line_status;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prevstatus^status;
+
+		if (((arg & TIOCM_RNG) && (changed & MSR_STATUS_LINE_RI)) ||
+		    ((arg & TIOCM_DSR) && (changed & MSR_STATUS_LINE_DSR)) ||
+		    ((arg & TIOCM_CD)  && (changed & MSR_STATUS_LINE_DCD)) ||
+		    ((arg & TIOCM_CTS) && (changed & MSR_STATUS_LINE_CTS)))
+			return 0;
+
+		prevstatus = status;
+	}
+	/* NOTREACHED */
+	return 0;
+}
+
+static int spcp8x5_ioctl(struct tty_struct *tty,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+		return spcp8x5_wait_modem_info(port, arg);
+
+	default:
+		dbg("%s not supported = 0x%04x", __func__, cmd);
+		break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+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_ctrlLine(port->serial->dev, control , priv->type);
+}
+
+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;
+	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 & 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;
+}
+
+/* All of the device info needed for the spcp8x5 SIO serial converter */
+static struct usb_serial_driver spcp8x5_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"SPCP8x5",
+	},
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.open 			= spcp8x5_open,
+	.dtr_rts		= spcp8x5_dtr_rts,
+	.carrier_raised		= spcp8x5_carrier_raised,
+	.set_termios 		= spcp8x5_set_termios,
+	.init_termios		= spcp8x5_init_termios,
+	.ioctl 			= spcp8x5_ioctl,
+	.tiocmget 		= spcp8x5_tiocmget,
+	.tiocmset 		= spcp8x5_tiocmset,
+	.attach 		= spcp8x5_startup,
+	.release 		= spcp8x5_release,
+	.process_read_urb	= spcp8x5_process_read_urb,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&spcp8x5_device, NULL
+};
+
+module_usb_serial_driver(spcp8x5_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ssu100.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ssu100.c
new file mode 100644
index 0000000..f425507
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ssu100.c
@@ -0,0 +1,700 @@
+/*
+ * 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/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/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
+
+static bool debug;
+
+/* Version Information */
+#define DRIVER_VERSION "v0.1"
+#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);
+
+
+static struct usb_driver ssu100_driver = {
+	.name			       = "ssu100",
+	.probe			       = usb_serial_probe,
+	.disconnect		       = usb_serial_disconnect,
+	.id_table		       = id_table,
+	.suspend		       = usb_serial_suspend,
+	.resume			       = usb_serial_resume,
+	.supports_autosuspend	       = 1,
+};
+
+struct ssu100_port_private {
+	spinlock_t status_lock;
+	u8 shadowLSR;
+	u8 shadowMSR;
+	struct async_icount icount;
+};
+
+static void ssu100_release(struct usb_serial *serial)
+{
+	struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port);
+
+	dbg("%s", __func__);
+	kfree(priv);
+}
+
+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)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			       QT_SET_GET_DEVICE, 0xc0, 0, 0,
+			       data, 3, 300);
+}
+
+static inline int ssu100_getregister(struct usb_device *dev,
+				     unsigned short uart,
+				     unsigned short reg,
+				     u8 *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			       QT_SET_GET_REGISTER, 0xc0, reg,
+			       uart, data, sizeof(*data), 300);
+
+}
+
+
+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) {
+		dbg("%s - DTR|RTS not being set|cleared", __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)
+		dbg("%s Error from MODEM_CTRL urb", __func__);
+
+	return result;
+}
+
+static int ssu100_initdevice(struct usb_device *dev)
+{
+	u8 *data;
+	int result = 0;
+
+	dbg("%s", __func__);
+
+	data = kzalloc(3, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	result = ssu100_getdevice(dev, data);
+	if (result < 0) {
+		dbg("%s - get_device failed %i", __func__, result);
+		goto out;
+	}
+
+	data[1] &= ~FULLPWRBIT;
+
+	result = ssu100_setdevice(dev, data);
+	if (result < 0) {
+		dbg("%s - setdevice failed %i", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
+	if (result < 0) {
+		dbg("%s - set prebuffer level failed %i", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
+	if (result < 0) {
+		dbg("%s - set ATFprebuffer level failed %i", __func__, result);
+		goto out;
+	}
+
+	result = ssu100_getdevice(dev, data);
+	if (result < 0) {
+		dbg("%s - get_device failed %i", __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) {
+		dbg("%s - setdevice failed %i", __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;
+
+	dbg("%s", __func__);
+
+	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;
+
+	dbg("%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)
+		dbg("%s - set uart failed", __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)
+		dbg("%s - set HW flow control failed", __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)
+		dbg("%s - set SW flow control failed", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 < 0) {
+		dbg("%s - open failed %i", __func__, result);
+		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)
+		dbg("%s - set uart failed", __func__);
+
+	if (tty)
+		ssu100_set_termios(tty, port, tty->termios);
+
+	return usb_serial_generic_open(tty, port);
+}
+
+static void ssu100_close(struct usb_serial_port *port)
+{
+	dbg("%s", __func__);
+	usb_serial_generic_close(port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line		= port->serial->minor;
+	tmp.port		= 0;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+	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 wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+	struct async_icount prev, cur;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->status_lock, flags);
+	prev = priv->icount;
+	spin_unlock_irqrestore(&priv->status_lock, flags);
+
+	while (1) {
+		wait_event_interruptible(port->delta_msr_wait,
+					 (port->serial->disconnected ||
+					  (priv->icount.rng != prev.rng) ||
+					  (priv->icount.dsr != prev.dsr) ||
+					  (priv->icount.dcd != prev.dcd) ||
+					  (priv->icount.cts != prev.cts)));
+
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		if (port->serial->disconnected)
+			return -EIO;
+
+		spin_lock_irqsave(&priv->status_lock, flags);
+		cur = priv->icount;
+		spin_unlock_irqrestore(&priv->status_lock, flags);
+
+		if ((prev.rng == cur.rng) &&
+		    (prev.dsr == cur.dsr) &&
+		    (prev.dcd == cur.dcd) &&
+		    (prev.cts == cur.cts))
+			return -EIO;
+
+		if ((arg & TIOCM_RNG && (prev.rng != cur.rng)) ||
+		    (arg & TIOCM_DSR && (prev.dsr != cur.dsr)) ||
+		    (arg & TIOCM_CD  && (prev.dcd != cur.dcd)) ||
+		    (arg & TIOCM_CTS && (prev.cts != cur.cts)))
+			return 0;
+	}
+	return 0;
+}
+
+static int ssu100_get_icount(struct tty_struct *tty,
+			struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+	struct async_icount cnow = priv->icount;
+
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	return 0;
+}
+
+
+
+static int ssu100_ioctl(struct tty_struct *tty,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dbg("%s cmd 0x%04x", __func__, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *) arg);
+
+	case TIOCMIWAIT:
+		return wait_modem_info(port, arg);
+
+	default:
+		break;
+	}
+
+	dbg("%s arg not supported", __func__);
+
+	return -ENOIOCTLCMD;
+}
+
+static int ssu100_attach(struct usb_serial *serial)
+{
+	struct ssu100_port_private *priv;
+	struct usb_serial_port *port = *serial->port;
+
+	dbg("%s", __func__);
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,
+			sizeof(*priv));
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&priv->status_lock);
+	usb_set_serial_port_data(port, priv);
+
+	return ssu100_initdevice(serial->dev);
+}
+
+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;
+
+	dbg("%s\n", __func__);
+
+	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;
+
+	dbg("%s\n", __func__);
+	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;
+
+	dbg("%s\n", __func__);
+
+	/* 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)
+			priv->icount.cts++;
+		if (msr & UART_MSR_DDSR)
+			priv->icount.dsr++;
+		if (msr & UART_MSR_DDCD)
+			priv->icount.dcd++;
+		if (msr & UART_MSR_TERI)
+			priv->icount.rng++;
+		wake_up_interruptible(&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) {
+			priv->icount.brk++;
+			*tty_flag = TTY_BREAK;
+			usb_serial_handle_break(port);
+		}
+		if (lsr & UART_LSR_PE) {
+			priv->icount.parity++;
+			if (*tty_flag == TTY_NORMAL)
+				*tty_flag = TTY_PARITY;
+		}
+		if (lsr & UART_LSR_FE) {
+			priv->icount.frame++;
+			if (*tty_flag == TTY_NORMAL)
+				*tty_flag = TTY_FRAME;
+		}
+		if (lsr & UART_LSR_OE) {
+			priv->icount.overrun++;
+			tty_insert_flip_char(tty_port_tty_get(&port->port),
+					0, TTY_OVERRUN);
+		}
+	}
+
+}
+
+static int ssu100_process_packet(struct urb *urb,
+				 struct tty_struct *tty)
+{
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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 0;	/* 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(tty, *ch, flag);
+		}
+	} else
+		tty_insert_flip_string_fixed_flag(tty, ch, flag, len);
+
+	return len;
+}
+
+static void ssu100_process_read_urb(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct tty_struct *tty;
+	int count;
+
+	dbg("%s", __func__);
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	count = ssu100_process_packet(urb, tty);
+
+	if (count)
+		tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+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,
+	.close		     = ssu100_close,
+	.attach              = ssu100_attach,
+	.release             = ssu100_release,
+	.dtr_rts             = ssu100_dtr_rts,
+	.process_read_urb    = ssu100_process_read_urb,
+	.tiocmget            = ssu100_tiocmget,
+	.tiocmset            = ssu100_tiocmset,
+	.get_icount	     = ssu100_get_icount,
+	.ioctl               = ssu100_ioctl,
+	.set_termios         = ssu100_set_termios,
+	.disconnect          = usb_serial_generic_disconnect,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&ssu100_device, NULL
+};
+
+module_usb_serial_driver(ssu100_driver, serial_drivers);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/symbolserial.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/symbolserial.c
new file mode 100644
index 0000000..1a5be13
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/symbolserial.c
@@ -0,0 +1,317 @@
+/*
+ * Symbol USB barcode to serial driver
+ *
+ * Copyright (C) 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2009 Novell Inc.
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.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 bool debug;
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x05e0, 0x0600) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* This structure holds all of the individual device information */
+struct symbol_private {
+	struct usb_device *udev;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	unsigned char *int_buffer;
+	struct urb *int_urb;
+	int buffer_size;
+	u8 bInterval;
+	u8 int_address;
+	spinlock_t lock;	/* protects the following flags */
+	bool throttled;
+	bool actually_throttled;
+	bool rts;
+};
+
+static void symbol_int_callback(struct urb *urb)
+{
+	struct symbol_private *priv = urb->context;
+	unsigned char *data = urb->transfer_buffer;
+	struct usb_serial_port *port = priv->port;
+	int status = urb->status;
+	struct tty_struct *tty;
+	int result;
+	int data_length;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __func__, status);
+		goto exit;
+	}
+
+	usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
+			      data);
+
+	if (urb->actual_length > 1) {
+		data_length = urb->actual_length - 1;
+
+		/*
+		 * Data from the device comes with a 1 byte header:
+		 *
+		 * <size of data>data...
+		 * 	This is real data to be sent to the tty layer
+		 * we pretty much just ignore the size and send everything
+		 * else to the tty layer.
+		 */
+		tty = tty_port_tty_get(&port->port);
+		if (tty) {
+			tty_insert_flip_string(tty, &data[1], data_length);
+			tty_flip_buffer_push(tty);
+			tty_kref_put(tty);
+		}
+	} else {
+		dev_dbg(&priv->udev->dev,
+			"Improper amount of data received from the device, "
+			"%d bytes", urb->actual_length);
+	}
+
+exit:
+	spin_lock(&priv->lock);
+
+	/* Continue trying to always read if we should */
+	if (!priv->throttled) {
+		usb_fill_int_urb(priv->int_urb, priv->udev,
+				 usb_rcvintpipe(priv->udev,
+				 		priv->int_address),
+				 priv->int_buffer, priv->buffer_size,
+				 symbol_int_callback, priv, priv->bInterval);
+		result = usb_submit_urb(priv->int_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(&priv->lock);
+}
+
+static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	struct symbol_private *priv = usb_get_serial_data(port->serial);
+	unsigned long flags;
+	int result = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->throttled = false;
+	priv->actually_throttled = false;
+	priv->port = port;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Start reading from the device */
+	usb_fill_int_urb(priv->int_urb, priv->udev,
+			 usb_rcvintpipe(priv->udev, priv->int_address),
+			 priv->int_buffer, priv->buffer_size,
+			 symbol_int_callback, priv, priv->bInterval);
+	result = usb_submit_urb(priv->int_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)
+{
+	struct symbol_private *priv = usb_get_serial_data(port->serial);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* shutdown our urbs */
+	usb_kill_urb(priv->int_urb);
+}
+
+static void symbol_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct symbol_private *priv = usb_get_serial_data(port->serial);
+
+	dbg("%s - port %d", __func__, port->number);
+	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_data(port->serial);
+	int result;
+	bool was_throttled;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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(priv->int_urb, GFP_KERNEL);
+		if (result)
+			dev_err(&port->dev,
+				"%s - failed submitting read urb, error %d\n",
+							__func__, result);
+	}
+}
+
+static int symbol_startup(struct usb_serial *serial)
+{
+	struct symbol_private *priv;
+	struct usb_host_interface *intf;
+	int i;
+	int retval = -ENOMEM;
+	bool int_in_found = false;
+
+	/* create our private serial structure */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	spin_lock_init(&priv->lock);
+	priv->serial = serial;
+	priv->port = serial->port[0];
+	priv->udev = serial->dev;
+
+	/* find our interrupt endpoint */
+	intf = serial->interface->altsetting;
+	for (i = 0; i < intf->desc.bNumEndpoints; ++i) {
+		struct usb_endpoint_descriptor *endpoint;
+
+		endpoint = &intf->endpoint[i].desc;
+		if (!usb_endpoint_is_int_in(endpoint))
+			continue;
+
+		priv->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!priv->int_urb) {
+			dev_err(&priv->udev->dev, "out of memory\n");
+			goto error;
+		}
+
+		priv->buffer_size = usb_endpoint_maxp(endpoint) * 2;
+		priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
+		if (!priv->int_buffer) {
+			dev_err(&priv->udev->dev, "out of memory\n");
+			goto error;
+		}
+
+		priv->int_address = endpoint->bEndpointAddress;
+		priv->bInterval = endpoint->bInterval;
+
+		/* set up our int urb */
+		usb_fill_int_urb(priv->int_urb, priv->udev,
+				 usb_rcvintpipe(priv->udev,
+				 		endpoint->bEndpointAddress),
+				 priv->int_buffer, priv->buffer_size,
+				 symbol_int_callback, priv, priv->bInterval);
+
+		int_in_found = true;
+		break;
+		}
+
+	if (!int_in_found) {
+		dev_err(&priv->udev->dev,
+			"Error - the proper endpoints were not found!\n");
+		goto error;
+	}
+
+	usb_set_serial_data(serial, priv);
+	return 0;
+
+error:
+	usb_free_urb(priv->int_urb);
+	kfree(priv->int_buffer);
+	kfree(priv);
+	return retval;
+}
+
+static void symbol_disconnect(struct usb_serial *serial)
+{
+	struct symbol_private *priv = usb_get_serial_data(serial);
+
+	dbg("%s", __func__);
+
+	usb_kill_urb(priv->int_urb);
+	usb_free_urb(priv->int_urb);
+}
+
+static void symbol_release(struct usb_serial *serial)
+{
+	struct symbol_private *priv = usb_get_serial_data(serial);
+
+	dbg("%s", __func__);
+
+	kfree(priv->int_buffer);
+	kfree(priv);
+}
+
+static struct usb_driver symbol_driver = {
+	.name =			"symbol",
+	.probe =		usb_serial_probe,
+	.disconnect =		usb_serial_disconnect,
+	.id_table =		id_table,
+};
+
+static struct usb_serial_driver symbol_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"symbol",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+	.attach =		symbol_startup,
+	.open =			symbol_open,
+	.close =		symbol_close,
+	.disconnect =		symbol_disconnect,
+	.release =		symbol_release,
+	.throttle = 		symbol_throttle,
+	.unthrottle =		symbol_unthrottle,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&symbol_device, NULL
+};
+
+module_usb_serial_driver(symbol_driver, serial_drivers);
+
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.c
new file mode 100644
index 0000000..2575779
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.c
@@ -0,0 +1,1755 @@
+/* vi: ts=8 sw=8
+ *
+ * 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
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/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/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>
+
+#include "ti_usb_3410_5052.h"
+
+/* Defines */
+
+#define TI_DRIVER_VERSION	"v0.10"
+#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_WRITE_BUF_SIZE	1024
+
+#define TI_TRANSFER_TIMEOUT	2
+
+#define TI_DEFAULT_CLOSING_WAIT	4000		/* in .01 secs */
+
+/* supported setserial flags */
+#define TI_SET_SERIAL_FLAGS	0
+
+/* 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
+
+
+/* Structures */
+
+struct ti_port {
+	int			tp_is_open;
+	__u8			tp_msr;
+	__u8			tp_lsr;
+	__u8			tp_shadow_mcr;
+	__u8			tp_uart_mode;	/* 232 or 485 modes */
+	unsigned int		tp_uart_base_addr;
+	int			tp_flags;
+	int			tp_closing_wait;/* in .01 secs */
+	struct async_icount	tp_icount;
+	wait_queue_head_t	tp_write_wait;
+	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 kfifo		write_fifo;
+};
+
+struct ti_device {
+	struct mutex		td_open_close_lock;
+	int			td_open_port_count;
+	struct usb_serial	*td_serial;
+	int			td_is_3410;
+	int			td_urb_error;
+};
+
+
+/* Function Declarations */
+
+static int ti_startup(struct usb_serial *serial);
+static void ti_release(struct usb_serial *serial);
+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 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 int ti_get_icount(struct tty_struct *tty,
+		struct serial_icounter_struct *icount);
+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 device *dev, struct tty_struct *tty,
+	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);
+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_drain(struct ti_port *tport, unsigned long timeout, int flush);
+
+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 ti_device *tdev, unsigned long addr,
+	__u8 mask, __u8 byte);
+
+static int ti_download_firmware(struct ti_device *tdev);
+
+
+/* Data */
+
+/* module parameters */
+static bool debug;
+static int closing_wait = TI_DEFAULT_CLOSING_WAIT;
+static ushort vendor_3410[TI_EXTRA_VID_PID_COUNT];
+static unsigned int vendor_3410_count;
+static ushort product_3410[TI_EXTRA_VID_PID_COUNT];
+static unsigned int product_3410_count;
+static ushort vendor_5052[TI_EXTRA_VID_PID_COUNT];
+static unsigned int vendor_5052_count;
+static ushort product_5052[TI_EXTRA_VID_PID_COUNT];
+static unsigned int product_5052_count;
+
+/* supported devices */
+/* the array dimension is the number of default entries plus */
+/* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */
+/* null entry */
+static struct usb_device_id ti_id_table_3410[15+TI_EXTRA_VID_PID_COUNT+1] = {
+	{ 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) },
+};
+
+static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = {
+	{ 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 struct usb_device_id ti_id_table_combined[19+2*TI_EXTRA_VID_PID_COUNT+1] = {
+	{ 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) },
+	{ }
+};
+
+static struct usb_driver ti_usb_driver = {
+	.name			= "ti_usb_3410_5052",
+	.probe			= usb_serial_probe,
+	.disconnect		= usb_serial_disconnect,
+	.id_table		= ti_id_table_combined,
+};
+
+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,
+	.attach			= ti_startup,
+	.release		= ti_release,
+	.open			= ti_open,
+	.close			= ti_close,
+	.write			= ti_write,
+	.write_room		= ti_write_room,
+	.chars_in_buffer	= ti_chars_in_buffer,
+	.throttle		= ti_throttle,
+	.unthrottle		= ti_unthrottle,
+	.ioctl			= ti_ioctl,
+	.set_termios		= ti_set_termios,
+	.tiocmget		= ti_tiocmget,
+	.tiocmset		= ti_tiocmset,
+	.get_icount		= ti_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,
+	.attach			= ti_startup,
+	.release		= ti_release,
+	.open			= ti_open,
+	.close			= ti_close,
+	.write			= ti_write,
+	.write_room		= ti_write_room,
+	.chars_in_buffer	= ti_chars_in_buffer,
+	.throttle		= ti_throttle,
+	.unthrottle		= ti_unthrottle,
+	.ioctl			= ti_ioctl,
+	.set_termios		= ti_set_termios,
+	.tiocmget		= ti_tiocmget,
+	.tiocmset		= ti_tiocmset,
+	.get_icount		= ti_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 */
+
+MODULE_AUTHOR(TI_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(TI_DRIVER_DESC);
+MODULE_VERSION(TI_DRIVER_VERSION);
+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_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging, 0=no, 1=yes");
+
+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_param_array(vendor_3410, ushort, &vendor_3410_count, S_IRUGO);
+MODULE_PARM_DESC(vendor_3410,
+		"Vendor ids for 3410 based devices, 1-5 short integers");
+module_param_array(product_3410, ushort, &product_3410_count, S_IRUGO);
+MODULE_PARM_DESC(product_3410,
+		"Product ids for 3410 based devices, 1-5 short integers");
+module_param_array(vendor_5052, ushort, &vendor_5052_count, S_IRUGO);
+MODULE_PARM_DESC(vendor_5052,
+		"Vendor ids for 5052 based devices, 1-5 short integers");
+module_param_array(product_5052, ushort, &product_5052_count, S_IRUGO);
+MODULE_PARM_DESC(product_5052,
+		"Product ids for 5052 based devices, 1-5 short integers");
+
+MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
+
+
+/* Functions */
+
+static int __init ti_init(void)
+{
+	int i, j, c;
+	int ret;
+
+	/* insert extra vendor and product ids */
+	c = ARRAY_SIZE(ti_id_table_combined) - 2 * TI_EXTRA_VID_PID_COUNT - 1;
+	j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1;
+	for (i = 0; i < min(vendor_3410_count, product_3410_count); i++, j++, c++) {
+		ti_id_table_3410[j].idVendor = vendor_3410[i];
+		ti_id_table_3410[j].idProduct = product_3410[i];
+		ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+		ti_id_table_combined[c].idVendor = vendor_3410[i];
+		ti_id_table_combined[c].idProduct = product_3410[i];
+		ti_id_table_combined[c].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+	}
+	j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1;
+	for (i = 0; i < min(vendor_5052_count, product_5052_count); i++, j++, c++) {
+		ti_id_table_5052[j].idVendor = vendor_5052[i];
+		ti_id_table_5052[j].idProduct = product_5052[i];
+		ti_id_table_5052[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+		ti_id_table_combined[c].idVendor = vendor_5052[i];
+		ti_id_table_combined[c].idProduct = product_5052[i];
+		ti_id_table_combined[c].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+	}
+
+	ret = usb_serial_register_drivers(&ti_usb_driver, serial_drivers);
+	if (ret == 0)
+		printk(KERN_INFO KBUILD_MODNAME ": " TI_DRIVER_VERSION ":"
+			       TI_DRIVER_DESC "\n");
+	return ret;
+}
+
+
+static void __exit ti_exit(void)
+{
+	usb_serial_deregister_drivers(&ti_usb_driver, serial_drivers);
+}
+
+
+module_init(ti_init);
+module_exit(ti_exit);
+
+
+static int ti_startup(struct usb_serial *serial)
+{
+	struct ti_device *tdev;
+	struct ti_port *tport;
+	struct usb_device *dev = serial->dev;
+	int status;
+	int i;
+
+
+	dbg("%s - product 0x%4X, num configurations %d, configuration value %d",
+	    __func__, le16_to_cpu(dev->descriptor.idProduct),
+	    dev->descriptor.bNumConfigurations,
+	    dev->actconfig->desc.bConfigurationValue);
+
+	/* create device structure */
+	tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
+	if (tdev == NULL) {
+		dev_err(&dev->dev, "%s - out of memory\n", __func__);
+		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;
+	dbg("%s - device type is %s", __func__,
+				tdev->td_is_3410 ? "3410" : "5052");
+
+	/* if we have only 1 configuration, download firmware */
+	if (dev->descriptor.bNumConfigurations == 1) {
+		if ((status = ti_download_firmware(tdev)) != 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;
+	}
+
+	/* set up port structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		tport = kzalloc(sizeof(struct ti_port), GFP_KERNEL);
+		if (tport == NULL) {
+			dev_err(&dev->dev, "%s - out of memory\n", __func__);
+			status = -ENOMEM;
+			goto free_tports;
+		}
+		spin_lock_init(&tport->tp_lock);
+		tport->tp_uart_base_addr = (i == 0 ?
+				TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR);
+		tport->tp_closing_wait = closing_wait;
+		init_waitqueue_head(&tport->tp_write_wait);
+		if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE,
+								GFP_KERNEL)) {
+			dev_err(&dev->dev, "%s - out of memory\n", __func__);
+			kfree(tport);
+			status = -ENOMEM;
+			goto free_tports;
+		}
+		tport->tp_port = serial->port[i];
+		tport->tp_tdev = tdev;
+		usb_set_serial_port_data(serial->port[i], tport);
+		tport->tp_uart_mode = 0;	/* default is RS232 */
+	}
+
+	return 0;
+
+free_tports:
+	for (--i; i >= 0; --i) {
+		tport = usb_get_serial_port_data(serial->port[i]);
+		kfifo_free(&tport->write_fifo);
+		kfree(tport);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+free_tdev:
+	kfree(tdev);
+	usb_set_serial_data(serial, NULL);
+	return status;
+}
+
+
+static void ti_release(struct usb_serial *serial)
+{
+	int i;
+	struct ti_device *tdev = usb_get_serial_data(serial);
+	struct ti_port *tport;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		tport = usb_get_serial_port_data(serial->port[i]);
+		if (tport) {
+			kfifo_free(&tport->write_fifo);
+			kfree(tport);
+		}
+	}
+
+	kfree(tdev);
+}
+
+
+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 = (__u8)(TI_PIPE_MODE_CONTINOUS |
+			     TI_PIPE_TIMEOUT_ENABLE |
+			     (TI_TRANSFER_TIMEOUT << 2));
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return -ENODEV;
+
+	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->number - port->serial->minor;
+
+	memset(&(tport->tp_icount), 0x00, sizeof(tport->tp_icount));
+
+	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) {
+		dbg("%s - start interrupt in urb", __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);
+
+	dbg("%s - sending TI_OPEN_PORT", __func__);
+	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;
+	}
+
+	dbg("%s - sending TI_START_PORT", __func__);
+	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;
+	}
+
+	dbg("%s - sending TI_PURGE_PORT", __func__);
+	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);
+
+	dbg("%s - sending TI_OPEN_PORT (2)", __func__);
+	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;
+	}
+
+	dbg("%s - sending TI_START_PORT (2)", __func__);
+	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 */
+	dbg("%s - start read urb", __func__);
+	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);
+	dbg("%s - exit %d", __func__, status);
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	tdev = usb_get_serial_data(port->serial);
+	tport = usb_get_serial_port_data(port);
+	if (tdev == NULL || tport == NULL)
+		return;
+
+	tport->tp_is_open = 0;
+
+	ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 1);
+
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->write_urb);
+	tport->tp_write_urb_in_use = 0;
+
+	port_number = port->number - port->serial->minor;
+
+	dbg("%s - sending TI_CLOSE_PORT", __func__);
+	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);
+
+	dbg("%s - exit", __func__);
+}
+
+
+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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __func__);
+		return 0;
+	}
+
+	if (tport == NULL || !tport->tp_is_open)
+		return -ENODEV;
+
+	count = kfifo_in_locked(&tport->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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return 0;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	room = kfifo_avail(&tport->write_fifo);
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	dbg("%s - returns %d", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return 0;
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+	chars = kfifo_len(&tport->write_fifo);
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+	dbg("%s - returns %d", __func__, chars);
+	return chars;
+}
+
+
+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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return;
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return;
+
+	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_get_icount(struct tty_struct *tty,
+		struct serial_icounter_struct *icount)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct ti_port *tport = usb_get_serial_port_data(port);
+	struct async_icount cnow = tport->tp_icount;
+
+	dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d",
+		__func__, port->number,
+		cnow.rx, cnow.tx);
+
+	icount->cts = cnow.cts;
+	icount->dsr = cnow.dsr;
+	icount->rng = cnow.rng;
+	icount->dcd = cnow.dcd;
+	icount->rx = cnow.rx;
+	icount->tx = cnow.tx;
+	icount->frame = cnow.frame;
+	icount->overrun = cnow.overrun;
+	icount->parity = cnow.parity;
+	icount->brk = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	return 0;
+}
+
+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);
+	struct async_icount cnow;
+	struct async_icount cprev;
+
+	dbg("%s - port %d, cmd = 0x%04X", __func__, port->number, cmd);
+
+	if (tport == NULL)
+		return -ENODEV;
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
+		return ti_get_serial_info(tport,
+				(struct serial_struct __user *)arg);
+	case TIOCSSERIAL:
+		dbg("%s - (%d) TIOCSSERIAL", __func__, port->number);
+		return ti_set_serial_info(tty, tport,
+				(struct serial_struct __user *)arg);
+	case TIOCMIWAIT:
+		dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
+		cprev = tport->tp_icount;
+		while (1) {
+			interruptible_sleep_on(&port->delta_msr_wait);
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (port->serial->disconnected)
+				return -EIO;
+
+			cnow = tport->tp_icount;
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((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)))
+				return 0;
+			cprev = cnow;
+		}
+		break;
+	}
+	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;
+	tcflag_t cflag, iflag;
+	int baud;
+	int status;
+	int port_number = port->number - port->serial->minor;
+	unsigned int mcr;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	cflag = tty->termios->c_cflag;
+	iflag = tty->termios->c_iflag;
+
+	dbg("%s - cflag %08x, iflag %08x", __func__, cflag, iflag);
+	dbg("%s - old clfag %08x, old iflag %08x", __func__,
+				old_termios->c_cflag, old_termios->c_iflag);
+
+	if (tport == NULL)
+		return;
+
+	config = kmalloc(sizeof(*config), GFP_KERNEL);
+	if (!config) {
+		dev_err(&port->dev, "%s - out of memory\n", __func__);
+		return;
+	}
+
+	config->wFlags = 0;
+
+	/* these flags must be set */
+	config->wFlags |= TI_UART_ENABLE_MS_INTS;
+	config->wFlags |= TI_UART_ENABLE_AUTO_START_DMA;
+	config->bUartMode = (__u8)(tport->tp_uart_mode);
+
+	switch (cflag & CSIZE) {
+	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 (cflag & PARENB) {
+		if (cflag & PARODD) {
+			config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+			config->bParity = TI_UART_ODD_PARITY;
+		} else {
+			config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+			config->bParity = TI_UART_EVEN_PARITY;
+		}
+	} else {
+		config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING;
+		config->bParity = TI_UART_NO_PARITY;
+	}
+
+	if (cflag & CSTOPB)
+		config->bStopBits = TI_UART_2_STOP_BITS;
+	else
+		config->bStopBits = TI_UART_1_STOP_BITS;
+
+	if (cflag & CRTSCTS) {
+		/* RTS flow control must be off to drop RTS for baud rate B0 */
+		if ((cflag & CBAUD) != B0)
+			config->wFlags |= TI_UART_ENABLE_RTS_IN;
+		config->wFlags |= TI_UART_ENABLE_CTS_OUT;
+	} else {
+		tty->hw_stopped = 0;
+		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))
+			config->wFlags |= TI_UART_ENABLE_X_IN;
+		else
+			ti_restart_read(tport, tty);
+
+		if (I_IXON(tty))
+			config->wFlags |= TI_UART_ENABLE_X_OUT;
+	}
+
+	baud = tty_get_baud_rate(tty);
+	if (!baud)
+		baud = 9600;
+	if (tport->tp_tdev->td_is_3410)
+		config->wBaudRate = (__u16)((923077 + baud/2) / baud);
+	else
+		config->wBaudRate = (__u16)((461538 + baud/2) / baud);
+
+	/* FIXME: Should calculate resulting baud here and report it back */
+	if ((cflag & CBAUD) != B0)
+		tty_encode_baud_rate(tty, baud, baud);
+
+	dbg("%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d",
+	__func__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode);
+
+	cpu_to_be16s(&config->wBaudRate);
+	cpu_to_be16s(&config->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 ((cflag & CBAUD) == 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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return -ENODEV;
+
+	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);
+
+	dbg("%s - 0x%04X", __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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (tport == NULL)
+		return -ENODEV;
+
+	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;
+
+	dbg("%s - state = %d", __func__, break_state);
+
+	if (tport == NULL)
+		return;
+
+	ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 0);
+
+	status = ti_write_byte(tport->tp_tdev,
+		tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
+		TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
+
+	if (status)
+		dbg("%s - error setting break, %d", __func__, status);
+}
+
+
+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;
+
+	dbg("%s", __func__);
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dbg("%s - urb shutting down, %d", __func__, status);
+		tdev->td_urb_error = 1;
+		return;
+	default:
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__func__, status);
+		tdev->td_urb_error = 1;
+		goto exit;
+	}
+
+	if (length != 2) {
+		dbg("%s - bad packet size, %d", __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]);
+
+	dbg("%s - port_number %d, function %d, data 0x%02X",
+				__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];
+		dbg("%s - port %d, msr 0x%02X", __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;
+	int retval = 0;
+	struct tty_struct *tty;
+
+	dbg("%s", __func__);
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dbg("%s - urb shutting down, %d", __func__, status);
+		tport->tp_tdev->td_urb_error = 1;
+		wake_up_interruptible(&tport->tp_write_wait);
+		return;
+	default:
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__func__, status);
+		tport->tp_tdev->td_urb_error = 1;
+		wake_up_interruptible(&tport->tp_write_wait);
+	}
+
+	if (status == -EPIPE)
+		goto exit;
+
+	if (status) {
+		dev_err(dev, "%s - stopping read!\n", __func__);
+		return;
+	}
+
+	tty = tty_port_tty_get(&port->port);
+	if (tty) {
+		if (urb->actual_length) {
+			usb_serial_debug_data(debug, dev, __func__,
+				urb->actual_length, urb->transfer_buffer);
+
+			if (!tport->tp_is_open)
+				dbg("%s - port closed, dropping data",
+					__func__);
+			else
+				ti_recv(&urb->dev->dev, tty,
+						urb->transfer_buffer,
+						urb->actual_length);
+			spin_lock(&tport->tp_lock);
+			tport->tp_icount.rx += urb->actual_length;
+			spin_unlock(&tport->tp_lock);
+		}
+		tty_kref_put(tty);
+	}
+
+exit:
+	/* continue to read unless stopping */
+	spin_lock(&tport->tp_lock);
+	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(&tport->tp_lock);
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	tport->tp_write_urb_in_use = 0;
+
+	switch (status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dbg("%s - urb shutting down, %d", __func__, status);
+		tport->tp_tdev->td_urb_error = 1;
+		wake_up_interruptible(&tport->tp_write_wait);
+		return;
+	default:
+		dev_err_console(port, "%s - nonzero urb status, %d\n",
+			__func__, status);
+		tport->tp_tdev->td_urb_error = 1;
+		wake_up_interruptible(&tport->tp_write_wait);
+	}
+
+	/* send any buffered data */
+	ti_send(tport);
+}
+
+
+static void ti_recv(struct device *dev, struct tty_struct *tty,
+	unsigned char *data, int length)
+{
+	int cnt;
+
+	do {
+		cnt = tty_insert_flip_string(tty, data, length);
+		if (cnt < length) {
+			dev_err(dev, "%s - dropping data, %d bytes lost\n",
+						__func__, length - cnt);
+			if (cnt == 0)
+				break;
+		}
+		tty_flip_buffer_push(tty);
+		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;
+	struct tty_struct *tty = tty_port_tty_get(&port->port);	/* FIXME */
+	unsigned long flags;
+
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&tport->tp_lock, flags);
+
+	if (tport->tp_write_urb_in_use)
+		goto unlock;
+
+	count = kfifo_out(&tport->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(debug, &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);
+		tport->tp_icount.tx += count;
+		spin_unlock_irqrestore(&tport->tp_lock, flags);
+	}
+
+	/* more room in the buffer for new writes, wakeup */
+	if (tty)
+		tty_wakeup(tty);
+	tty_kref_put(tty);
+	wake_up_interruptible(&tport->tp_write_wait);
+	return;
+unlock:
+	spin_unlock_irqrestore(&tport->tp_lock, flags);
+	tty_kref_put(tty);
+	return;
+}
+
+
+static int ti_set_mcr(struct ti_port *tport, unsigned int mcr)
+{
+	unsigned long flags;
+	int status;
+
+	status = ti_write_byte(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)
+{
+	int size, status;
+	struct ti_device *tdev = tport->tp_tdev;
+	struct usb_serial_port *port = tport->tp_port;
+	int port_number = port->number - port->serial->minor;
+	struct ti_port_status *data;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	size = sizeof(struct ti_port_status);
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data) {
+		dev_err(&port->dev, "%s - out of memory\n", __func__);
+		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;
+	}
+
+	dbg("%s - lsr 0x%02X", __func__, data->bLSR);
+
+	tport->tp_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;
+
+	if (!ret_arg)
+		return -EFAULT;
+
+	memset(&ret_serial, 0, sizeof(ret_serial));
+
+	ret_serial.type = PORT_16550A;
+	ret_serial.line = port->serial->minor;
+	ret_serial.port = port->number - port->serial->minor;
+	ret_serial.flags = tport->tp_flags;
+	ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE;
+	ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
+	ret_serial.closing_wait = tport->tp_closing_wait;
+
+	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;
+
+	if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
+		return -EFAULT;
+
+	tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS;
+	tport->tp_closing_wait = new_serial.closing_wait;
+
+	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;
+
+	dbg("%s - msr 0x%02X", __func__, msr);
+
+	if (msr & TI_MSR_DELTA_MASK) {
+		spin_lock_irqsave(&tport->tp_lock, flags);
+		icount = &tport->tp_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->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->hw_stopped = 0;
+			tty_wakeup(tty);
+		} else {
+			tty->hw_stopped = 1;
+		}
+	}
+	tty_kref_put(tty);
+}
+
+
+static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush)
+{
+	struct ti_device *tdev = tport->tp_tdev;
+	struct usb_serial_port *port = tport->tp_port;
+	wait_queue_t wait;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irq(&tport->tp_lock);
+
+	/* wait for data to drain from the buffer */
+	tdev->td_urb_error = 0;
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&tport->tp_write_wait, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kfifo_len(&tport->write_fifo) == 0
+		|| timeout == 0 || signal_pending(current)
+		|| tdev->td_urb_error
+		|| port->serial->disconnected)  /* disconnect */
+			break;
+		spin_unlock_irq(&tport->tp_lock);
+		timeout = schedule_timeout(timeout);
+		spin_lock_irq(&tport->tp_lock);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&tport->tp_write_wait, &wait);
+
+	/* flush any remaining data in the buffer */
+	if (flush)
+		kfifo_reset_out(&tport->write_fifo);
+
+	spin_unlock_irq(&tport->tp_lock);
+
+	mutex_lock(&port->serial->disc_mutex);
+	/* wait for data to drain from the device */
+	/* wait for empty tx register, plus 20 ms */
+	timeout += jiffies;
+	tport->tp_lsr &= ~TI_LSR_TX_EMPTY;
+	while ((long)(jiffies - timeout) < 0 && !signal_pending(current)
+	&& !(tport->tp_lsr&TI_LSR_TX_EMPTY) && !tdev->td_urb_error
+	&& !port->serial->disconnected) {
+		if (ti_get_lsr(tport))
+			break;
+		mutex_unlock(&port->serial->disc_mutex);
+		msleep_interruptible(20);
+		mutex_lock(&port->serial->disc_mutex);
+	}
+	mutex_unlock(&port->serial->disc_mutex);
+}
+
+
+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 == size)
+		status = 0;
+
+	if (status > 0)
+		status = -ECOMM;
+
+	return status;
+}
+
+
+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;
+
+	if (status > 0)
+		status = -ECOMM;
+
+	return status;
+}
+
+
+static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
+	__u8 mask, __u8 byte)
+{
+	int status;
+	unsigned int size;
+	struct ti_write_data_bytes *data;
+	struct device *dev = &tdev->td_serial->dev->dev;
+
+	dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X",
+					__func__, addr, mask, byte);
+
+	size = sizeof(struct ti_write_data_bytes) + 2;
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data) {
+		dev_err(dev, "%s - out of memory\n", __func__);
+		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(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((__u16)(size
+					- sizeof(struct ti_firmware_header)));
+	header->bCheckSum = cs;
+
+	dbg("%s - downloading firmware", __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];
+
+	dbg("%s\n", __func__);
+	/* 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));
+	if ((status = request_firmware(&fw_p, buf, &dev->dev)) != 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);
+	}
+	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 {
+		dbg("%s ENOMEM\n", __func__);
+		status = -ENOMEM;
+	}
+	release_firmware(fw_p);
+	if (status) {
+		dev_err(&dev->dev, "%s - error downloading firmware, %d\n",
+							__func__, status);
+		return status;
+	}
+
+	dbg("%s - download successful", __func__);
+
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.h
new file mode 100644
index 0000000..4a2423e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/ti_usb_3410_5052.h
@@ -0,0 +1,247 @@
+/* vi: ts=8 sw=8
+ *
+ * TI 3410/5052 USB Serial Driver Header
+ *
+ * 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
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * For questions or problems with this driver, contact Texas Instruments
+ * technical support, or Al Borchers <alborchers@steinerpoint.com>, or
+ * Peter Berger <pberger@brimson.com>.
+ */
+
+#ifndef _TI_3410_5052_H_
+#define _TI_3410_5052_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
+
+/* 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_CONTINOUS		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 {
+	__u16	wBaudRate;
+	__u16	wFlags;
+	__u8	bDataBits;
+	__u8	bParity;
+	__u8	bStopBits;
+	char	cXon;
+	char	cXoff;
+	__u8	bUartMode;
+} __attribute__((packed));
+
+/* Get port status */
+struct ti_port_status {
+	__u8	bCmdCode;
+	__u8	bModuleId;
+	__u8	bErrorCode;
+	__u8	bMSR;
+	__u8	bLSR;
+} __attribute__((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];
+} __attribute__((packed));
+
+struct ti_read_data_request {
+	__u8	bAddrType;
+	__u8	bDataType;
+	__u8	bDataCounter;
+	__be16	wBaseAddrHi;
+	__be16	wBaseAddrLo;
+} __attribute__((packed));
+
+struct ti_read_data_bytes {
+	__u8	bCmdCode;
+	__u8	bModuleId;
+	__u8	bErrorCode;
+	__u8	bData[0];
+} __attribute__((packed));
+
+/* Interrupt struct */
+struct ti_interrupt {
+	__u8	bICode;
+	__u8	bIInfo;
+} __attribute__((packed));
+
+/* Interrupt codes */
+#define TI_GET_PORT_FROM_CODE(c)	(((c) >> 4) - 3)
+#define TI_GET_FUNC_FROM_CODE(c)	((c) & 0x0f)
+#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;
+} __attribute__((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 */
+
+#endif /* _TI_3410_5052_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-serial.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-serial.c
new file mode 100644
index 0000000..a08230e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-serial.c
@@ -0,0 +1,1480 @@
+/*
+ * USB Serial Converter driver
+ *
+ * Copyright (C) 1999 - 2005 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#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 "pl2303.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
+#define DRIVER_DESC "USB Serial Driver core"
+
+/* Driver structure we register with the USB core */
+static struct usb_driver usb_serial_driver = {
+	.name =		"usbserial",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.suspend =	usb_serial_suspend,
+	.resume =	usb_serial_resume,
+	.no_dynamic_id =	1,
+	.supports_autosuspend =	1,
+};
+
+/* 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 bool debug;
+/* initially all NULL */
+static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
+static DEFINE_MUTEX(table_lock);
+static LIST_HEAD(usb_serial_driver_list);
+
+/*
+ * Look up the serial structure.  If it is found and it hasn't been
+ * disconnected, return with its disc_mutex held and its refcount
+ * incremented.  Otherwise return NULL.
+ */
+struct usb_serial *usb_serial_get_by_index(unsigned index)
+{
+	struct usb_serial *serial;
+
+	mutex_lock(&table_lock);
+	serial = serial_table[index];
+
+	if (serial) {
+		mutex_lock(&serial->disc_mutex);
+		if (serial->disconnected) {
+			mutex_unlock(&serial->disc_mutex);
+			serial = NULL;
+		} else {
+			kref_get(&serial->kref);
+		}
+	}
+	mutex_unlock(&table_lock);
+	return serial;
+}
+
+static struct usb_serial *get_free_serial(struct usb_serial *serial,
+					int num_ports, unsigned int *minor)
+{
+	unsigned int i, j;
+	int good_spot;
+
+	dbg("%s %d", __func__, num_ports);
+
+	*minor = 0;
+	mutex_lock(&table_lock);
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		if (serial_table[i])
+			continue;
+
+		good_spot = 1;
+		for (j = 1; j <= num_ports-1; ++j)
+			if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {
+				good_spot = 0;
+				i += j;
+				break;
+			}
+		if (good_spot == 0)
+			continue;
+
+		*minor = i;
+		j = 0;
+		dbg("%s - minor base = %d", __func__, *minor);
+		for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) {
+			serial_table[i] = serial;
+			serial->port[j++]->number = i;
+		}
+		mutex_unlock(&table_lock);
+		return serial;
+	}
+	mutex_unlock(&table_lock);
+	return NULL;
+}
+
+static void return_serial(struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __func__);
+
+	mutex_lock(&table_lock);
+	for (i = 0; i < serial->num_ports; ++i)
+		serial_table[serial->minor + i] = NULL;
+	mutex_unlock(&table_lock);
+}
+
+static void destroy_serial(struct kref *kref)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int i;
+
+	serial = to_usb_serial(kref);
+
+	dbg("%s - %s", __func__, serial->type->description);
+
+	/* return the minor range that this device had */
+	if (serial->minor != SERIAL_TTY_NO_MINOR)
+		return_serial(serial);
+
+	if (serial->attached)
+		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;
+
+	dbg("%s", __func__);
+
+	serial = usb_serial_get_by_index(idx);
+	if (!serial)
+		return retval;
+
+	port = serial->port[idx - serial->minor];
+	if (!port)
+		goto error_no_port;
+	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:
+ error_no_port:
+	usb_serial_put(serial);
+	mutex_unlock(&serial->disc_mutex);
+	return retval;
+}
+
+static int serial_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;
+
+	dbg("%s - port %d", __func__, port->number);
+	return tty_port_open(&port->port, tty, filp);
+}
+
+/**
+ * serial_down - shut down hardware
+ * @tport: tty port to shut down
+ *
+ * Shut down a USB serial port unless it is the console.  We never
+ * shut down the console hardware as it will always be in use. Serialized
+ * against activate by the tport mutex and kept to matching open/close pairs
+ * of calls by the ASYNCB_INITIALIZED flag.
+ */
+static void serial_down(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;
+	/*
+	 * The console is magical.  Do not hang up the console hardware
+	 * or there will be tears.
+	 */
+	if (port->port.console)
+		return;
+	if (drv->close)
+		drv->close(port);
+}
+
+static void serial_hangup(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s - port %d", __func__, port->number);
+	tty_port_hangup(&port->port);
+}
+
+static void serial_close(struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s - port %d", __func__, port->number);
+	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,
+ * and the tty layer has already done the tty_shutdown(tty);
+ */
+static void serial_cleanup(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial;
+	struct module *owner;
+
+	/* The console is magical.  Do not hang up the console hardware
+	 * or there will be tears.
+	 */
+	if (port->port.console)
+		return;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
+
+	/* pass on to the driver specific version of this function */
+	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;
+	dbg("%s - port %d", __func__, port->number);
+	/* pass on to the driver specific version of this function */
+	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;
+	dbg("%s - port %d", __func__, port->number);
+
+	/* if the device was unplugged then any remaining characters
+	   fell out of the connector ;) */
+	if (port->serial->disconnected)
+		return 0;
+	/* pass on to the driver specific version of this function */
+	return port->serial->type->chars_in_buffer(tty);
+}
+
+static void serial_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s - port %d", __func__, port->number);
+
+	/* pass on to the driver specific version of this function */
+	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;
+	dbg("%s - port %d", __func__, port->number);
+
+	/* pass on to the driver specific version of this function */
+	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 = -ENODEV;
+
+	dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+
+	/* pass on to the driver specific version of this function
+	   if it is available */
+	if (port->serial->type->ioctl) {
+		retval = port->serial->type->ioctl(tty, cmd, arg);
+	} else
+		retval = -ENOIOCTLCMD;
+	return retval;
+}
+
+static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	dbg("%s - port %d", __func__, port->number);
+
+	/* pass on to the driver specific version of this function
+	   if it is available */
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* pass on to the driver specific version of this function
+	   if it is available */
+	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;
+	int i;
+	char tmp[40];
+
+	dbg("%s", __func__);
+	seq_puts(m, "usbserinfo:1.0 driver:2.0\n");
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		serial = usb_serial_get_by_index(i);
+		if (serial == NULL)
+			continue;
+
+		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", i - serial->minor + 1);
+		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_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, serial_proc_show, NULL);
+}
+
+static const struct file_operations serial_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= serial_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int serial_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+	struct tty_struct *tty;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	tty = tty_port_tty_get(&port->port);
+	if (!tty)
+		return;
+
+	tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+static void kill_traffic(struct usb_serial_port *port)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+		usb_kill_urb(port->read_urbs[i]);
+	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+		usb_kill_urb(port->write_urbs[i]);
+	/*
+	 * This is tricky.
+	 * Some drivers submit the read_urb in the
+	 * handler for the write_urb or vice versa
+	 * this order determines the order in which
+	 * usb_kill_urb() must be used to reliably
+	 * kill the URBs. As it is unknown here,
+	 * both orders must be used in turn.
+	 * The call below is not redundant.
+	 */
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+	usb_kill_urb(port->interrupt_out_urb);
+}
+
+static void port_release(struct device *dev)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	int i;
+
+	dbg ("%s - %s", __func__, dev_name(dev));
+
+	/*
+	 * Stop all the traffic before cancelling the work, so that
+	 * nobody will restart it by calling usb_serial_port_softint.
+	 */
+	kill_traffic(port);
+	cancel_work_sync(&port->work);
+
+	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);
+	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) {
+		dev_err(&dev->dev, "%s - out of memory\n", __func__);
+		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->minor = SERIAL_TTY_NO_MINOR;
+
+	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) {
+		dbg("static descriptor matches");
+		goto exit;
+	}
+	id = match_dynamic_id(intf, drv);
+	if (id)
+		dbg("dynamic descriptor matches");
+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_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_dtr_rts(struct tty_port *port, int on)
+{
+	struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+	struct usb_serial *serial = p->serial;
+	struct usb_serial_driver *drv = serial->type;
+
+	if (!drv->dtr_rts)
+		return;
+	/*
+	 * Work-around bug in the tty-layer which can result in dtr_rts
+	 * being called after a disconnect (and tty_unregister_device
+	 * has returned). Remove once bug has been squashed.
+	 */
+	mutex_lock(&serial->disc_mutex);
+	if (!serial->disconnected)
+		drv->dtr_rts(p, on);
+	mutex_unlock(&serial->disc_mutex);
+}
+
+static const struct tty_port_operations serial_port_ops = {
+	.carrier_raised = serial_carrier_raised,
+	.dtr_rts = serial_dtr_rts,
+	.activate = serial_activate,
+	.shutdown = serial_down,
+};
+
+int usb_serial_probe(struct usb_interface *interface,
+			       const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(interface);
+	struct usb_serial *serial = NULL;
+	struct usb_serial_port *port;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
+	struct usb_serial_driver *type = NULL;
+	int retval;
+	unsigned int minor;
+	int buffer_size;
+	int i;
+	int j;
+	int num_interrupt_in = 0;
+	int num_interrupt_out = 0;
+	int num_bulk_in = 0;
+	int num_bulk_out = 0;
+	int num_ports = 0;
+	int max_endpoints;
+
+	mutex_lock(&table_lock);
+	type = search_serial_device(interface);
+	if (!type) {
+		mutex_unlock(&table_lock);
+		dbg("none matched");
+		return -ENODEV;
+	}
+
+	if (!try_module_get(type->driver.owner)) {
+		mutex_unlock(&table_lock);
+		dev_err(&interface->dev, "module get failed, exiting\n");
+		return -EIO;
+	}
+	mutex_unlock(&table_lock);
+
+	serial = create_serial(dev, interface, type);
+	if (!serial) {
+		module_put(type->driver.owner);
+		dev_err(&interface->dev, "%s - out of memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* 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) {
+			dbg("sub driver rejected device");
+			usb_serial_put(serial);
+			module_put(type->driver.owner);
+			return retval;
+		}
+	}
+
+	/* descriptor matches, let's find the endpoints needed */
+	/* check out the endpoints */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_bulk_in(endpoint)) {
+			/* we found a bulk in endpoint */
+			dbg("found bulk in on endpoint %d", i);
+			if (num_bulk_in < MAX_NUM_PORTS) {
+				bulk_in_endpoint[num_bulk_in] = endpoint;
+				++num_bulk_in;
+			}
+		}
+
+		if (usb_endpoint_is_bulk_out(endpoint)) {
+			/* we found a bulk out endpoint */
+			dbg("found bulk out on endpoint %d", i);
+			if (num_bulk_out < MAX_NUM_PORTS) {
+				bulk_out_endpoint[num_bulk_out] = endpoint;
+				++num_bulk_out;
+			}
+		}
+
+		if (usb_endpoint_is_int_in(endpoint)) {
+			/* we found a interrupt in endpoint */
+			dbg("found interrupt in on endpoint %d", i);
+			if (num_interrupt_in < MAX_NUM_PORTS) {
+				interrupt_in_endpoint[num_interrupt_in] = endpoint;
+				++num_interrupt_in;
+			}
+		}
+
+		if (usb_endpoint_is_int_out(endpoint)) {
+			/* we found an interrupt out endpoint */
+			dbg("found interrupt out on endpoint %d", i);
+			if (num_interrupt_out < MAX_NUM_PORTS) {
+				interrupt_out_endpoint[num_interrupt_out] = endpoint;
+				++num_interrupt_out;
+			}
+		}
+	}
+
+#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
+	/* BEGIN HORRIBLE HACK FOR PL2303 */
+	/* this is needed due to the looney way its endpoints are set up */
+	if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
+	     (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
+	    ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
+	     (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
+	    ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
+	     (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID)) ||
+	    ((le16_to_cpu(dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
+	     (le16_to_cpu(dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_EF81))) {
+		if (interface != dev->actconfig->interface[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)) {
+					/* we found a interrupt in endpoint */
+					dbg("found interrupt in for Prolific device on separate interface");
+					if (num_interrupt_in < MAX_NUM_PORTS) {
+						interrupt_in_endpoint[num_interrupt_in] = endpoint;
+						++num_interrupt_in;
+					}
+				}
+			}
+		}
+
+		/* Now make sure the PL-2303 is configured correctly.
+		 * If not, give up now and hope this hack will work
+		 * properly during a later invocation of usb_serial_probe
+		 */
+		if (num_bulk_in == 0 || num_bulk_out == 0) {
+			dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
+			usb_serial_put(serial);
+			module_put(type->driver.owner);
+			return -ENODEV;
+		}
+	}
+	/* END HORRIBLE HACK FOR PL2303 */
+#endif
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	if (type == &usb_serial_generic_device) {
+		num_ports = num_bulk_out;
+		if (num_ports == 0) {
+			dev_err(&interface->dev,
+			    "Generic device with no bulk out, not allowed.\n");
+			usb_serial_put(serial);
+			module_put(type->driver.owner);
+			return -EIO;
+		}
+	}
+#endif
+	if (!num_ports) {
+		/* if this device type has a calc_num_ports function, call it */
+		if (type->calc_num_ports)
+			num_ports = type->calc_num_ports(serial);
+		if (!num_ports)
+			num_ports = type->num_ports;
+	}
+
+	if (num_ports > MAX_NUM_PORTS) {
+		dev_warn(&interface->dev, "too many ports requested: %d\n", num_ports);
+		num_ports = MAX_NUM_PORTS;
+	}
+
+	serial->num_ports = num_ports;
+	serial->num_bulk_in = num_bulk_in;
+	serial->num_bulk_out = num_bulk_out;
+	serial->num_interrupt_in = num_interrupt_in;
+	serial->num_interrupt_out = num_interrupt_out;
+
+	/* found all that we need */
+	dev_info(&interface->dev, "%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(num_bulk_in, num_bulk_out);
+	max_endpoints = max(max_endpoints, num_interrupt_in);
+	max_endpoints = max(max_endpoints, num_interrupt_out);
+	max_endpoints = max(max_endpoints, (int)serial->num_ports);
+	serial->num_port_pointers = max_endpoints;
+
+	dbg("%s - setting up %d port structures for this device",
+						__func__, max_endpoints);
+	for (i = 0; i < max_endpoints; ++i) {
+		port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
+		if (!port)
+			goto probe_error;
+		tty_port_init(&port->port);
+		port->port.ops = &serial_port_ops;
+		port->serial = serial;
+		spin_lock_init(&port->lock);
+		init_waitqueue_head(&port->delta_msr_wait);
+		/* 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 = &port_release;
+		device_initialize(&port->dev);
+	}
+
+	/* set up the endpoint information */
+	for (i = 0; i < num_bulk_in; ++i) {
+		endpoint = bulk_in_endpoint[i];
+		port = serial->port[i];
+		buffer_size = max_t(int, serial->type->bulk_in_size,
+				usb_endpoint_maxp(endpoint));
+		port->bulk_in_size = buffer_size;
+		port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
+
+		for (j = 0; j < ARRAY_SIZE(port->read_urbs); ++j) {
+			set_bit(j, &port->read_urbs_free);
+			port->read_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
+			if (!port->read_urbs[j]) {
+				dev_err(&interface->dev,
+						"No free urbs available\n");
+				goto probe_error;
+			}
+			port->bulk_in_buffers[j] = kmalloc(buffer_size,
+								GFP_KERNEL);
+			if (!port->bulk_in_buffers[j]) {
+				dev_err(&interface->dev,
+					"Couldn't allocate bulk_in_buffer\n");
+				goto probe_error;
+			}
+			usb_fill_bulk_urb(port->read_urbs[j], dev,
+					usb_rcvbulkpipe(dev,
+						endpoint->bEndpointAddress),
+					port->bulk_in_buffers[j], buffer_size,
+					serial->type->read_bulk_callback,
+					port);
+		}
+
+		port->read_urb = port->read_urbs[0];
+		port->bulk_in_buffer = port->bulk_in_buffers[0];
+	}
+
+	for (i = 0; i < num_bulk_out; ++i) {
+		endpoint = bulk_out_endpoint[i];
+		port = serial->port[i];
+		if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+			goto probe_error;
+		buffer_size = serial->type->bulk_out_size;
+		if (!buffer_size)
+			buffer_size = usb_endpoint_maxp(endpoint);
+		port->bulk_out_size = buffer_size;
+		port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+
+		for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) {
+			set_bit(j, &port->write_urbs_free);
+			port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
+			if (!port->write_urbs[j]) {
+				dev_err(&interface->dev,
+						"No free urbs available\n");
+				goto probe_error;
+			}
+			port->bulk_out_buffers[j] = kmalloc(buffer_size,
+								GFP_KERNEL);
+			if (!port->bulk_out_buffers[j]) {
+				dev_err(&interface->dev,
+					"Couldn't allocate bulk_out_buffer\n");
+				goto probe_error;
+			}
+			usb_fill_bulk_urb(port->write_urbs[j], dev,
+					usb_sndbulkpipe(dev,
+						endpoint->bEndpointAddress),
+					port->bulk_out_buffers[j], buffer_size,
+					serial->type->write_bulk_callback,
+					port);
+		}
+
+		port->write_urb = port->write_urbs[0];
+		port->bulk_out_buffer = port->bulk_out_buffers[0];
+	}
+
+	if (serial->type->read_int_callback) {
+		for (i = 0; i < num_interrupt_in; ++i) {
+			endpoint = interrupt_in_endpoint[i];
+			port = serial->port[i];
+			port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!port->interrupt_in_urb) {
+				dev_err(&interface->dev,
+						"No free urbs available\n");
+				goto probe_error;
+			}
+			buffer_size = usb_endpoint_maxp(endpoint);
+			port->interrupt_in_endpointAddress =
+						endpoint->bEndpointAddress;
+			port->interrupt_in_buffer = kmalloc(buffer_size,
+								GFP_KERNEL);
+			if (!port->interrupt_in_buffer) {
+				dev_err(&interface->dev,
+				    "Couldn't allocate interrupt_in_buffer\n");
+				goto probe_error;
+			}
+			usb_fill_int_urb(port->interrupt_in_urb, dev,
+				usb_rcvintpipe(dev,
+						endpoint->bEndpointAddress),
+				port->interrupt_in_buffer, buffer_size,
+				serial->type->read_int_callback, port,
+				endpoint->bInterval);
+		}
+	} else if (num_interrupt_in) {
+		dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");
+	}
+
+	if (serial->type->write_int_callback) {
+		for (i = 0; i < num_interrupt_out; ++i) {
+			endpoint = interrupt_out_endpoint[i];
+			port = serial->port[i];
+			port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!port->interrupt_out_urb) {
+				dev_err(&interface->dev,
+						"No free urbs available\n");
+				goto probe_error;
+			}
+			buffer_size = usb_endpoint_maxp(endpoint);
+			port->interrupt_out_size = buffer_size;
+			port->interrupt_out_endpointAddress =
+						endpoint->bEndpointAddress;
+			port->interrupt_out_buffer = kmalloc(buffer_size,
+								GFP_KERNEL);
+			if (!port->interrupt_out_buffer) {
+				dev_err(&interface->dev,
+				  "Couldn't allocate interrupt_out_buffer\n");
+				goto probe_error;
+			}
+			usb_fill_int_urb(port->interrupt_out_urb, dev,
+				usb_sndintpipe(dev,
+						  endpoint->bEndpointAddress),
+				port->interrupt_out_buffer, buffer_size,
+				serial->type->write_int_callback, port,
+				endpoint->bInterval);
+		}
+	} else if (num_interrupt_out) {
+		dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");
+	}
+
+	/* if this device type has an attach function, call it */
+	if (type->attach) {
+		retval = type->attach(serial);
+		if (retval < 0)
+			goto probe_error;
+		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;
+	}
+
+	/* Avoid race with tty_open and serial_install by setting the
+	 * disconnected flag and not clearing it until all ports have been
+	 * registered.
+	 */
+	serial->disconnected = 1;
+
+	if (get_free_serial(serial, num_ports, &minor) == NULL) {
+		dev_err(&interface->dev, "No more free serial devices\n");
+		goto probe_error;
+	}
+	serial->minor = minor;
+
+	/* 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->number);
+		dbg ("%s - registering %s", __func__, dev_name(&port->dev));
+		device_enable_async_suspend(&port->dev);
+
+		retval = device_add(&port->dev);
+		if (retval)
+			dev_err(&port->dev, "Error registering port device, "
+				"continuing\n");
+	}
+
+	serial->disconnected = 0;
+
+	usb_serial_console_init(debug, minor);
+
+exit:
+	/* success */
+	usb_set_intfdata(interface, serial);
+	module_put(type->driver.owner);
+	return 0;
+
+probe_error:
+	usb_serial_put(serial);
+	module_put(type->driver.owner);
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(usb_serial_probe);
+
+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;
+
+	usb_serial_console_disconnect(serial);
+	dbg("%s", __func__);
+
+	mutex_lock(&serial->disc_mutex);
+	usb_set_intfdata(interface, NULL);
+	/* 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];
+		if (port) {
+			struct tty_struct *tty = tty_port_tty_get(&port->port);
+			if (tty) {
+				tty_vhangup(tty);
+				tty_kref_put(tty);
+			}
+			kill_traffic(port);
+			cancel_work_sync(&port->work);
+			if (device_is_registered(&port->dev))
+				device_del(&port->dev);
+		}
+	}
+	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");
+}
+EXPORT_SYMBOL_GPL(usb_serial_disconnect);
+
+int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	struct usb_serial_port *port;
+	int i, r = 0;
+
+	serial->suspending = 1;
+
+	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) {
+		port = serial->port[i];
+		if (port)
+			kill_traffic(port);
+	}
+
+err_out:
+	return r;
+}
+EXPORT_SYMBOL(usb_serial_suspend);
+
+int usb_serial_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	int rv;
+
+	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 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,
+	.tiocmget =		serial_tiocmget,
+	.tiocmset =		serial_tiocmset,
+	.get_icount =		serial_get_icount,
+	.cleanup =		serial_cleanup,
+	.install =		serial_install,
+	.proc_fops =		&serial_proc_fops,
+};
+
+
+struct tty_driver *usb_serial_tty_driver;
+
+static int __init usb_serial_init(void)
+{
+	int i;
+	int result;
+
+	usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
+	if (!usb_serial_tty_driver)
+		return -ENOMEM;
+
+	/* Initialize our global data */
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i)
+		serial_table[i] = NULL;
+
+	result = bus_register(&usb_serial_bus_type);
+	if (result) {
+		printk(KERN_ERR "usb-serial: %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 = 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) {
+		printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
+		       __func__);
+		goto exit_reg_driver;
+	}
+
+	/* register the USB driver */
+	result = usb_register(&usb_serial_driver);
+	if (result < 0) {
+		printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
+		       __func__);
+		goto exit_tty;
+	}
+
+	/* register the generic driver, if we should */
+	result = usb_serial_generic_register(debug);
+	if (result < 0) {
+		printk(KERN_ERR "usb-serial: %s - registering generic "
+		       "driver failed\n", __func__);
+		goto exit_generic;
+	}
+
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
+
+	return result;
+
+exit_generic:
+	usb_deregister(&usb_serial_driver);
+
+exit_tty:
+	tty_unregister_driver(usb_serial_tty_driver);
+
+exit_reg_driver:
+	bus_unregister(&usb_serial_bus_type);
+
+exit_bus:
+	printk(KERN_ERR "usb-serial: %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();
+
+	usb_deregister(&usb_serial_driver);
+	tty_unregister_driver(usb_serial_tty_driver);
+	put_tty_driver(usb_serial_tty_driver);
+	bus_unregister(&usb_serial_bus_type);
+}
+
+
+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;	\
+			dbg("Had to override the " #function		\
+				" usb serial operation with the generic one.");\
+			}						\
+	} while (0)
+
+static void fixup_generic(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);
+	set_to_generic_if_null(device, read_bulk_callback);
+	set_to_generic_if_null(device, write_bulk_callback);
+	set_to_generic_if_null(device, disconnect);
+	set_to_generic_if_null(device, release);
+	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;
+
+	fixup_generic(driver);
+
+	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;
+	}
+
+	/* 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) {
+		printk(KERN_ERR "usb-serial: problem %d when registering "
+		       "driver %s\n", retval, driver->description);
+		list_del(&driver->driver_list);
+	} else
+		printk(KERN_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)
+{
+	printk(KERN_INFO "USB Serial deregistering driver %s\n",
+	       device->description);
+	mutex_lock(&table_lock);
+	list_del(&device->driver_list);
+	usb_serial_bus_deregister(device);
+	mutex_unlock(&table_lock);
+}
+
+/**
+ * usb_serial_register_drivers - register drivers for a usb-serial module
+ * @udriver: usb_driver used for matching devices/interfaces
+ * @serial_drivers: NULL-terminated array of pointers to drivers to be registered
+ *
+ * Registers @udriver and all the drivers in the @serial_drivers array.
+ * Automatically fills in the .no_dynamic_id and PM fields in @udriver and
+ * the .usb_driver field in each serial driver.
+ */
+int usb_serial_register_drivers(struct usb_driver *udriver,
+		struct usb_serial_driver * const serial_drivers[])
+{
+	int rc;
+	const struct usb_device_id *saved_id_table;
+	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.
+	 * Therefore save off udriver's id_table until we are all set.
+	 *
+	 * Suspend/resume support is implemented in the usb-serial core,
+	 * so fill in the PM-related fields in udriver.
+	 */
+	saved_id_table = udriver->id_table;
+	udriver->id_table = NULL;
+
+	udriver->no_dynamic_id = 1;
+	udriver->supports_autosuspend = 1;
+	udriver->suspend = usb_serial_suspend;
+	udriver->resume = usb_serial_resume;
+	rc = usb_register(udriver);
+	if (rc)
+		return rc;
+
+	for (sd = serial_drivers; *sd; ++sd) {
+		(*sd)->usb_driver = udriver;
+		rc = usb_serial_register(*sd);
+		if (rc)
+			goto failed;
+	}
+
+	/* Now restore udriver's id_table and look for matches */
+	udriver->id_table = saved_id_table;
+	rc = driver_attach(&udriver->drvwrap.driver);
+	return 0;
+
+ failed:
+	while (sd-- > serial_drivers)
+		usb_serial_deregister(*sd);
+	usb_deregister(udriver);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(usb_serial_register_drivers);
+
+/**
+ * usb_serial_deregister_drivers - deregister drivers for a usb-serial module
+ * @udriver: usb_driver to unregister
+ * @serial_drivers: NULL-terminated array of pointers to drivers to be deregistered
+ *
+ * Deregisters @udriver and all the drivers in the @serial_drivers array.
+ */
+void usb_serial_deregister_drivers(struct usb_driver *udriver,
+		struct usb_serial_driver * const serial_drivers[])
+{
+	for (; *serial_drivers; ++serial_drivers)
+		usb_serial_deregister(*serial_drivers);
+	usb_deregister(udriver);
+}
+EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
+
+/* Module information */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-wwan.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-wwan.h
new file mode 100644
index 0000000..c47b6ec
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb-wwan.h
@@ -0,0 +1,69 @@
+/*
+ * 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_startup(struct usb_serial *serial);
+extern void usb_wwan_disconnect(struct usb_serial *serial);
+extern void usb_wwan_release(struct usb_serial *serial);
+extern int usb_wwan_write_room(struct tty_struct *tty);
+extern void usb_wwan_set_termios(struct tty_struct *tty,
+				 struct usb_serial_port *port,
+				 struct ktermios *old);
+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_send_setup(struct usb_serial_port *port);
+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;
+	int in_flight;
+	int (*send_setup) (struct usb_serial_port *port);
+	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 */
+	int opened;
+	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/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_debug.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_debug.c
new file mode 100644
index 0000000..e3e8995
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_debug.c
@@ -0,0 +1,87 @@
+/*
+ * USB Debug cable driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/init.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 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) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver debug_driver = {
+	.name =		"debug",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+/* 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 * const serial_drivers[] = {
+	&debug_device, NULL
+};
+
+module_usb_serial_driver(debug_driver, serial_drivers);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_wwan.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_wwan.c
new file mode 100644
index 0000000..6c6bc61
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/usb_wwan.c
@@ -0,0 +1,786 @@
+/*
+  USB Driver layer for GSM modems
+
+  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
+
+  This driver is free software; you can redistribute it and/or modify
+  it under the terms of Version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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_VERSION "v0.7.2"
+#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"
+
+#define ZTE_VENDOR_ID				0x19d2
+static bool debug;
+
+void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct usb_wwan_port_private *portdata;
+
+	struct usb_wwan_intf_private *intfdata;
+
+	dbg("%s", __func__);
+
+	intfdata = port->serial->private;
+
+	if (!intfdata->send_setup)
+		return;
+
+	portdata = usb_get_serial_port_data(port);
+	/* FIXME: locking */
+	portdata->rts_state = on;
+	portdata->dtr_state = on;
+
+	intfdata->send_setup(port);
+}
+EXPORT_SYMBOL(usb_wwan_dtr_rts);
+
+void usb_wwan_set_termios(struct tty_struct *tty,
+			  struct usb_serial_port *port,
+			  struct ktermios *old_termios)
+{
+	struct usb_wwan_intf_private *intfdata = port->serial->private;
+
+	dbg("%s", __func__);
+
+	/* Doesn't support option setting */
+	tty_termios_copy_hw(tty->termios, old_termios);
+
+	if (intfdata->send_setup)
+		intfdata->send_setup(port);
+}
+EXPORT_SYMBOL(usb_wwan_set_termios);
+
+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 = port->serial->private;
+
+	if (!intfdata->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 intfdata->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;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line            = port->serial->minor;
+	tmp.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;
+
+	dbg("%s cmd 0x%04x", __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;
+	}
+
+	dbg("%s arg not supported", __func__);
+
+	return -ENOIOCTLCMD;
+}
+EXPORT_SYMBOL(usb_wwan_ioctl);
+
+/* Write */
+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 = port->serial->private;
+
+	dbg("%s: write (%d chars)", __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;
+		}
+		dbg("%s: endpoint %d buf %d", __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) {
+				dbg("usb_submit_urb %p (write bulk) failed "
+				    "(%d)", this_urb, 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;
+	dbg("%s: wrote (did %d)", __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 tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg("%s: %p", __func__, urb);
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+	port = urb->context;
+
+	if (status) {
+		dbg("%s: nonzero status: %d on endpoint %02x.",
+		    __func__, status, endpoint);
+	} else {
+		tty = tty_port_tty_get(&port->port);
+		if (tty) {
+			if (urb->actual_length) {
+				tty_insert_flip_string(tty, data,
+						urb->actual_length);
+				tty_flip_buffer_push(tty);
+			} else
+				dbg("%s: empty read urb received", __func__);
+			tty_kref_put(tty);
+		}
+
+		/* Resubmit urb so we continue receiving */
+		if (status != -ESHUTDOWN) {
+			err = usb_submit_urb(urb, GFP_ATOMIC);
+			if (err) {
+				if (err != -EPERM) {
+					printk(KERN_ERR "%s: resubmit read urb failed. "
+						"(%d)", __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;
+	int i;
+
+	dbg("%s", __func__);
+
+	port = urb->context;
+	intfdata = port->serial->private;
+
+	usb_serial_port_softint(port);
+	usb_autopm_put_interface_async(port->serial->interface);
+	portdata = usb_get_serial_port_data(port);
+	spin_lock(&intfdata->susp_lock);
+	intfdata->in_flight--;
+	spin_unlock(&intfdata->susp_lock);
+
+	for (i = 0; i < N_OUT_URB; ++i) {
+		if (portdata->out_urbs[i] == urb) {
+			smp_mb__before_clear_bit();
+			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;
+	}
+
+	dbg("%s: %d", __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;
+	}
+	dbg("%s: %d", __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 = serial->private;
+
+	dbg("%s", __func__);
+
+	if (port->interrupt_in_urb) {
+		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (err) {
+			dev_dbg(&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) {
+			dbg("%s: submit urb %d failed (%d) %d",
+			    __func__, i, err, urb->transfer_buffer_length);
+		}
+	}
+
+	if (intfdata->send_setup)
+		intfdata->send_setup(port);
+
+	serial->interface->needs_remote_wakeup = 1;
+	spin_lock_irq(&intfdata->susp_lock);
+	portdata->opened = 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 = port->serial->private;
+	struct urb *urb;
+
+	dbg("%s", __func__);
+	portdata = usb_get_serial_port_data(port);
+
+	if (serial->dev) {
+		/* Stop reading/writing urbs */
+		spin_lock_irq(&intfdata->susp_lock);
+		portdata->opened = 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);
+		/* balancing - important as an error cannot be handled*/
+		usb_autopm_get_interface_no_resume(serial->interface);
+		serial->interface->needs_remote_wakeup = 0;
+	}
+}
+EXPORT_SYMBOL(usb_wwan_close);
+
+/* Helper functions used by usb_wwan_setup_urbs */
+static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
+				      int dir, void *ctx, char *buf, int len,
+				      void (*callback) (struct urb *))
+{
+	struct urb *urb;
+
+	if (endpoint == -1)
+		return NULL;	/* endpoint not needed */
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
+	if (urb == NULL) {
+		dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
+		return NULL;
+	}
+
+	/* Fill URB using supplied data. */
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
+			  buf, len, callback, ctx);
+
+	return urb;
+}
+
+/* Setup urbs */
+static void usb_wwan_setup_urbs(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_port_private *portdata;
+
+	dbg("%s", __func__);
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+
+		/* Do indat endpoints first */
+		for (j = 0; j < N_IN_URB; ++j) {
+			portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
+								  port->
+								  bulk_in_endpointAddress,
+								  USB_DIR_IN,
+								  port,
+								  portdata->
+								  in_buffer[j],
+								  IN_BUFLEN,
+								  usb_wwan_indat_callback);
+		}
+
+		/* outdat endpoints */
+		for (j = 0; j < N_OUT_URB; ++j) {
+			portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
+								   port->
+								   bulk_out_endpointAddress,
+								   USB_DIR_OUT,
+								   port,
+								   portdata->
+								   out_buffer
+								   [j],
+								   OUT_BUFLEN,
+								   usb_wwan_outdat_callback);
+		}
+	}
+}
+
+int usb_wwan_startup(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_port_private *portdata;
+	u8 *buffer;
+
+	dbg("%s", __func__);
+
+	/* Now setup per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+		if (!portdata) {
+			dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
+			    __func__, i);
+			return 1;
+		}
+		init_usb_anchor(&portdata->delayed);
+
+		for (j = 0; j < N_IN_URB; j++) {
+			buffer = (u8 *) __get_free_page(GFP_KERNEL);
+			if (!buffer)
+				goto bail_out_error;
+			portdata->in_buffer[j] = buffer;
+		}
+
+		for (j = 0; j < N_OUT_URB; j++) {
+			buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+			if (!buffer)
+				goto bail_out_error2;
+			portdata->out_buffer[j] = buffer;
+		}
+
+		usb_set_serial_port_data(port, portdata);
+	}
+	usb_wwan_setup_urbs(serial);
+	return 0;
+
+bail_out_error2:
+	for (j = 0; j < N_OUT_URB; j++)
+		kfree(portdata->out_buffer[j]);
+bail_out_error:
+	for (j = 0; j < N_IN_URB; j++)
+		if (portdata->in_buffer[j])
+			free_page((unsigned long)portdata->in_buffer[j]);
+	kfree(portdata);
+	return 1;
+}
+EXPORT_SYMBOL(usb_wwan_startup);
+
+static void stop_read_write_urbs(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_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);
+		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]);
+	}
+}
+
+void usb_wwan_disconnect(struct usb_serial *serial)
+{
+	dbg("%s", __func__);
+
+	stop_read_write_urbs(serial);
+}
+EXPORT_SYMBOL(usb_wwan_disconnect);
+
+void usb_wwan_release(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_port_private *portdata;
+
+	dbg("%s", __func__);
+
+	/* Now free them */
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+
+		for (j = 0; j < N_IN_URB; j++) {
+			usb_free_urb(portdata->in_urbs[j]);
+			free_page((unsigned long)
+				  portdata->in_buffer[j]);
+			portdata->in_urbs[j] = NULL;
+		}
+		for (j = 0; j < N_OUT_URB; j++) {
+			usb_free_urb(portdata->out_urbs[j]);
+			kfree(portdata->out_buffer[j]);
+			portdata->out_urbs[j] = NULL;
+		}
+	}
+
+	/* Now free per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		kfree(usb_get_serial_port_data(port));
+	}
+}
+EXPORT_SYMBOL(usb_wwan_release);
+
+#ifdef CONFIG_PM
+int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
+{
+	struct usb_wwan_intf_private *intfdata = serial->private;
+
+	dbg("%s entered", __func__);
+
+	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;
+}
+EXPORT_SYMBOL(usb_wwan_suspend);
+
+static int play_delayed(struct usb_serial_port *port)
+{
+	struct usb_wwan_intf_private *data;
+	struct usb_wwan_port_private *portdata;
+	struct urb *urb;
+	int err = 0;
+
+	portdata = usb_get_serial_port_data(port);
+	data = port->serial->private;
+	while ((urb = usb_get_from_anchor(&portdata->delayed))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (!err) {
+			data->in_flight++;
+		} else {
+			/* we have to throw away the rest */
+			do {
+				unbusy_queued_urb(urb, portdata);
+				usb_autopm_put_interface_no_suspend(port->serial->interface);
+			} while ((urb = usb_get_from_anchor(&portdata->delayed)));
+			break;
+		}
+	}
+
+	return err;
+}
+
+int usb_wwan_resume(struct usb_serial *serial)
+{
+	int i, j;
+	struct usb_serial_port *port;
+	struct usb_wwan_intf_private *intfdata = serial->private;
+	struct usb_wwan_port_private *portdata;
+	struct urb *urb;
+	int err;
+	int err_count = 0;
+
+	dbg("%s entered", __func__);
+
+	spin_lock_irq(&intfdata->susp_lock);
+	for (i = 0; i < serial->num_ports; i++) {
+		/* walk all ports */
+		port = serial->port[i];
+		portdata = usb_get_serial_port_data(port);
+
+		/* skip closed ports */
+		if (!portdata || !portdata->opened)
+			continue;
+
+		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 = play_delayed(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) {
+				err("%s: Error %d for bulk URB %d",
+				    __func__, err, i);
+				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_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.c
new file mode 100644
index 0000000..c5cf350
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.c
@@ -0,0 +1,725 @@
+/*
+ * 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)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ *
+ * 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/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);
+static void visor_read_int_callback(struct urb *urb);
+static int  clie_3_5_startup(struct usb_serial *serial);
+static int  treo_attach(struct usb_serial *serial);
+static int clie_5_attach(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);
+
+/* Parameters that may be passed into the module. */
+static bool debug;
+static __u16 vendor;
+static __u16 product;
+
+static 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 },
+	{ },					/* optional parameter entry */
+	{ }					/* Terminating entry */
+};
+
+static 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 },
+	{ },					/* optional parameter entry */
+	{ }					/* Terminating entry */
+};
+
+static struct usb_device_id clie_id_3_5_table [] = {
+	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
+	{ }					/* Terminating entry */
+};
+
+static 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) },
+	{ },					/* optional parameter entry */
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+static struct usb_driver visor_driver = {
+	.name =		"visor",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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,
+	.attach =		treo_attach,
+	.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,
+	.bulk_out_size =	256,
+	.open =			visor_open,
+	.close =		visor_close,
+	.throttle =		usb_serial_generic_throttle,
+	.unthrottle =		usb_serial_generic_unthrottle,
+	.attach =		clie_5_attach,
+	.probe =		visor_probe,
+	.calc_num_ports =	visor_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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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) {
+		dbg("%s - adding interrupt input for treo", __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);
+	}
+exit:
+	return result;
+}
+
+
+static void visor_close(struct usb_serial_port *port)
+{
+	unsigned char *transfer_buffer;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	/* shutdown our urbs */
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	mutex_lock(&port->serial->disc_mutex);
+	if (!port->serial->disconnected) {
+		/* Try to send shutdown message, unless the device is gone */
+		transfer_buffer =  kmalloc(0x12, GFP_KERNEL);
+		if (transfer_buffer) {
+			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);
+		}
+	}
+	mutex_unlock(&port->serial->disc_mutex);
+}
+
+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 */
+		dbg("%s - urb shutting down with status: %d",
+		    __func__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __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(debug, &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;
+
+	dbg("%s", __func__);
+
+	transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__,
+			sizeof(*connection_info));
+		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)) {
+			connection_info = (struct visor_connection_info *)
+							transfer_buffer;
+
+		num_ports = le16_to_cpu(connection_info->num_ports);
+		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);
+		}
+	}
+	/*
+	* 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;
+	}
+
+	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;
+
+	dbg("%s", __func__);
+
+	transfer_buffer =  kmalloc(sizeof(*connection_info), GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__,
+			sizeof(*connection_info));
+		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(debug, &serial->dev->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);
+
+	dbg("%s", __func__);
+
+	/*
+	 * 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)
+{
+	int num_ports = (int)(long)(usb_get_serial_data(serial));
+
+	if (num_ports)
+		usb_set_serial_data(serial, NULL);
+
+	return num_ports;
+}
+
+static int clie_3_5_startup(struct usb_serial *serial)
+{
+	struct device *dev = &serial->dev->dev;
+	int result;
+	u8 *data;
+
+	dbg("%s", __func__);
+
+	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;
+}
+
+static int treo_attach(struct usb_serial *serial)
+{
+	struct usb_serial_port *swap_port;
+
+	/* Only do this endpoint hack for the Handspring devices with
+	 * interrupt in endpoints, which for now are the Treo devices. */
+	if (!((le16_to_cpu(serial->dev->descriptor.idVendor)
+						== HANDSPRING_VENDOR_ID) ||
+		(le16_to_cpu(serial->dev->descriptor.idVendor)
+						== KYOCERA_VENDOR_ID)) ||
+		(serial->num_interrupt_in == 0))
+		return 0;
+
+	dbg("%s", __func__);
+
+	/*
+	* 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.
+	*/
+#define COPY_PORT(dest, src)						\
+	do { \
+		int i;							\
+									\
+		for (i = 0; i < ARRAY_SIZE(src->read_urbs); ++i) {	\
+			dest->read_urbs[i] = src->read_urbs[i];		\
+			dest->read_urbs[i]->context = dest;		\
+			dest->bulk_in_buffers[i] = src->bulk_in_buffers[i]; \
+		}							\
+		dest->read_urb = src->read_urb;				\
+		dest->bulk_in_endpointAddress = src->bulk_in_endpointAddress;\
+		dest->bulk_in_buffer = src->bulk_in_buffer;		\
+		dest->bulk_in_size = src->bulk_in_size;			\
+		dest->interrupt_in_urb = src->interrupt_in_urb;		\
+		dest->interrupt_in_urb->context = dest;			\
+		dest->interrupt_in_endpointAddress = \
+					src->interrupt_in_endpointAddress;\
+		dest->interrupt_in_buffer = src->interrupt_in_buffer;	\
+	} while (0);
+
+	swap_port = kmalloc(sizeof(*swap_port), GFP_KERNEL);
+	if (!swap_port)
+		return -ENOMEM;
+	COPY_PORT(swap_port, serial->port[0]);
+	COPY_PORT(serial->port[0], serial->port[1]);
+	COPY_PORT(serial->port[1], swap_port);
+	kfree(swap_port);
+
+	return 0;
+}
+
+static int clie_5_attach(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	unsigned int pipe;
+	int j;
+
+	dbg("%s", __func__);
+
+	/* TH55 registers 2 ports.
+	   Communication in from the UX50/TH55 uses bulk_in_endpointAddress
+	   from port 0. Communication out to the UX50/TH55 uses
+	   bulk_out_endpointAddress from port 1
+
+	   Lets do a quick and dirty mapping
+	 */
+
+	/* some sanity check */
+	if (serial->num_ports < 2)
+		return -1;
+
+	/* port 0 now uses the modified endpoint Address */
+	port = serial->port[0];
+	port->bulk_out_endpointAddress =
+				serial->port[1]->bulk_out_endpointAddress;
+
+	pipe = usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress);
+	for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j)
+		port->write_urbs[j]->pipe = pipe;
+
+	return 0;
+}
+
+static int __init visor_init(void)
+{
+	int i, retval;
+	/* Only if parameters were passed to us */
+	if (vendor > 0 && product > 0) {
+		struct usb_device_id usb_dev_temp[] = {
+			{
+				USB_DEVICE(vendor, product),
+				.driver_info =
+					(kernel_ulong_t) &palm_os_4_probe
+			}
+		};
+
+		/* Find the last entry in id_table */
+		for (i = 0;; i++) {
+			if (id_table[i].idVendor == 0) {
+				id_table[i] = usb_dev_temp[0];
+				break;
+			}
+		}
+		/* Find the last entry in id_table_combined */
+		for (i = 0;; i++) {
+			if (id_table_combined[i].idVendor == 0) {
+				id_table_combined[i] = usb_dev_temp[0];
+				break;
+			}
+		}
+		printk(KERN_INFO KBUILD_MODNAME
+		       ": Untested USB device specified at time of module insertion\n");
+		printk(KERN_INFO KBUILD_MODNAME
+		       ": Warning: This is not guaranteed to work\n");
+		printk(KERN_INFO KBUILD_MODNAME
+		       ": Using a newer kernel is preferred to this method\n");
+		printk(KERN_INFO KBUILD_MODNAME
+		       ": Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x\n",
+			vendor, product);
+	}
+
+	retval = usb_serial_register_drivers(&visor_driver, serial_drivers);
+	if (retval == 0)
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
+	return retval;
+}
+
+
+static void __exit visor_exit (void)
+{
+	usb_serial_deregister_drivers(&visor_driver, serial_drivers);
+}
+
+
+module_init(visor_init);
+module_exit(visor_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "User specified vendor ID");
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "User specified product ID");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.h
new file mode 100644
index 0000000..88db4d0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/visor.h
@@ -0,0 +1,161 @@
+/*
+ * USB HandSpring Visor driver
+ *
+ *	Copyright (C) 1999 - 2003
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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 applicaton 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/vivopay-serial.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/vivopay-serial.c
new file mode 100644
index 0000000..078f338
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/vivopay-serial.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2001-2005 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2009 Outpost Embedded, LLC
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_DESC "ViVOpay USB Serial Driver"
+
+#define VIVOPAY_VENDOR_ID 0x1d5f
+
+
+static struct usb_device_id id_table [] = {
+	/* ViVOpay 8800 */
+	{ USB_DEVICE(VIVOPAY_VENDOR_ID, 0x1004) },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver vivopay_serial_driver = {
+	.name =			"vivopay-serial",
+	.probe =		usb_serial_probe,
+	.disconnect =		usb_serial_disconnect,
+	.id_table =		id_table,
+};
+
+static struct usb_serial_driver vivopay_serial_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"vivopay-serial",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&vivopay_serial_device, NULL
+};
+
+module_usb_serial_driver(vivopay_serial_driver, serial_drivers);
+
+MODULE_AUTHOR("Forest Bond <forest.bond@outpostembedded.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.c
new file mode 100644
index 0000000..bf7014d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.c
@@ -0,0 +1,1476 @@
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *	Copyright (C) 2002
+ *	    Connect Tech Inc.
+ *
+ *	Copyright (C) 1999 - 2001
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 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/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/firmware.h>
+#include <linux/ihex.h>
+#include "whiteheat.h"			/* WhiteHEAT specific commands */
+
+static bool debug;
+
+#ifndef CMSPAR
+#define CMSPAR 0
+#endif
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.0"
+#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);
+
+static struct usb_driver whiteheat_driver = {
+	.name =		"whiteheat",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	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_open(struct tty_struct *tty,
+			struct usb_serial_port *port);
+static void whiteheat_close(struct usb_serial_port *port);
+static int  whiteheat_write(struct tty_struct *tty,
+			struct usb_serial_port *port,
+			const unsigned char *buf, int count);
+static int  whiteheat_write_room(struct tty_struct *tty);
+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 int  whiteheat_chars_in_buffer(struct tty_struct *tty);
+static void whiteheat_throttle(struct tty_struct *tty);
+static void whiteheat_unthrottle(struct tty_struct *tty);
+static void whiteheat_read_callback(struct urb *urb);
+static void whiteheat_write_callback(struct urb *urb);
+
+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,
+	.attach =		whiteheat_attach,
+	.release =		whiteheat_release,
+	.open =			whiteheat_open,
+	.close =		whiteheat_close,
+	.write =		whiteheat_write,
+	.write_room =		whiteheat_write_room,
+	.ioctl =		whiteheat_ioctl,
+	.set_termios =		whiteheat_set_termios,
+	.break_ctl =		whiteheat_break_ctl,
+	.tiocmget =		whiteheat_tiocmget,
+	.tiocmset =		whiteheat_tiocmset,
+	.chars_in_buffer =	whiteheat_chars_in_buffer,
+	.throttle =		whiteheat_throttle,
+	.unthrottle =		whiteheat_unthrottle,
+	.read_bulk_callback =	whiteheat_read_callback,
+	.write_bulk_callback =	whiteheat_write_callback,
+};
+
+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];
+};
+
+
+#define THROTTLED		0x01
+#define ACTUALLY_THROTTLED	0x02
+
+static int urb_pool_size = 8;
+
+struct whiteheat_urb_wrap {
+	struct list_head	list;
+	struct urb		*urb;
+};
+
+struct whiteheat_private {
+	spinlock_t		lock;
+	__u8			flags;
+	__u8			mcr;		/* FIXME: no locking on mcr */
+	struct list_head	rx_urbs_free;
+	struct list_head	rx_urbs_submitted;
+	struct list_head	rx_urb_q;
+	struct work_struct	rx_work;
+	struct usb_serial_port	*port;
+	struct list_head	tx_urbs_free;
+	struct list_head	tx_urbs_submitted;
+	struct mutex		deathwarrant;
+};
+
+
+/* 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 start_port_read(struct usb_serial_port *port);
+static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
+						struct list_head *head);
+static struct list_head *list_first(struct list_head *head);
+static void rx_data_softint(struct work_struct *work);
+
+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, ret = -ENOENT;
+	const struct firmware *loader_fw = NULL, *firmware_fw = NULL;
+	const struct ihex_binrec *record;
+
+	dbg("%s", __func__);
+
+	if (request_ihex_firmware(&firmware_fw, "whiteheat.fw",
+				  &serial->dev->dev)) {
+		dev_err(&serial->dev->dev,
+			"%s - request \"whiteheat.fw\" failed\n", __func__);
+		goto out;
+	}
+	if (request_ihex_firmware(&loader_fw, "whiteheat_loader.fw",
+			     &serial->dev->dev)) {
+		dev_err(&serial->dev->dev,
+			"%s - request \"whiteheat_loader.fw\" failed\n",
+			__func__);
+		goto out;
+	}
+	ret = 0;
+	response = ezusb_set_reset (serial, 1);
+
+	record = (const struct ihex_binrec *)loader_fw->data;
+	while (record) {
+		response = ezusb_writememory (serial, be32_to_cpu(record->addr),
+					      (unsigned char *)record->data,
+					      be16_to_cpu(record->len), 0xa0);
+		if (response < 0) {
+			dev_err(&serial->dev->dev, "%s - ezusb_writememory "
+				"failed for loader (%d %04X %p %d)\n",
+				__func__, response, be32_to_cpu(record->addr),
+				record->data, be16_to_cpu(record->len));
+			break;
+		}
+		record = ihex_next_binrec(record);
+	}
+
+	response = ezusb_set_reset(serial, 0);
+
+	record = (const struct ihex_binrec *)firmware_fw->data;
+	while (record && be32_to_cpu(record->addr) < 0x1b40)
+		record = ihex_next_binrec(record);
+	while (record) {
+		response = ezusb_writememory (serial, be32_to_cpu(record->addr),
+					      (unsigned char *)record->data,
+					      be16_to_cpu(record->len), 0xa3);
+		if (response < 0) {
+			dev_err(&serial->dev->dev, "%s - ezusb_writememory "
+				"failed for first firmware step "
+				"(%d %04X %p %d)\n", __func__, response,
+				be32_to_cpu(record->addr), record->data,
+				be16_to_cpu(record->len));
+			break;
+		}
+		++record;
+	}
+
+	response = ezusb_set_reset(serial, 1);
+
+	record = (const struct ihex_binrec *)firmware_fw->data;
+	while (record && be32_to_cpu(record->addr) < 0x1b40) {
+		response = ezusb_writememory (serial, be32_to_cpu(record->addr),
+					      (unsigned char *)record->data,
+					      be16_to_cpu(record->len), 0xa0);
+		if (response < 0) {
+			dev_err(&serial->dev->dev, "%s - ezusb_writememory "
+				"failed for second firmware step "
+				"(%d %04X %p %d)\n", __func__, response,
+				be32_to_cpu(record->addr), record->data,
+				be16_to_cpu(record->len));
+			break;
+		}
+		++record;
+	}
+	ret = 0;
+	response = ezusb_set_reset (serial, 0);
+ out:
+	release_firmware(loader_fw);
+	release_firmware(firmware_fw);
+	return ret;
+}
+
+
+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 usb_serial_port *port;
+	struct whiteheat_private *info;
+	struct whiteheat_hw_info *hw_info;
+	int pipe;
+	int ret;
+	int alen;
+	__u8 *command;
+	__u8 *result;
+	int i;
+	int j;
+	struct urb *urb;
+	int buf_size;
+	struct whiteheat_urb_wrap *wrap;
+	struct list_head *tmp;
+
+	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: Driver %s: Firmware v%d.%02d\n",
+		 serial->type->description, DRIVER_VERSION,
+		 hw_info->sw_major_rev, hw_info->sw_minor_rev);
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+
+		info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
+		if (info == NULL) {
+			dev_err(&port->dev,
+				"%s: Out of memory for port structures\n",
+				serial->type->description);
+			goto no_private;
+		}
+
+		spin_lock_init(&info->lock);
+		mutex_init(&info->deathwarrant);
+		info->flags = 0;
+		info->mcr = 0;
+		INIT_WORK(&info->rx_work, rx_data_softint);
+		info->port = port;
+
+		INIT_LIST_HEAD(&info->rx_urbs_free);
+		INIT_LIST_HEAD(&info->rx_urbs_submitted);
+		INIT_LIST_HEAD(&info->rx_urb_q);
+		INIT_LIST_HEAD(&info->tx_urbs_free);
+		INIT_LIST_HEAD(&info->tx_urbs_submitted);
+
+		for (j = 0; j < urb_pool_size; j++) {
+			urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!urb) {
+				dev_err(&port->dev, "No free urbs available\n");
+				goto no_rx_urb;
+			}
+			buf_size = port->read_urb->transfer_buffer_length;
+			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
+			if (!urb->transfer_buffer) {
+				dev_err(&port->dev,
+					"Couldn't allocate urb buffer\n");
+				goto no_rx_buf;
+			}
+			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
+			if (!wrap) {
+				dev_err(&port->dev,
+					"Couldn't allocate urb wrapper\n");
+				goto no_rx_wrap;
+			}
+			usb_fill_bulk_urb(urb, serial->dev,
+					usb_rcvbulkpipe(serial->dev,
+						port->bulk_in_endpointAddress),
+					urb->transfer_buffer, buf_size,
+					whiteheat_read_callback, port);
+			wrap->urb = urb;
+			list_add(&wrap->list, &info->rx_urbs_free);
+
+			urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!urb) {
+				dev_err(&port->dev, "No free urbs available\n");
+				goto no_tx_urb;
+			}
+			buf_size = port->write_urb->transfer_buffer_length;
+			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
+			if (!urb->transfer_buffer) {
+				dev_err(&port->dev,
+					"Couldn't allocate urb buffer\n");
+				goto no_tx_buf;
+			}
+			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
+			if (!wrap) {
+				dev_err(&port->dev,
+					"Couldn't allocate urb wrapper\n");
+				goto no_tx_wrap;
+			}
+			usb_fill_bulk_urb(urb, serial->dev,
+					usb_sndbulkpipe(serial->dev,
+						port->bulk_out_endpointAddress),
+					urb->transfer_buffer, buf_size,
+					whiteheat_write_callback, port);
+			wrap->urb = urb;
+			list_add(&wrap->list, &info->tx_urbs_free);
+		}
+
+		usb_set_serial_port_data(port, info);
+	}
+
+	command_info = kmalloc(sizeof(struct whiteheat_command_private),
+								GFP_KERNEL);
+	if (command_info == NULL) {
+		dev_err(&serial->dev->dev,
+			"%s: Out of memory for port structures\n",
+			serial->type->description);
+		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:
+	for (i = serial->num_ports - 1; i >= 0; i--) {
+		port = serial->port[i];
+		info = usb_get_serial_port_data(port);
+		for (j = urb_pool_size - 1; j >= 0; j--) {
+			tmp = list_first(&info->tx_urbs_free);
+			list_del(tmp);
+			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+			urb = wrap->urb;
+			kfree(wrap);
+no_tx_wrap:
+			kfree(urb->transfer_buffer);
+no_tx_buf:
+			usb_free_urb(urb);
+no_tx_urb:
+			tmp = list_first(&info->rx_urbs_free);
+			list_del(tmp);
+			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+			urb = wrap->urb;
+			kfree(wrap);
+no_rx_wrap:
+			kfree(urb->transfer_buffer);
+no_rx_buf:
+			usb_free_urb(urb);
+no_rx_urb:
+			;
+		}
+		kfree(info);
+no_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;
+	struct usb_serial_port *port;
+	struct whiteheat_private *info;
+	struct whiteheat_urb_wrap *wrap;
+	struct urb *urb;
+	struct list_head *tmp;
+	struct list_head *tmp2;
+	int i;
+
+	dbg("%s", __func__);
+
+	/* free up our private data for our command port */
+	command_port = serial->port[COMMAND_PORT];
+	kfree(usb_get_serial_port_data(command_port));
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		info = usb_get_serial_port_data(port);
+		list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
+			list_del(tmp);
+			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+			urb = wrap->urb;
+			kfree(wrap);
+			kfree(urb->transfer_buffer);
+			usb_free_urb(urb);
+		}
+		list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) {
+			list_del(tmp);
+			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+			urb = wrap->urb;
+			kfree(wrap);
+			kfree(urb->transfer_buffer);
+			usb_free_urb(urb);
+		}
+		kfree(info);
+	}
+}
+
+static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+	int		retval = 0;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	retval = start_command_port(port->serial);
+	if (retval)
+		goto exit;
+
+	if (tty)
+		tty->low_latency = 1;
+
+	/* 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);
+
+	/* Start reading from the device */
+	retval = start_port_read(port);
+	if (retval) {
+		dev_err(&port->dev,
+			"%s - failed submitting read urb, error %d\n",
+			__func__, retval);
+		firm_close(port);
+		stop_command_port(port->serial);
+		goto exit;
+	}
+
+exit:
+	dbg("%s - exit, retval = %d", __func__, retval);
+	return retval;
+}
+
+
+static void whiteheat_close(struct usb_serial_port *port)
+{
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct whiteheat_urb_wrap *wrap;
+	struct urb *urb;
+	struct list_head *tmp;
+	struct list_head *tmp2;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	firm_report_tx_done(port);
+	firm_close(port);
+
+	/* shutdown our bulk reads and writes */
+	mutex_lock(&info->deathwarrant);
+	spin_lock_irq(&info->lock);
+	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		urb = wrap->urb;
+		list_del(tmp);
+		spin_unlock_irq(&info->lock);
+		usb_kill_urb(urb);
+		spin_lock_irq(&info->lock);
+		list_add(tmp, &info->rx_urbs_free);
+	}
+	list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
+		list_move(tmp, &info->rx_urbs_free);
+	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		urb = wrap->urb;
+		list_del(tmp);
+		spin_unlock_irq(&info->lock);
+		usb_kill_urb(urb);
+		spin_lock_irq(&info->lock);
+		list_add(tmp, &info->tx_urbs_free);
+	}
+	spin_unlock_irq(&info->lock);
+	mutex_unlock(&info->deathwarrant);
+	stop_command_port(port->serial);
+}
+
+
+static int whiteheat_write(struct tty_struct *tty,
+	struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct whiteheat_urb_wrap *wrap;
+	struct urb *urb;
+	int result;
+	int bytes;
+	int sent = 0;
+	unsigned long flags;
+	struct list_head *tmp;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __func__);
+		return (0);
+	}
+
+	while (count) {
+		spin_lock_irqsave(&info->lock, flags);
+		if (list_empty(&info->tx_urbs_free)) {
+			spin_unlock_irqrestore(&info->lock, flags);
+			break;
+		}
+		tmp = list_first(&info->tx_urbs_free);
+		list_del(tmp);
+		spin_unlock_irqrestore(&info->lock, flags);
+
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		urb = wrap->urb;
+		bytes = (count > port->bulk_out_size) ?
+					port->bulk_out_size : count;
+		memcpy(urb->transfer_buffer, buf + sent, bytes);
+
+		usb_serial_debug_data(debug, &port->dev,
+				__func__, bytes, urb->transfer_buffer);
+
+		urb->transfer_buffer_length = bytes;
+		result = usb_submit_urb(urb, GFP_ATOMIC);
+		if (result) {
+			dev_err_console(port,
+				"%s - failed submitting write urb, error %d\n",
+				__func__, result);
+			sent = result;
+			spin_lock_irqsave(&info->lock, flags);
+			list_add(tmp, &info->tx_urbs_free);
+			spin_unlock_irqrestore(&info->lock, flags);
+			break;
+		} else {
+			sent += bytes;
+			count -= bytes;
+			spin_lock_irqsave(&info->lock, flags);
+			list_add(tmp, &info->tx_urbs_submitted);
+			spin_unlock_irqrestore(&info->lock, flags);
+		}
+	}
+
+	return sent;
+}
+
+static int whiteheat_write_room(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct list_head *tmp;
+	int room = 0;
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&info->lock, flags);
+	list_for_each(tmp, &info->tx_urbs_free)
+		room++;
+	spin_unlock_irqrestore(&info->lock, flags);
+	room *= port->bulk_out_size;
+
+	dbg("%s - returns %d", __func__, room);
+	return (room);
+}
+
+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;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	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;
+
+	dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		memset(&serstruct, 0, sizeof(serstruct));
+		serstruct.type = PORT_16654;
+		serstruct.line = port->serial->minor;
+		serstruct.port = port->number;
+		serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+		serstruct.xmit_fifo_size = port->bulk_out_size;
+		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);
+}
+
+
+static int whiteheat_chars_in_buffer(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct list_head *tmp;
+	struct whiteheat_urb_wrap *wrap;
+	int chars = 0;
+	unsigned long flags;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irqsave(&info->lock, flags);
+	list_for_each(tmp, &info->tx_urbs_submitted) {
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		chars += wrap->urb->transfer_buffer_length;
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	dbg("%s - returns %d", __func__, chars);
+	return chars;
+}
+
+
+static void whiteheat_throttle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irq(&info->lock);
+	info->flags |= THROTTLED;
+	spin_unlock_irq(&info->lock);
+}
+
+
+static void whiteheat_unthrottle(struct tty_struct *tty)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	int actually_throttled;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock_irq(&info->lock);
+	actually_throttled = info->flags & ACTUALLY_THROTTLED;
+	info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+	spin_unlock_irq(&info->lock);
+
+	if (actually_throttled)
+		rx_data_softint(&info->rx_work);
+}
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat callback routines
+ *****************************************************************************/
+static void command_port_write_callback(struct urb *urb)
+{
+	int status = urb->status;
+
+	dbg("%s", __func__);
+
+	if (status) {
+		dbg("nonzero urb status: %d", 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;
+
+	dbg("%s", __func__);
+
+	command_info = usb_get_serial_port_data(command_port);
+	if (!command_info) {
+		dbg("%s - command_info is NULL, exiting.", __func__);
+		return;
+	}
+	if (!urb->actual_length) {
+		dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__);
+		return;
+	}
+	if (status) {
+		dbg("%s - nonzero urb status: %d", __func__, status);
+		if (status != -ENOENT)
+			command_info->command_finished = WHITEHEAT_CMD_FAILURE;
+		wake_up(&command_info->wait_command);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &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 */
+		dbg("%s - event received", __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
+		dbg("%s - bad reply from firmware", __func__);
+
+	/* Continue trying to always read */
+	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
+	if (result)
+		dbg("%s - failed resubmitting read urb, error %d",
+			__func__, result);
+}
+
+
+static void whiteheat_read_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct whiteheat_urb_wrap *wrap;
+	unsigned char *data = urb->transfer_buffer;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock(&info->lock);
+	wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);
+	if (!wrap) {
+		spin_unlock(&info->lock);
+		dev_err(&port->dev, "%s - Not my urb!\n", __func__);
+		return;
+	}
+	list_del(&wrap->list);
+	spin_unlock(&info->lock);
+
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __func__, status);
+		spin_lock(&info->lock);
+		list_add(&wrap->list, &info->rx_urbs_free);
+		spin_unlock(&info->lock);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &port->dev,
+				__func__, urb->actual_length, data);
+
+	spin_lock(&info->lock);
+	list_add_tail(&wrap->list, &info->rx_urb_q);
+	if (info->flags & THROTTLED) {
+		info->flags |= ACTUALLY_THROTTLED;
+		spin_unlock(&info->lock);
+		return;
+	}
+	spin_unlock(&info->lock);
+
+	schedule_work(&info->rx_work);
+}
+
+
+static void whiteheat_write_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct whiteheat_urb_wrap *wrap;
+	int status = urb->status;
+
+	dbg("%s - port %d", __func__, port->number);
+
+	spin_lock(&info->lock);
+	wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
+	if (!wrap) {
+		spin_unlock(&info->lock);
+		dev_err(&port->dev, "%s - Not my urb!\n", __func__);
+		return;
+	}
+	list_move(&wrap->list, &info->tx_urbs_free);
+	spin_unlock(&info->lock);
+
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __func__, status);
+		return;
+	}
+
+	usb_serial_port_softint(port);
+}
+
+
+/*****************************************************************************
+ * 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;
+	__u8 *transfer_buffer;
+	int retval = 0;
+	int t;
+
+	dbg("%s - command %d", __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) {
+		dbg("%s - submit urb failed", __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) {
+		dbg("%s - command timed out.", __func__);
+		retval = -ETIMEDOUT;
+		goto exit;
+	}
+
+	if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
+		dbg("%s - command failed.", __func__);
+		retval = -EIO;
+		goto exit;
+	}
+
+	if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
+		dbg("%s - command completed.", __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->number - port->serial->minor + 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->number - port->serial->minor + 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 whiteheat_port_settings port_settings;
+	unsigned int cflag = tty->termios->c_cflag;
+
+	port_settings.port = port->number - port->serial->minor + 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;
+	}
+	dbg("%s - data bits = %d", __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;
+	dbg("%s - parity = %c", __func__, port_settings.parity);
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		port_settings.stop = 2;
+	else
+		port_settings.stop = 1;
+	dbg("%s - stop bits = %d", __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;
+	dbg("%s - hardware flow control = %s %s %s %s", __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;
+	dbg("%s - software flow control = %c", __func__, port_settings.sflow);
+
+	port_settings.xon = START_CHAR(tty);
+	port_settings.xoff = STOP_CHAR(tty);
+	dbg("%s - XON = %2x, XOFF = %2x",
+			__func__, port_settings.xon, port_settings.xoff);
+
+	/* get the baud rate wanted */
+	port_settings.baud = tty_get_baud_rate(tty);
+	dbg("%s - baud rate = %d", __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->number - port->serial->minor + 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->number - port->serial->minor + 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->number - port->serial->minor + 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->number - port->serial->minor + 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->number - port->serial->minor + 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->number - port->serial->minor + 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);
+}
+
+
+static int start_port_read(struct usb_serial_port *port)
+{
+	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	struct whiteheat_urb_wrap *wrap;
+	struct urb *urb;
+	int retval = 0;
+	unsigned long flags;
+	struct list_head *tmp;
+	struct list_head *tmp2;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
+		list_del(tmp);
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		urb = wrap->urb;
+		spin_unlock_irqrestore(&info->lock, flags);
+		retval = usb_submit_urb(urb, GFP_KERNEL);
+		if (retval) {
+			spin_lock_irqsave(&info->lock, flags);
+			list_add(tmp, &info->rx_urbs_free);
+			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
+				wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+				urb = wrap->urb;
+				list_del(tmp);
+				spin_unlock_irqrestore(&info->lock, flags);
+				usb_kill_urb(urb);
+				spin_lock_irqsave(&info->lock, flags);
+				list_add(tmp, &info->rx_urbs_free);
+			}
+			break;
+		}
+		spin_lock_irqsave(&info->lock, flags);
+		list_add(tmp, &info->rx_urbs_submitted);
+	}
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return retval;
+}
+
+
+static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
+						struct list_head *head)
+{
+	struct whiteheat_urb_wrap *wrap;
+	struct list_head *tmp;
+
+	list_for_each(tmp, head) {
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		if (wrap->urb == urb)
+			return wrap;
+	}
+
+	return NULL;
+}
+
+
+static struct list_head *list_first(struct list_head *head)
+{
+	return head->next;
+}
+
+
+static void rx_data_softint(struct work_struct *work)
+{
+	struct whiteheat_private *info =
+		container_of(work, struct whiteheat_private, rx_work);
+	struct usb_serial_port *port = info->port;
+	struct tty_struct *tty = tty_port_tty_get(&port->port);
+	struct whiteheat_urb_wrap *wrap;
+	struct urb *urb;
+	unsigned long flags;
+	struct list_head *tmp;
+	struct list_head *tmp2;
+	int result;
+	int sent = 0;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (info->flags & THROTTLED) {
+		spin_unlock_irqrestore(&info->lock, flags);
+		goto out;
+	}
+
+	list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
+		list_del(tmp);
+		spin_unlock_irqrestore(&info->lock, flags);
+
+		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+		urb = wrap->urb;
+
+		if (tty && urb->actual_length)
+			sent += tty_insert_flip_string(tty,
+				urb->transfer_buffer, urb->actual_length);
+
+		result = usb_submit_urb(urb, GFP_ATOMIC);
+		if (result) {
+			dev_err(&port->dev,
+				"%s - failed resubmitting read urb, error %d\n",
+				__func__, result);
+			spin_lock_irqsave(&info->lock, flags);
+			list_add(tmp, &info->rx_urbs_free);
+			continue;
+		}
+
+		spin_lock_irqsave(&info->lock, flags);
+		list_add(tmp, &info->rx_urbs_submitted);
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (sent)
+		tty_flip_buffer_push(tty);
+out:
+	tty_kref_put(tty);
+}
+
+module_usb_serial_driver(whiteheat_driver, serial_drivers);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("whiteheat.fw");
+MODULE_FIRMWARE("whiteheat_loader.fw");
+
+module_param(urb_pool_size, int, 0);
+MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.h b/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.h
new file mode 100644
index 0000000..38065df
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/whiteheat.h
@@ -0,0 +1,302 @@
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *      Copyright (C) 2002
+ *          Connect Tech Inc.
+ *
+ *      Copyright (C) 1999, 2000
+ *          Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ * 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/ap/os/linux/linux-3.4.x/drivers/usb/serial/zio.c b/ap/os/linux/linux-3.4.x/drivers/usb/serial/zio.c
new file mode 100644
index 0000000..9d0bb37
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/serial/zio.c
@@ -0,0 +1,46 @@
+/*
+ * ZIO Motherboard USB driver
+ *
+ * Copyright (C) 2010 Zilogic Systems <code@zilogic.com>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.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(0x1CBE, 0x0103) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver zio_driver = {
+	.name =		"zio",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+};
+
+static struct usb_serial_driver zio_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"zio",
+	},
+	.id_table =		id_table,
+	.num_ports =		1,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&zio_device, NULL
+};
+
+module_usb_serial_driver(zio_driver, serial_drivers);
+MODULE_LICENSE("GPL");