The driver is based on the CDC-ACM driver. In addition to supporting 
the features of the MaxLinear/Exar USB UART devices, the driver also 
has support for 2 other functions per customer requirements:

- Specific entries are checked in the BIOS to detect if the board is a
  "Caracalla" board before enabling specific modes in the MaxLinear/Exar
  USB UARTs.  The smbios code is based on the example at:
  https://sourceforge.net/projects/smbios/

- When specific IOCTLs are called by a user-space application, a 
  port_config file is created for the /dev/ttyXRUSB device at a
  specific USB tree location, and some configuration data is stored. 
  The driver checks for the port_config file when the driver is loaded
  for each port and loads the configuration settings if there is a
  port_config file for the USB tree location.

Signed-off-by: Patong Yang <patong....@gmail.com>
---
 drivers/usb/serial/xrusb_serial.c | 3285 +++++++++++++++++++++++++++++++++++++
 drivers/usb/serial/xrusb_serial.h |  448 +++++
 2 files changed, 3733 insertions(+)
 create mode 100644 drivers/usb/serial/xrusb_serial.c
 create mode 100644 drivers/usb/serial/xrusb_serial.h

diff --git a/drivers/usb/serial/xrusb_serial.c 
b/drivers/usb/serial/xrusb_serial.c
new file mode 100644
index 000000000000..16a5bcff9103
--- /dev/null
+++ b/drivers/usb/serial/xrusb_serial.c
@@ -0,0 +1,3285 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * xrusb_serial.c
+ *
+ * Copyright (c) 2018 Patong Yang <patong....@gmail.com>
+ *
+ * USB Serial Driver based on the cdc-acm.c driver for the
+ * MaxLinear/Exar USB UARTs/Serial adapters
+ */
+
+
+#undef DEBUG
+#undef VERBOSE_DEBUG
+
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/log2.h>
+#include <linux/serial.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/dmi.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+
+#include "linux/version.h"
+#include "xrusb_serial.h"
+
+#define DRIVER_AUTHOR "Patong Yang <patong....@gmail.com>"
+#define DRIVER_DESC "MaxLinear/Exar USB UART (serial port) driver"
+
+static struct usb_driver xrusb_driver;
+static struct tty_driver *xrusb_tty_driver;
+static struct xrusb *xrusb_table[XRUSB_TTY_MINORS];
+static struct file *port_param_file[32];
+static struct reg_addr_map xr2280x_reg_map;
+static struct reg_addr_map xr21b1411_reg_map;
+static struct reg_addr_map xr21v141x_reg_map;
+static struct reg_addr_map xr21b142x_reg_map;
+
+static DEFINE_MUTEX(xrusb_table_lock);
+
+/*
+ * SMBIOS code snippets below borrowed from
+ * https://sourceforge.net/projects/smbios/
+ *
+ * Checking SMBIOS values to determine if this is Caracalla board
+ * where modes are programmed in the BIOS
+ *
+ */
+
+unsigned char smbios_check_entry_point (void *addr)
+{
+       unsigned char *i;
+       unsigned char checksum = 0;
+       unsigned char length;
+
+       length = ((struct smbios_entry_point_struct *) addr)->
+               entry_point_length;
+       /* calculate checksum for entry point structure (should be 0) */
+       for (i = (unsigned char *) addr;
+               i < (unsigned char *) addr + length; i++)
+               checksum += *i;
+       return checksum;
+}
+
+struct smbios_entry_point_struct *smbios_find_entry_point (void *base)
+{
+       struct smbios_entry_point_struct *entry_point = 0;
+       unsigned int *temp;
+
+       /* search for the magic dword on paragraph boundaries */
+       for (temp = base;
+               !entry_point && temp < (unsigned int *) base + BIOS_MAP_LENGTH;
+               temp += 4) {
+
+               if (*temp == SMBIOS_MAGIC_DWORD) {
+                       /* check if entry point valid (build checksum) */
+                       if (!(smbios_check_entry_point (temp))) {
+                               entry_point = (struct
+                                       smbios_entry_point_struct *) temp;
+
+                               // fix display of Bios version string
+                               // SMBios version is known as 2.1, 2.2,
+                               // 2.3 and 2.3.1, never as 2.01 (JB)
+                               SM_BIOS_DEBUG("SM-BIOS V%d.%d entry point ",
+                                       entry_point->major_version,
+                                       entry_point->minor_version);
+                               SM_BIOS_DEBUG("found at 0x%x\n",
+                                       (unsigned int) temp);
+                       }
+               }
+       }
+       return entry_point;
+}
+
+int smbios_check_caracalla_config(unsigned char *config0,
+       unsigned char *config1)
+{
+
+       int i;
+       int result = -1;
+       unsigned char *p;
+
+       smbios_base = ioremap(BIOS_START_ADDRESS, BIOS_MAP_LENGTH);
+
+       if (!smbios_base) {
+               SM_BIOS_DEBUG("ioremap() for entry point failed\n");
+               result = -ENXIO;
+               return result;
+       }
+
+       smbios_entry_point = smbios_find_entry_point (smbios_base);
+       if (!smbios_entry_point) {
+               SM_BIOS_DEBUG("SM-BIOS entry point not found\n");
+               iounmap(smbios_base);
+               result = -ENXIO;
+               return result;
+       }
+       /*
+        *      for SM-BIOS:
+        *      check if Pointer to DMI structures exist.
+        *      intermediate_string (_DMI_) is not '\0' terminated,
+        *      so strncmp() with sizeof(DMI_STRING) - 1 is needed.
+        */
+       if (smbios_entry_point) {
+               if (strncmp((char *) &(smbios_entry_point->intermediate_string),
+                       DMI_STRING, sizeof(DMI_STRING) - 1))
+                       SM_BIOS_DEBUG("Pointer to DMI struct not found!\n");
+       }
+
+       /*
+        *      map the SM-BIOS structures physical address range.
+        *      the 'real' smbios_structures_base contains the starting
+        *      address, where the instances of dmi structures are located.
+        */
+       if (smbios_entry_point) {
+               smbios_structures_base =
+                       ioremap(smbios_entry_point->struct_table_address,
+                       (unsigned long)smbios_entry_point->struct_table_length);
+               if (!(smbios_structures_base)) {
+                       SM_BIOS_DEBUG("ioremap() for struct table failed\n");
+                       iounmap(smbios_base);
+                       result = -ENXIO;
+               return result;
+               }
+       }
+       SM_BIOS_DEBUG("smbios_structures_base to 0x%p length %d ",
+                       smbios_structures_base,
+                       smbios_entry_point->struct_table_length);
+       SM_BIOS_DEBUG(" no_of_structures:%d\n",
+                       smbios_entry_point->no_of_structures);
+
+       p = (unsigned char *)smbios_structures_base;
+       for (i = 0; i < smbios_entry_point->struct_table_length; i++) {
+               if ((p[i] == 0xc0) && (p[i+1] == 0x06)) {
+                       SM_BIOS_DEBUG("Found 0xc0 at offset:%d 0x%02x 0x%02x ",
+                               i, p[i], p[i+1]);
+                       SM_BIOS_DEBUG("0x%02x 0x%02x 0x%02x 0x%02x\n\t",
+                               p[i+2], p[i+3], p[i+4], p[i+5]);
+               *config0 = p[i+4];
+               *config1 = p[i+5];
+               result = 0;
+               break;
+               }
+       }
+       iounmap(smbios_structures_base);
+       iounmap(smbios_base);
+       return result;
+}
+
+/*
+ * Look up an XRUSB structure by index. If found and not disconnected,
+ * increment its refcount and return it with its mutex held.
+ */
+
+static struct xrusb *xrusb_get_by_index(unsigned int index)
+{
+       struct xrusb *xrusb;
+
+       mutex_lock(&xrusb_table_lock);
+       xrusb = xrusb_table[index];
+       if (xrusb) {
+               mutex_lock(&xrusb->mutex);
+               if (xrusb->disconnected) {
+                       mutex_unlock(&xrusb->mutex);
+                       xrusb = NULL;
+               } else {
+                       tty_port_get(&xrusb->port);
+                       mutex_unlock(&xrusb->mutex);
+               }
+       }
+       mutex_unlock(&xrusb_table_lock);
+       return xrusb;
+}
+
+/*
+ * Try to find an available minor number and if found, associate it with
+ * 'xrusb'.
+ */
+static int xrusb_alloc_minor(struct xrusb *xrusb)
+{
+       int minor;
+
+       mutex_lock(&xrusb_table_lock);
+       for (minor = 0; minor < XRUSB_TTY_MINORS; minor++) {
+               if (!xrusb_table[minor]) {
+                       xrusb_table[minor] = xrusb;
+                       break;
+               }
+       }
+       mutex_unlock(&xrusb_table_lock);
+
+       return minor;
+}
+
+/* Release the minor number associated with 'xrusb'.  */
+static void xrusb_release_minor(struct xrusb *xrusb)
+{
+       mutex_lock(&xrusb_table_lock);
+       xrusb_table[xrusb->minor] = NULL;
+       mutex_unlock(&xrusb_table_lock);
+}
+
+/*
+ * Functions for XRUSB control messages.
+ */
+
+static int xrusb_ctrl_msg(struct xrusb *xrusb,
+               int request, int value, void *buf, int len)
+{
+       int rv = usb_control_msg(xrusb->dev,
+               usb_sndctrlpipe(xrusb->dev, 0),
+               request,
+               USB_RT_XRUSB,
+               value,
+               xrusb->control->altsetting[0].desc.bInterfaceNumber,
+               buf,
+               len,
+               5000);
+       dev_dbg(&xrusb->control->dev,
+               "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
+               __func__, request, value, len, rv);
+       return rv < 0 ? rv : 0;
+}
+
+int xrusb_set_reg(struct xrusb *xrusb, int regnum, int value)
+{
+       int result;
+       int channel = 0;
+       int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum;
+
+       if ((xrusb->DeviceProduct & 0xfff0) == 0x1400) {
+               result = usb_control_msg(xrusb->dev,            // usb device
+                       usb_sndctrlpipe(xrusb->dev, 0),         // endpoint pipe
+                       XRUSB_SET_XR2280X,                      // request
+                       USB_DIR_OUT | USB_TYPE_VENDOR,          // request_type
+                       value,                                  // request value
+                       XR2280xaddr,                            // index
+                       NULL,                                   // data
+                       0,                                      // size
+                       5000);                                  // timeout
+       } else if ((xrusb->DeviceProduct & 0xfff0) == 0x1420) {
+               channel = (xrusb->channel - 4)*2;
+               result = usb_control_msg(xrusb->dev,            // usb device
+                       usb_sndctrlpipe(xrusb->dev, 0),         // endpoint pipe
+                       XRUSB_SET_XR21B142X,                    // request
+                       USB_DIR_OUT | USB_TYPE_VENDOR | 1,      // request_type
+                       value,                                  // request value
+                       regnum | (channel << 8),                // index
+                       NULL,                                   // data
+                       0,                                      // size
+                       5000);                                  // timeout
+       } else if (xrusb->DeviceProduct == 0x1411) {
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_sndctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_SET_XR21B1411,                    // request
+                       USB_DIR_OUT | USB_TYPE_VENDOR,          // request_type
+                       value,                                  // request value
+                       regnum,                                 // index
+                       NULL,                                   // data
+                       0,                                      // size
+                       5000);                                  // timeout
+       } else if ((xrusb->DeviceProduct & 0xfff0) == 0x1410) {
+               if (xrusb->channel)
+                       channel = xrusb->channel - 1;
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_sndctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_SET_XR21V141X,                    // request
+                       USB_DIR_OUT | USB_TYPE_VENDOR,          // request_type
+                       value,                                  // request value
+                       regnum | (channel << 8),                // index
+                       NULL,                                   // data
+                       0,                                      // size
+                       5000);                                  // timeout
+       } else
+               result = -1;
+
+       if (result < 0)
+               dev_err(&xrusb->control->dev,
+                       "%s Error:%d\n", __func__, result);
+
+       return result;
+}
+
+/* Write to UART Block registers in XR21V141x */
+int xrusb_set_block_reg(struct xrusb *xrusb, int block, int regnum, int value)
+{
+       int result;
+
+       result = usb_control_msg(xrusb->dev,            // usb device
+               usb_sndctrlpipe(xrusb->dev, 0),         // endpoint pipe
+               XRUSB_SET_XR21V141X,                    // request
+               USB_DIR_OUT | USB_TYPE_VENDOR,          // request_type
+               value,                                  // request value
+               regnum | (block << 8),                  // index
+               NULL,                                   // data
+               0,                                      // size
+               5000);                                  // timeout
+
+       if (result < 0)
+               dev_err(&xrusb->control->dev, "%s Error:%d\n",
+                       __func__, result);
+
+       return result;
+}
+
+int xrusb_get_reg(struct xrusb *xrusb, int regnum, short *value)
+{
+       int result;
+       int channel = 0;
+       int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum;
+       void *dmadata = kmalloc(2, GFP_KERNEL);
+
+       if (!dmadata)
+               return -ENOMEM;
+
+       if ((xrusb->DeviceProduct & 0xfff0) == 0x1400) {
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_rcvctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_GET_XR2280X,                      // request
+                       USB_DIR_IN | USB_TYPE_VENDOR,           // request_type
+                       0,                                      // request value
+                       XR2280xaddr,                            // index
+                       dmadata,                                // data
+                       2,                                      // size
+                       5000);                                  // timeout
+               memcpy(value, dmadata, 2);
+       } else if ((xrusb->DeviceProduct & 0xfff0) == 0x1420) {
+               channel = (xrusb->channel - 4)*2;
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_rcvctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_GET_XR21B142X,                    // request
+                       USB_DIR_IN | USB_TYPE_VENDOR | 1,       // request_type
+                       0,                                      // request value
+                       regnum | (channel << 8),                // index
+                       dmadata,                                // data
+                       2,                                      // size
+                       5000);                                  // timeout
+                       memcpy(value, dmadata, 2);
+       } else if (xrusb->DeviceProduct == 0x1411) {
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_rcvctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_GET_XR21B1411,                    // request
+                       USB_DIR_IN | USB_TYPE_VENDOR,           // request_type
+                       0,                                      // request value
+                       regnum,                                 // index
+                       dmadata,                                // data
+                       2,                                      // size
+                       5000);                                  // timeout
+                       memcpy(value, dmadata, 2);
+       } else if ((xrusb->DeviceProduct & 0xfff0) == 0x1410) {
+               if (xrusb->channel)
+                       channel = xrusb->channel - 1;
+               result = usb_control_msg(xrusb->dev,    // usb device
+                       usb_rcvctrlpipe(xrusb->dev, 0), // endpoint pipe
+                       XRUSB_GET_XR21V141X,                    // request
+                       USB_DIR_IN | USB_TYPE_VENDOR,           // request_type
+                       0,                                      // request value
+                       regnum | (channel << 8),                // index
+                       dmadata,                                // data
+                       1,                                      // size
+                       5000);                                  // timeout
+                       memcpy(value, dmadata, 1);
+       } else
+               result = -1;
+
+       if (result < 0) {
+               dev_err(&xrusb->control->dev,
+                       "%s channel:%d Reg 0x%x Error:%d\n",
+                       __func__, channel, regnum, result);
+       }
+       kfree(dmadata);
+       return result;
+}
+
+/* Read from UART Block registers in XR21V141x */
+int xrusb_get_block_reg(struct xrusb *xrusb, int block,
+       int regnum, short *value)
+{
+       int result;
+       void *dmadata = kmalloc(2, GFP_KERNEL);
+
+       if (!dmadata)
+               return -ENOMEM;
+
+       result = usb_control_msg(xrusb->dev,            // usb device
+               usb_rcvctrlpipe(xrusb->dev, 0),         // endpoint pipe
+               XRUSB_GET_XR21V141X,                    // request
+               USB_DIR_IN | USB_TYPE_VENDOR,           // request_type
+               0,                                      // request value
+               regnum | (block << 8),                  // index
+               dmadata,                                // data
+               1,                                      // size
+               5000);                                  // timeout
+       memcpy(value, dmadata, 1);
+
+       if (result < 0) {
+               dev_err(&xrusb->control->dev, "%s Error:%d\n",
+                       __func__, result);
+       }
+       kfree(dmadata);
+       return result;
+}
+
+int check_portparamfile(struct xrusb *xrusb)
+{
+       char filename[256];
+       int level_num, port_num, channel_num, config_num;
+
+       memset(filename, 0, sizeof(filename));
+
+       level_num = xrusb->dev->level;
+       port_num = xrusb->dev->portnum;
+       channel_num = xrusb->channel % 4;
+
+       // config_num is a unique number identifying USB level, USB port
+       // and UART/serial port channel. Driver assumes that devices are
+       // plugged into a single USB hub at each level.
+       config_num = (level_num * 100) + (port_num * 10) + channel_num;
+
+       sprintf(filename, "/etc/port_config%d", config_num);
+
+       port_param_file[config_num] =
+               filp_open(filename, O_RDWR | O_LARGEFILE, 0600);
+       if (IS_ERR(port_param_file[config_num])) {
+               xrusb->have_txcvr_config = 0;
+               return 0;
+       }
+       filp_close(port_param_file[config_num], NULL);
+       xrusb->have_txcvr_config = 1;
+       return 1;
+}
+
+int load_portparamfile(struct xrusb *xrusb)
+{
+       int length;
+       loff_t offset = 0;
+       mm_segment_t old_fs;
+       char filename[256];
+       int level_num, port_num, channel_num, config_num;
+
+       memset(filename, 0, sizeof(filename));
+
+       level_num = xrusb->dev->level;
+       port_num = xrusb->dev->portnum;
+       channel_num = xrusb->channel % 4;
+
+       // config_num is a unique number identifying USB level, USB port
+       // and UART/serial port channel. Driver assumes that devices are
+       // plugged into a single USB hub at each level.
+       config_num = (level_num * 100) + (port_num * 10) + channel_num;
+
+       sprintf(filename, "/etc/port_config%d", config_num);
+
+       port_param_file[config_num] =
+               filp_open(filename, O_RDWR | O_LARGEFILE, 0600);
+       if (IS_ERR(port_param_file[config_num])) {
+               dev_err(&xrusb->control->dev, "filp_open failed\n");
+               return PTR_ERR(port_param_file[config_num]);
+       }
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       length = vfs_read(port_param_file[config_num],
+               (char *) &(xrusb->port_setting),
+               sizeof(struct xrusb_setting),
+               &offset);
+       //dev_err(&xrusb->control->dev, "vfs_read :%d\n", length);
+       set_fs(old_fs);
+       filp_close(port_param_file[config_num], NULL);
+       return 0;
+}
+
+int update_portparamfile(struct xrusb *xrusb)
+{
+       int length;
+       loff_t offset = 0;
+       mm_segment_t old_fs;
+       char filename[256];
+       int level_num, port_num, channel_num, config_num;
+
+       memset(filename, 0, sizeof(filename));
+
+       level_num = xrusb->dev->level;
+       port_num = xrusb->dev->portnum;
+       channel_num = xrusb->channel % 4;
+
+       // config_num is a unique number identifying USB level, USB port
+       // and UART/serial port channel. Driver assumes that devices are
+       // plugged into a single USB hub at each level.
+       config_num = (level_num * 100) + (port_num * 10) + channel_num;
+
+       sprintf(filename, "/etc/port_config%d", config_num);
+       port_param_file[config_num] =
+               filp_open(filename, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
+       if (IS_ERR(port_param_file[config_num])) {
+               dev_err(&xrusb->control->dev, "filp_open failed\n");
+               return PTR_ERR(port_param_file[config_num]);
+       }
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       length = vfs_write(port_param_file[config_num],
+               (char *) &(xrusb->port_setting),
+               sizeof(struct xrusb_setting),
+               &offset);
+       if (length != sizeof(struct xrusb_setting)) {
+               dev_err(&xrusb->control->dev, "vfs_write failed :%d\n", length);
+               set_fs(old_fs);
+               filp_close(port_param_file[config_num], NULL);
+               return -1;
+       }
+       //dev_err(&xrusb->control->dev, "successful vfs_write :%d\n", length);
+       set_fs(old_fs);
+       filp_close(port_param_file[config_num], NULL);
+       xrusb->have_txcvr_config = 1;
+       return 0;
+}
+
+static int set_txcvr_mode(struct xrusb *xrusb, unsigned int mode)
+{
+       unsigned short tmp_value;
+       int rv;
+       unsigned int mode1_mask, mode0_mask;
+
+       mode1_mask = (1<<(xrusb->port_setting.txcvr_mode1_pin));
+       mode0_mask = (1<<(xrusb->port_setting.txcvr_mode0_pin));
+       tmp_value = 0;
+
+       dev_err(&xrusb->control->dev, "Ch %d: ", (xrusb->channel % 4));
+
+       switch (mode) {
+       case 0:
+               dev_err(&xrusb->control->dev, "Transceiver Mode = LOOPBACK\n");
+               tmp_value &= ~mode0_mask;
+               tmp_value &= ~mode1_mask;
+               break;
+       case 1:
+               dev_err(&xrusb->control->dev, "Transceiver Mode = RS232\n");
+               tmp_value |= mode0_mask;
+               tmp_value &= ~mode1_mask;
+               break;
+       case 2:
+               dev_err(&xrusb->control->dev,
+                       "Transceiver Mode = RS485 Half-Duplex\n");
+               tmp_value &= ~mode0_mask;
+               tmp_value |= mode1_mask;
+               break;
+       case 3:
+               dev_err(&xrusb->control->dev,
+                       "Transceiver Mode = RS485/RS422 Full-Duplex\n");
+               tmp_value |= mode0_mask;
+               tmp_value |= mode1_mask;
+               break;
+       default:
+               dev_err(&xrusb->control->dev,
+                       "Invalid Parameter. No mode change.\n");
+               break;
+       }
+
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+                       (int)tmp_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       tmp_value = ~tmp_value;
+       tmp_value &= (mode1_mask | mode0_mask);
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_clr,
+                       (int)tmp_value);
+       if (rv < 0)
+               return -EFAULT;
+       return rv;
+}
+
+static int set_rs485_pin(struct xrusb *xrusb, unsigned int option)
+{
+       short reg_value;
+       int rv;
+
+       rv = xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_mode, &reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       //dev_err(&xrusb->control->dev, "set_rs485_pin option = %d\n", option);
+
+       switch (option) {
+       case 0: // disable RS485 pin
+               reg_value &= (GPIO9_RXT | GPIO8_TXT);
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode,
+                               (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_flow,
+                       UART_FLOW_MODE_NONE);
+               if (rv < 0)
+                       return -EFAULT;
+               break;
+       case 5: // enable GPIO5/RTS as active-high RS-485 control output
+               reg_value = UART_GPIO_MODE_DIR_RTS_HI;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode,
+                               (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_flow,
+                       UART_FLOW_MODE_NONE);
+               if (rv < 0)
+                       return -EFAULT;
+               break;
+       case 7: // Enable GPIO7/XEN as active-high RS-485 control output
+               reg_value = UART_GPIO_MODE_DIR_XEN_HI;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode,
+                               (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_flow,
+                       UART_FLOW_MODE_NONE);
+               if (rv < 0)
+                       return -EFAULT;
+               break;
+       default: // Invalid option
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int set_txcvr_term_mode(struct xrusb *xrusb, unsigned int option)
+{
+       unsigned int term_mask;
+       int rv;
+
+       term_mask =  (1<<(xrusb->port_setting.txcvr_term_pin));
+
+       if (option) { //Set GPIO pin High
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+                               term_mask);
+               if (rv < 0)
+                       return -EFAULT;
+       } else { //Set GPIO pin Low
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_clr,
+                               term_mask);
+               if (rv < 0)
+                       return -EFAULT;
+       }
+       return 0;
+}
+static int set_txcvr_slew_mode(struct xrusb *xrusb, unsigned int option)
+{
+       int rv;
+       unsigned int slew_mask;
+
+       slew_mask =  (1<<(xrusb->port_setting.txcvr_slew_pin));
+       if (option) { //Slew pin is high
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+                       slew_mask);
+               if (rv < 0)
+                       return -EFAULT;
+       } else { // Slew pin is low
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_clr,
+                       slew_mask);
+               if (rv < 0)
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+int init_portparam(struct xrusb *xrusb)
+{
+       int rv;
+
+       if (xrusb->port_setting.txcvr_mode < 2)
+               rv = set_rs485_pin(xrusb, 0);
+       else
+               rv = set_rs485_pin(xrusb, xrusb->port_setting.txcvr_dir_pin);
+       if (rv < 0)
+               return -EFAULT;
+
+       rv = set_txcvr_mode(xrusb, xrusb->port_setting.txcvr_mode);
+       if (rv < 0)
+               return -EFAULT;
+
+       rv = set_txcvr_term_mode(xrusb,
+                       xrusb->port_setting.term_mode);
+       if (rv < 0)
+               return -EFAULT;
+
+       rv = set_txcvr_slew_mode(xrusb, xrusb->port_setting.slew_mode);
+       if (rv < 0)
+               return -EFAULT;
+
+       return rv;
+}
+
+int xrusb_gpio_dir_out(struct xrusb *xrusb, int gpio_mask)
+{
+       int rv = 0;
+       short reg_value;
+
+       rv = xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_dir, &reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       reg_value |= gpio_mask;
+
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_dir, reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       return rv;
+}
+
+int xrusb_gpio_dir_in(struct xrusb *xrusb, int gpio_mask)
+{
+       int rv = 0;
+       short reg_value;
+
+       rv = xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_dir, &reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       gpio_mask = ~gpio_mask;
+       reg_value &= gpio_mask;
+
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_dir, reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       return rv;
+}
+
+struct xr21v141x_baud_rate {
+       unsigned int tx;
+       unsigned int rx0;
+       unsigned int rx1; };
+
+static struct xr21v141x_baud_rate xr21v141x_baud_rates[] = {
+       { 0x000, 0x000, 0x000 },
+       { 0x000, 0x000, 0x000 },
+       { 0x100, 0x000, 0x100 },
+       { 0x020, 0x400, 0x020 },
+       { 0x010, 0x100, 0x010 },
+       { 0x208, 0x040, 0x208 },
+       { 0x104, 0x820, 0x108 },
+       { 0x844, 0x210, 0x884 },
+       { 0x444, 0x110, 0x444 },
+       { 0x122, 0x888, 0x224 },
+       { 0x912, 0x448, 0x924 },
+       { 0x492, 0x248, 0x492 },
+       { 0x252, 0x928, 0x292 },
+       { 0X94A, 0X4A4, 0XA52 },
+       { 0X52A, 0XAA4, 0X54A },
+       { 0XAAA, 0x954, 0X4AA },
+       { 0XAAA, 0x554, 0XAAA },
+       { 0x555, 0XAD4, 0X5AA },
+       { 0XB55, 0XAB4, 0X55A },
+       { 0X6B5, 0X5AC, 0XB56 },
+       { 0X5B5, 0XD6C, 0X6D6 },
+       { 0XB6D, 0XB6A, 0XDB6 },
+       { 0X76D, 0X6DA, 0XBB6 },
+       { 0XEDD, 0XDDA, 0X76E },
+       { 0XDDD, 0XBBA, 0XEEE },
+       { 0X7BB, 0XF7A, 0XDDE },
+       { 0XF7B, 0XEF6, 0X7DE },
+       { 0XDF7, 0XBF6, 0XF7E },
+       { 0X7F7, 0XFEE, 0XEFE },
+       { 0XFDF, 0XFBE, 0X7FE },
+       { 0XF7F, 0XEFE, 0XFFE },
+       { 0XFFF, 0XFFE, 0XFFD },
+};
+
+
+static int xr21v141x_set_baud_rate(struct xrusb *xrusb,        unsigned int 
rate)
+{
+       unsigned int divisor = 48000000 / rate;
+       unsigned int i = ((32 * 48000000) / rate) & 0x1f;
+       unsigned int tx_mask = xr21v141x_baud_rates[i].tx;
+       unsigned int rx_mask = (divisor & 1) ?
+               xr21v141x_baud_rates[i].rx1 :
+               xr21v141x_baud_rates[i].rx0;
+
+       xrusb_set_reg(xrusb, XR21V141X_CLOCK_DIVISOR_0, (divisor >>  0) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_CLOCK_DIVISOR_1, (divisor >>  8) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_CLOCK_DIVISOR_2, (divisor >> 16) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_TX_CLOCK_MASK_0, (tx_mask >>  0) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_TX_CLOCK_MASK_1, (tx_mask >>  8) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_RX_CLOCK_MASK_0, (rx_mask >>  0) & 0xff);
+       xrusb_set_reg(xrusb, XR21V141X_RX_CLOCK_MASK_1, (rx_mask >>  8) & 0xff);
+
+       return 0;
+}
+
+/* devices aren't required to support these requests.
+ * the cdc xrusb descriptor tells whether they do...
+ */
+int xrusb_set_control(struct xrusb *xrusb, unsigned int control)
+{
+       int rv = 0;
+
+       // Use custom vendor request for XR21V1410/12/14
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               if (control & XRUSB_CTRL_DTR)
+                       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_clr,
+                               GPIO3_DTR);
+               else
+                       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+                               GPIO3_DTR);
+
+               if (control & XRUSB_CTRL_RTS)
+                       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_clr,
+                               GPIO5_RTS);
+               else
+                       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+                               GPIO5_RTS);
+       }
+       // Use CDC command for XR21B14xx and XR2280x
+       else
+               rv = xrusb_ctrl_msg(xrusb,
+                       USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+                       control,
+                       NULL,
+                       0);
+
+       return rv;
+}
+
+int xrusb_set_line(struct xrusb *xrusb,        struct usb_cdc_line_coding 
*line)
+{
+       int rv = 0;
+       unsigned int data_size, data_parity, data_stop, format;
+
+       // Use custom vendor request for XR21V1410/12/14
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               xr21v141x_set_baud_rate(xrusb, line->dwDTERate);
+               data_size = line->bDataBits;
+               data_parity = line->bParityType;
+               data_stop = line->bCharFormat;
+               format = data_size | (data_parity << 4) | (data_stop << 7);
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_format, format);
+       }
+       // Use CDC command for XR21B14xx and XR2280x
+       else
+               rv = xrusb_ctrl_msg(xrusb,
+                       USB_CDC_REQ_SET_LINE_CODING,
+                       0,
+                       line,
+                       sizeof *(line));
+
+       return rv;
+}
+
+int xrusb_set_flow_mode(struct xrusb *xrusb,
+               struct tty_struct *tty, unsigned int cflag)
+{
+       unsigned int flow;
+       unsigned int gpio_mode;
+       short gpio_mode_reg;
+
+       xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_mode, &gpio_mode_reg);
+       gpio_mode = gpio_mode_reg;
+       if (cflag & CRTSCTS) {
+               flow = UART_FLOW_MODE_HW;
+               gpio_mode |= UART_GPIO_MODE_SEL_RTS_CTS;
+       } else if (I_IXOFF(tty) || I_IXON(tty)) {
+               unsigned char start_char = START_CHAR(tty);
+               unsigned char stop_char = STOP_CHAR(tty);
+
+               flow = UART_FLOW_MODE_SW;
+               gpio_mode |= UART_GPIO_MODE_SEL_GPIO;
+
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_xon_char, start_char);
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_xoff_char, stop_char);
+       } else {
+               flow = UART_FLOW_MODE_NONE;
+               gpio_mode |= UART_GPIO_MODE_SEL_GPIO;
+       }
+
+       // Mode configured in BIOS for Caracalla
+       // Do nothing for set_flow_mode
+       if (xrusb->found_smbios_caracalla_config)
+               return 0;
+
+       // If mode configured as RS485 or RS422 by application
+       // and stored in /etc/port_config file
+       if (xrusb->have_txcvr_config) {
+               xrusb_gpio_dir_out(xrusb,
+                       GPIO9_RXT | GPIO8_TXT | GPIO7_XEN | GPIO6_CLK);
+               gpio_mode &= 0x00FF; // disable TXT and RXT
+       }
+       if (xrusb->port_setting.txcvr_mode < 2) {
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_flow, flow);
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode, gpio_mode);
+       }
+       return 0;
+}
+
+int xrusb_send_break(struct xrusb *xrusb, int state)
+{
+       int rv = 0;
+
+       // Use custom vendor request for XR21V1410/12/14
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               if (state)
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_tx_break,
+                               0xffff);
+               else
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_tx_break,
+                               0);
+       }
+       // Use CDC command for XR21B14xx and XR2280x
+       else
+               rv = xrusb_ctrl_msg(xrusb,
+                       USB_CDC_REQ_SEND_BREAK,
+                       state,
+                       NULL,
+                       0);
+
+       return rv;
+}
+
+int xrusb_enable(struct xrusb *xrusb)
+{
+       int rv = 0;
+       int channel = xrusb->channel;
+
+       if (channel)
+               channel--;
+
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               rv = xrusb_set_block_reg(xrusb,
+                       XR21V141x_URM_REG_BLOCK,
+                       XR21V141x_URM_FIFO_ENABLE_REG + channel,
+                       XR21V141x_URM_ENABLE_TX_FIFO);
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_enable,
+                       UART_ENABLE_TX | UART_ENABLE_RX);
+               rv = xrusb_set_block_reg(xrusb,
+                       XR21V141x_URM_REG_BLOCK,
+                       XR21V141x_URM_FIFO_ENABLE_REG + channel,
+                       XR21V141x_URM_ENABLE_TX_FIFO |
+                       XR21V141x_URM_ENABLE_RX_FIFO);
+       } else
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_enable,
+                       UART_ENABLE_TX | UART_ENABLE_RX);
+
+       return rv;
+}
+
+int xrusb_disable(struct xrusb *xrusb)
+{
+       int rv = 0;
+       int channel = xrusb->channel;
+
+       if (channel)
+               channel--;
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_enable, 0);
+
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               rv = xrusb_set_block_reg(xrusb,
+                       XR21V141x_URM_REG_BLOCK,
+                       XR21V141x_URM_FIFO_ENABLE_REG + channel, 0);
+       }
+
+       return rv;
+}
+
+int xrusb_fifo_reset(struct xrusb *xrusb)
+{
+       int rv = 0;
+       int channel = xrusb->channel;
+
+       if (channel)
+               channel--;
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               rv = xrusb_set_block_reg(xrusb,
+                       XR21V141x_URM_REG_BLOCK,
+                       XR21V141x_URM_RX_FIFO_RESET + channel,
+                       0xff);
+               rv |= xrusb_set_block_reg(xrusb,
+                       XR21V141x_URM_REG_BLOCK,
+                       XR21V141x_URM_TX_FIFO_RESET + channel,
+                       0xff);
+       } else
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_enable,
+                       UART_ENABLE_TX | UART_ENABLE_RX);
+
+       return rv;
+}
+
+int xrusb_set_loopback(struct xrusb *xrusb, int channel)
+{
+       int rv = 0;
+
+       xrusb_disable(xrusb);
+
+       if ((xrusb->DeviceProduct & 0x1411) == 0x1410) {
+               switch (channel) {
+               case 0:
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_loopback,
+                               XR21V141x_LOOP_ENABLE_A);
+                       break;
+               case 1:
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_loopback,
+                               XR21V141x_LOOP_ENABLE_B);
+                       break;
+               case 2:
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_loopback,
+                               XR21V141x_LOOP_ENABLE_C);
+                       break;
+               case 3:
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_loopback,
+                               XR21V141x_LOOP_ENABLE_D);
+                       break;
+               default:
+                       break;
+               }
+       } else
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_loopback,
+                       LOOP_ENABLE);
+
+       xrusb_enable(xrusb);
+       return rv;
+}
+
+int xrusb_set_wide_mode(struct xrusb *xrusb, int wide_mode)
+{
+       int rv = 0;
+       int channel = xrusb->channel;
+
+       xrusb_disable(xrusb);
+
+       if ((xrusb->DeviceProduct & 0xFFF0) == 0x1400) {
+               xrusb_set_reg(xrusb, XR2280X_TX_WIDE_MODE_REG, wide_mode);
+               xrusb_set_reg(xrusb, XR2280X_RX_WIDE_MODE_REG, wide_mode);
+       } else if ((xrusb->DeviceProduct & 0xFFF0) == 0x1420) {
+               xrusb_set_reg(xrusb, XR21B142X_TX_WIDE_MODE_REG, wide_mode);
+               xrusb_set_reg(xrusb, XR21B142X_RX_WIDE_MODE_REG, wide_mode);
+       } else if (xrusb->DeviceProduct == 0x1411) {
+               xrusb_set_reg(xrusb, XR21B1411_WIDE_MODE_REG, wide_mode);
+       } else if ((xrusb->DeviceProduct & 0xFFF0) == 0x1410) {
+               if (channel)
+                       channel--;
+               xrusb_set_block_reg(xrusb,
+                       XR21V141x_UART_CUSTOM_BLOCK,
+                       channel*8 + XR21V141X_WIDE_MODE_REG,
+                       wide_mode);
+       }
+
+       xrusb_enable(xrusb);
+       return rv;
+}
+
+int xrusb_disable_txt_rxt(struct xrusb *xrusb)
+{
+       int rv = 0;
+       short reg_value;
+
+       rv = xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_mode, &reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       reg_value &= 0xFF;
+
+       rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode, reg_value);
+       if (rv < 0)
+               return -EFAULT;
+
+       return rv;
+}
+
+static int xrusb_tiocmget(struct xrusb *xrusb)
+
+{
+       short data;
+       int result;
+
+       result = xrusb_get_reg(xrusb, xrusb->reg_map.uart_gpio_status, &data);
+
+       if (result)
+               return ((data & 0x8) ? 0 : TIOCM_DTR) |
+                       ((data & 0x20) ? 0 : TIOCM_RTS) |
+                       ((data & 0x4) ? 0 : TIOCM_DSR) |
+                       ((data & 0x1) ? 0 : TIOCM_RI) |
+                       ((data & 0x2) ? 0 : TIOCM_CD) |
+                       ((data & 0x10) ? 0 : TIOCM_CTS);
+       else
+               return -EFAULT;
+}
+
+static int xrusb_tiocmset(struct xrusb *xrusb,
+               unsigned int set, unsigned int clear)
+{
+
+       unsigned int newctrl = 0;
+
+       newctrl = xrusb->ctrlout;
+       set = (set & TIOCM_DTR ? XRUSB_CTRL_DTR : 0) |
+               (set & TIOCM_RTS ? XRUSB_CTRL_RTS : 0);
+       clear = (clear & TIOCM_DTR ? XRUSB_CTRL_DTR : 0) |
+               (clear & TIOCM_RTS ? XRUSB_CTRL_RTS : 0);
+       newctrl = (newctrl & ~clear) | set;
+
+       if (xrusb->ctrlout == newctrl)
+               return 0;
+
+       xrusb->ctrlout = newctrl;
+
+       if (newctrl & XRUSB_CTRL_DTR)
+               xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_clr,
+                       GPIO3_DTR);
+       else
+               xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_set,
+                       GPIO3_DTR);
+
+       if (newctrl & XRUSB_CTRL_RTS)
+               xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_clr,
+                       GPIO5_RTS);
+       else
+               xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_set,
+                       GPIO5_RTS);
+
+       return 0;
+}
+
+static void init_xr2280x_reg_map(void)
+{
+       xr2280x_reg_map.uart_enable = 0x40;
+       xr2280x_reg_map.uart_flow = 0x46;
+       xr2280x_reg_map.uart_xon_char = 0x47;
+       xr2280x_reg_map.uart_xoff_char = 0x48;
+       xr2280x_reg_map.uart_tx_break = 0x4a;
+       xr2280x_reg_map.uart_rs485_delay = 0x4b;
+       xr2280x_reg_map.uart_gpio_mode = 0x4c;
+       xr2280x_reg_map.uart_gpio_dir = 0x4d;
+       xr2280x_reg_map.uart_gpio_set = 0x4e;
+       xr2280x_reg_map.uart_gpio_clr = 0x4f;
+       xr2280x_reg_map.uart_gpio_status = 0x50;
+       xr2280x_reg_map.uart_gpio_int_mask = 0x51;
+       xr2280x_reg_map.uart_customized_int = 0x52;
+       xr2280x_reg_map.uart_gpio_pull_up_enable = 0x54;
+       xr2280x_reg_map.uart_gpio_pull_down_enable = 0x55;
+       xr2280x_reg_map.uart_loopback = 0x56;
+       xr2280x_reg_map.uart_low_latency = 0x66;
+       xr2280x_reg_map.uart_custom_driver = 0x81;
+}
+
+static void init_xr21b1411_reg_map(void)
+{
+       xr21b1411_reg_map.uart_enable = 0xc00;
+       xr21b1411_reg_map.uart_flow = 0xc06;
+       xr21b1411_reg_map.uart_xon_char = 0xc07;
+       xr21b1411_reg_map.uart_xoff_char = 0xc08;
+       xr21b1411_reg_map.uart_tx_break = 0xc0a;
+       xr21b1411_reg_map.uart_gpio_mode = 0xc0c;
+       xr21b1411_reg_map.uart_gpio_dir = 0xc0d;
+       xr21b1411_reg_map.uart_gpio_set = 0xc0e;
+       xr21b1411_reg_map.uart_gpio_clr = 0xc0f;
+       xr21b1411_reg_map.uart_gpio_status = 0xc10;
+       xr21b1411_reg_map.uart_gpio_int_mask = 0xc11;
+       xr21b1411_reg_map.uart_customized_int = 0xc12;
+       xr21b1411_reg_map.uart_gpio_pull_up_enable = 0xc14;
+       xr21b1411_reg_map.uart_gpio_pull_down_enable = 0xc15;
+       xr21b1411_reg_map.uart_loopback = 0xc16;
+       xr21b1411_reg_map.uart_low_latency = 0xcc2;
+       xr21b1411_reg_map.uart_custom_driver = 0x20d;
+}
+
+static void init_xr21v141x_reg_map(void)
+{
+       xr21v141x_reg_map.uart_enable = 0x03;
+       xr21v141x_reg_map.uart_format = 0x0b;
+       xr21v141x_reg_map.uart_flow = 0x0c;
+       xr21v141x_reg_map.uart_xon_char = 0x10;
+       xr21v141x_reg_map.uart_xoff_char = 0x11;
+       xr21v141x_reg_map.uart_loopback = 0x12;
+       xr21v141x_reg_map.uart_tx_break = 0x14;
+       xr21v141x_reg_map.uart_rs485_delay = 0x15;
+       xr21v141x_reg_map.uart_gpio_mode = 0x1a;
+       xr21v141x_reg_map.uart_gpio_dir = 0x1b;
+       xr21v141x_reg_map.uart_gpio_int_mask = 0x1c;
+       xr21v141x_reg_map.uart_gpio_set = 0x1d;
+       xr21v141x_reg_map.uart_gpio_clr = 0x1e;
+       xr21v141x_reg_map.uart_gpio_status = 0x1f;
+}
+
+static void init_xr21b142x_reg_map(void)
+{
+       xr21b142x_reg_map.uart_enable = 0x00;
+       xr21b142x_reg_map.uart_flow = 0x06;
+       xr21b142x_reg_map.uart_xon_char = 0x07;
+       xr21b142x_reg_map.uart_xoff_char = 0x08;
+       xr21b142x_reg_map.uart_tx_break = 0x0a;
+       xr21b142x_reg_map.uart_rs485_delay = 0x0b;
+       xr21b142x_reg_map.uart_gpio_mode = 0x0c;
+       xr21b142x_reg_map.uart_gpio_dir = 0x0d;
+       xr21b142x_reg_map.uart_gpio_set = 0x0e;
+       xr21b142x_reg_map.uart_gpio_clr = 0x0f;
+       xr21b142x_reg_map.uart_gpio_status = 0x10;
+       xr21b142x_reg_map.uart_gpio_int_mask = 0x11;
+       xr21b142x_reg_map.uart_customized_int = 0x12;
+       xr21b142x_reg_map.uart_gpio_open_drain = 0x13;
+       xr21b142x_reg_map.uart_gpio_pull_up_enable = 0x14;
+       xr21b142x_reg_map.uart_gpio_pull_down_enable = 0x15;
+       xr21b142x_reg_map.uart_loopback = 0x16;
+       xr21b142x_reg_map.uart_custom_driver = 0x60;
+       xr21b142x_reg_map.uart_low_latency = 0x46;
+}
+
+int xrusb_reg_init(struct xrusb *xrusb)
+{
+       int rv = 0, gpio_mode = 0;
+       unsigned char ch1_caracalla_config = 0xff;
+       unsigned char ch2_caracalla_config = 0xff;
+
+       init_xr2280x_reg_map();
+       init_xr21b142x_reg_map();
+       init_xr21b1411_reg_map();
+       init_xr21v141x_reg_map();
+
+       if ((xrusb->DeviceProduct & 0xfff0) == 0x1400)
+               memcpy(&(xrusb->reg_map), &xr2280x_reg_map,
+                       sizeof(struct reg_addr_map));
+       else if ((xrusb->DeviceProduct & 0xFFF0) == 0x1420)
+               memcpy(&(xrusb->reg_map), &xr21b142x_reg_map,
+                       sizeof(struct reg_addr_map));
+       else if (xrusb->DeviceProduct == 0x1411)
+               memcpy(&(xrusb->reg_map), &xr21b1411_reg_map,
+                       sizeof(struct reg_addr_map));
+       else if ((xrusb->DeviceProduct & 0xfff0) == 0x1410)
+               memcpy(&(xrusb->reg_map), &xr21v141x_reg_map,
+                       sizeof(struct reg_addr_map));
+       else
+               rv = -1;
+
+       if (xrusb->reg_map.uart_custom_driver)
+               xrusb_set_reg(xrusb, xrusb->reg_map.uart_custom_driver, 1);
+
+       // Enable TXT and RXT function for XR21B1420/22/24
+       if ((xrusb->DeviceProduct & 0xfff0) == 0x1420)
+               gpio_mode |= (GPIO9_RXT | GPIO8_TXT);
+
+       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode, gpio_mode);
+
+       // Enable RTS and DTR as outputs and high (de-asserted)
+       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_dir,
+               GPIO5_RTS | GPIO3_DTR);
+       xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_set,
+               GPIO5_RTS | GPIO3_DTR);
+
+       // Check for Caracalla board using XR21V1412 (2-ch)
+       xrusb->found_smbios_caracalla_config = 0;
+       if (smbios_check_caracalla_config(&ch1_caracalla_config,
+               &ch2_caracalla_config) == 0) {
+               if (xrusb->channel == 1) {
+                       xrusb->found_smbios_caracalla_config = 1;
+                       xrusb->channel_config = ch1_caracalla_config;
+                       switch (ch1_caracalla_config) {
+                       case 1: // RS-232 Mode
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode, 0);
+                               break;
+                       case 2: // RS-485 Half-Duplex, active high RTS
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode,
+                                       UART_GPIO_MODE_DIR_RTS_HI);
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_flow, 0);
+                               break;
+                       case 3: // RS-485/422 Full-Duplex, active high RTS
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode,
+                                       UART_GPIO_MODE_DIR_RTS_HI);
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_flow, 0);
+                               break;
+                       default: // No Caracalla BIOS setting found
+                               xrusb->found_smbios_caracalla_config = 0;
+                               break;
+                       }
+               } else if (xrusb->channel == 2) {
+                       xrusb->found_smbios_caracalla_config = 1;
+                       xrusb->channel_config = ch2_caracalla_config;
+                       switch (ch2_caracalla_config) {
+                       case 1: // RS-232 Mode
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode, 0);
+                               break;
+                       case 2: // RS-485 Half-Duplex, active high RTS
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode,
+                                       UART_GPIO_MODE_DIR_RTS_HI);
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_flow, 0);
+                               break;
+                       case 3: // RS-485/422 Full-Duplex, active high RTS
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_gpio_mode,
+                                       UART_GPIO_MODE_DIR_RTS_HI);
+                               xrusb_set_reg(xrusb,
+                                       xrusb->reg_map.uart_flow, 0);
+                               break;
+                       default: // No Caracalla BIOS setting found
+                               xrusb->found_smbios_caracalla_config = 0;
+                               break;
+                       }
+               }
+       }
+       if (xrusb->found_smbios_caracalla_config)
+               return rv;
+
+       // Check for previously saved port_config files in /etc/
+       // for designs using XR21B142x using GPIO9-6 for transceiver control
+       if (check_portparamfile(xrusb)) {
+               xrusb_gpio_dir_out(xrusb,
+                       GPIO9_RXT | GPIO8_TXT | GPIO7_XEN | GPIO6_CLK);
+               load_portparamfile(xrusb);
+               init_portparam(xrusb);
+       }
+       return rv;
+}
+
+/*
+ * Write buffer management.
+ * All of these assume proper locks taken by the caller.
+ */
+
+static int xrusb_wb_alloc(struct xrusb *xrusb)
+{
+       int i, wbn;
+       struct xrusb_wb *wb;
+
+       wbn = 0;
+       i = 0;
+       for (;;) {
+               wb = &xrusb->wb[wbn];
+               if (!wb->use) {
+                       wb->use = 1;
+                       return wbn;
+               }
+               wbn = (wbn + 1) % XRUSB_NW;
+               if (++i >= XRUSB_NW)
+                       return -1;
+       }
+}
+
+static int xrusb_wb_is_avail(struct xrusb *xrusb)
+{
+       int i, n;
+       unsigned long flags;
+
+       n = XRUSB_NW;
+       spin_lock_irqsave(&xrusb->write_lock, flags);
+       for (i = 0; i < XRUSB_NW; i++)
+               n -= xrusb->wb[i].use;
+       spin_unlock_irqrestore(&xrusb->write_lock, flags);
+       return n;
+}
+
+/*
+ * Finish write. Caller must hold xrusb->write_lock
+ */
+static void xrusb_write_done(struct xrusb *xrusb,
+               struct xrusb_wb *wb)
+{
+       wb->use = 0;
+       xrusb->transmitting--;
+       usb_autopm_put_interface_async(xrusb->control);
+}
+
+/*
+ * Poke write.
+ *
+ * the caller is responsible for locking
+ */
+
+static int xrusb_start_wb(struct xrusb *xrusb, struct xrusb_wb *wb)
+{
+       int rc;
+
+       xrusb->transmitting++;
+
+       wb->urb->transfer_buffer = wb->buf;
+       wb->urb->transfer_dma = wb->dmah;
+       wb->urb->transfer_buffer_length = wb->len;
+       wb->urb->dev = xrusb->dev;
+
+       rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
+       if (rc < 0) {
+               dev_err(&xrusb->data->dev,
+                       "%s - usb_submit_urb(write bulk) failed: %d\n",
+                       __func__, rc);
+               xrusb_write_done(xrusb, wb);
+       }
+       return rc;
+}
+
+/*
+ * attributes exported through sysfs
+ */
+
+static ssize_t bmCapabilities_show
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct xrusb *xrusb = usb_get_intfdata(intf);
+
+       return sprintf(buf, "%d", xrusb->ctrl_caps);
+}
+static DEVICE_ATTR_RO(bmCapabilities);
+
+/*
+ * Interrupt handlers for various XRUSB device responses
+ */
+
+/* control interface reports status changes with "interrupt" transfers */
+static void xrusb_ctrl_irq(struct urb *urb)
+{
+       struct xrusb *xrusb = urb->context;
+       struct usb_cdc_notification *dr = urb->transfer_buffer;
+
+       unsigned char *data;
+       int newctrl;
+       int rv;
+       int status = urb->status;
+       int i;
+       unsigned char *p;
+
+       switch (status) {
+       case 0:
+               p = (unsigned char *)(urb->transfer_buffer);
+               for (i = 0; i < urb->actual_length; i++)
+                       dev_dbg(&xrusb->control->dev, "0x%02x\n", p[i]);
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated, clean up */
+               dev_dbg(&xrusb->control->dev,
+                       "%s - urb shutting down with status: %d\n", __func__,
+                       status);
+               return;
+       default:
+               dev_dbg(&xrusb->control->dev,
+                       "%s - nonzero urb status received: %d\n",
+                       __func__, status);
+               goto exit;
+       }
+
+       usb_mark_last_busy(xrusb->dev);
+
+       data = (unsigned char *)(dr + 1);
+       switch (dr->bNotificationType) {
+       case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+               dev_dbg(&xrusb->control->dev, "%s - network connection: %d\n",
+                       __func__, dr->wValue);
+               break;
+
+       case USB_CDC_NOTIFY_SERIAL_STATE:
+
+               newctrl = get_unaligned_le16(data);
+               if (!xrusb->clocal &&
+                       (xrusb->ctrlin & ~newctrl & XRUSB_CTRL_DCD)) {
+
+                       dev_dbg(&xrusb->control->dev,
+                               "%s - calling hangup\n",
+                               __func__);
+                       tty_port_tty_hangup(&xrusb->port, false);
+               }
+
+               xrusb->ctrlin = newctrl;
+               break;
+
+       default:
+               dev_dbg(&xrusb->control->dev,
+                       "%s - unknown notification %d received: index %d ",
+                       __func__, dr->bNotificationType, dr->wIndex);
+               dev_dbg(&xrusb->control->dev, "len %d data0 %d data1 %d\n",
+                       dr->wLength, data[0], data[1]);
+               break;
+       }
+exit:
+       rv = usb_submit_urb(urb, GFP_ATOMIC);
+       if (rv)
+               dev_err(&xrusb->control->dev,
+                       "%s - usb_submit_urb failed: %d\n", __func__, rv);
+}
+
+static int xrusb_submit_read_urb(struct xrusb *xrusb,
+               int index, gfp_t mem_flags)
+{
+       int res;
+
+       if (!test_and_clear_bit(index, &xrusb->read_urbs_free))
+               return 0;
+
+       //dev_vdbg(&xrusb->data->dev, "%s - urb %d\n", __func__, index);
+
+       res = usb_submit_urb(xrusb->read_urbs[index], mem_flags);
+       if (res) {
+               if (res != -EPERM) {
+                       dev_err(&xrusb->data->dev,
+                                       "%s - usb_submit_urb failed: %d\n",
+                                       __func__, res);
+               }
+               set_bit(index, &xrusb->read_urbs_free);
+               return res;
+       }
+
+       return 0;
+}
+
+static int xrusb_submit_read_urbs(struct xrusb *xrusb,
+               gfp_t mem_flags)
+{
+       int res;
+       int i;
+
+       for (i = 0; i < xrusb->rx_buflimit; ++i) {
+               res = xrusb_submit_read_urb(xrusb, i, mem_flags);
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
+
+static void xrusb_process_read_urb(struct xrusb *xrusb,        struct urb *urb)
+{
+       int wide_mode = xrusb->wide_mode;
+       int have_extra_byte;
+       int length;
+
+       if (!urb->actual_length)
+               return;
+
+       if (wide_mode) {
+               char *dp = urb->transfer_buffer;
+               int i, ch, ch_flags;
+
+               length = urb->actual_length;
+               length = length + (xrusb->have_extra_byte ? 1 : 0);
+               have_extra_byte = (wide_mode && (length & 1));
+               length = (wide_mode) ? (length / 2) : length;
+               for (i = 0; i < length; ++i) {
+                       char tty_flag;
+
+                       if (i == 0) {
+                               if (xrusb->have_extra_byte)
+                                       ch = xrusb->extra_byte;
+                               else
+                                       ch = *dp++;
+                       } else
+                               ch = *dp++;
+
+                       ch_flags = *dp++;
+                       if (ch_flags & WIDE_MODE_PARITY)
+                               tty_flag = TTY_PARITY;
+                       else if (ch_flags & WIDE_MODE_BREAK)
+                               tty_flag = TTY_BREAK;
+                       else if (ch_flags & WIDE_MODE_FRAME)
+                               tty_flag = TTY_FRAME;
+                       else if (ch_flags & WIDE_MODE_OVERRUN)
+                               tty_flag = TTY_OVERRUN;
+                       else
+                               tty_flag = TTY_NORMAL;
+
+                       tty_insert_flip_char(&xrusb->port,
+                               ch, tty_flag);
+                       tty_flip_buffer_push(&xrusb->port);
+               }
+       } else {
+               tty_insert_flip_string(&xrusb->port,
+                       urb->transfer_buffer, urb->actual_length);
+               tty_flip_buffer_push(&xrusb->port);
+       }
+}
+
+static void xrusb_read_bulk_callback(struct urb *urb)
+{
+       struct xrusb_rb *rb = urb->context;
+       struct xrusb *xrusb = rb->instance;
+       unsigned long flags;
+
+       set_bit(rb->index, &xrusb->read_urbs_free);
+
+       if (!xrusb->dev) {
+               dev_dbg(&xrusb->data->dev,
+                       "%s - disconnected\n", __func__);
+               return;
+       }
+       usb_mark_last_busy(xrusb->dev);
+
+       if (urb->status) {
+               dev_dbg(&xrusb->data->dev,
+                       "%s - non-zero urb status: %d\n",
+                       __func__, urb->status);
+               return;
+       }
+       xrusb_process_read_urb(xrusb, urb);
+
+       /* throttle device if requested by tty */
+       spin_lock_irqsave(&xrusb->read_lock, flags);
+       xrusb->throttled = xrusb->throttle_req;
+       if (!xrusb->throttled && !xrusb->susp_count) {
+               spin_unlock_irqrestore(&xrusb->read_lock, flags);
+               xrusb_submit_read_urb(xrusb,
+                       rb->index, GFP_ATOMIC);
+       } else {
+               spin_unlock_irqrestore(&xrusb->read_lock, flags);
+       }
+}
+
+/* data interface wrote those outgoing bytes */
+static void xrusb_write_bulk(struct urb *urb)
+{
+       struct xrusb_wb *wb = urb->context;
+       struct xrusb *xrusb = wb->instance;
+       unsigned long flags;
+
+       spin_lock_irqsave(&xrusb->write_lock, flags);
+       xrusb_write_done(xrusb, wb);
+       spin_unlock_irqrestore(&xrusb->write_lock, flags);
+       schedule_work(&xrusb->work);
+}
+
+static void xrusb_softint(struct work_struct *work)
+{
+       struct xrusb *xrusb = container_of(work, struct xrusb, work);
+
+       tty_port_tty_wakeup(&xrusb->port);
+}
+
+/*
+ * TTY handlers
+ */
+
+static int xrusb_tty_install(struct tty_driver *driver,
+               struct tty_struct *tty)
+{
+       struct xrusb *xrusb;
+       int rv;
+
+       dev_dbg(tty->dev, "%s\n", __func__);
+
+       xrusb = xrusb_get_by_index(tty->index);
+       if (!xrusb)
+               return -ENODEV;
+
+       rv = tty_standard_install(driver, tty);
+       if (rv)
+               goto error_init_termios;
+
+       tty->driver_data = xrusb;
+
+       return 0;
+
+error_init_termios:
+       tty_port_put(&xrusb->port);
+       return rv;
+}
+
+static int xrusb_tty_open(struct tty_struct *tty, struct file *filp)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       int result;
+
+       result = xrusb_fifo_reset(xrusb);
+       dev_dbg(tty->dev, "%s\n", __func__);
+
+       return tty_port_open(&xrusb->port, tty, filp);
+}
+
+static int xrusb_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+       struct xrusb *xrusb = container_of(port, struct xrusb, port);
+       int rv = -ENODEV;
+
+       dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+
+       mutex_lock(&xrusb->mutex);
+       if (xrusb->disconnected)
+               goto disconnected;
+
+       rv = usb_autopm_get_interface(xrusb->control);
+       if (rv)
+               goto error_get_interface;
+
+       /*
+        * FIXME: Why do we need this? Allocating 64K of physically contiguous
+        * memory is really nasty...
+        */
+       set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
+       xrusb->control->needs_remote_wakeup = 1;
+
+       xrusb->ctrlurb->dev = xrusb->dev;
+       if (usb_submit_urb(xrusb->ctrlurb, GFP_KERNEL)) {
+               dev_err(&xrusb->control->dev,
+                       "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
+               goto error_submit_urb;
+       }
+
+       xrusb->ctrlout = XRUSB_CTRL_DTR |
+               XRUSB_CTRL_RTS;
+       if (xrusb_set_control(xrusb, xrusb->ctrlout) < 0
+               && (xrusb->ctrl_caps & USB_CDC_CAP_LINE))
+               goto error_set_control;
+
+       usb_autopm_put_interface(xrusb->control);
+
+       /*
+        * Unthrottle device in case the TTY was closed while throttled.
+        */
+       spin_lock_irq(&xrusb->read_lock);
+       xrusb->throttled = 0;
+       xrusb->throttle_req = 0;
+       spin_unlock_irq(&xrusb->read_lock);
+
+       if (xrusb_submit_read_urbs(xrusb, GFP_KERNEL))
+               goto error_submit_read_urbs;
+
+       mutex_unlock(&xrusb->mutex);
+
+       return 0;
+
+error_submit_read_urbs:
+       xrusb->ctrlout = 0;
+       xrusb_set_control(xrusb, xrusb->ctrlout);
+error_set_control:
+       usb_kill_urb(xrusb->ctrlurb);
+error_submit_urb:
+       usb_autopm_put_interface(xrusb->control);
+error_get_interface:
+disconnected:
+       mutex_unlock(&xrusb->mutex);
+       return rv;
+}
+
+static void xrusb_port_destruct(struct tty_port *port)
+{
+       struct xrusb *xrusb = container_of(port, struct xrusb, port);
+
+       //dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+
+       xrusb_release_minor(xrusb);
+       usb_put_intf(xrusb->control);
+       kfree(xrusb);
+}
+
+static void xrusb_port_shutdown(struct tty_port *port)
+{
+       struct xrusb *xrusb = container_of(port, struct xrusb, port);
+       int i;
+
+       //dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+
+       mutex_lock(&xrusb->mutex);
+       if (!xrusb->disconnected) {
+               usb_autopm_get_interface(xrusb->control);
+               xrusb_set_control(xrusb,
+                       xrusb->ctrlout = 0);
+               usb_kill_urb(xrusb->ctrlurb);
+               for (i = 0; i < XRUSB_NW; i++)
+                       usb_kill_urb(xrusb->wb[i].urb);
+               for (i = 0; i < xrusb->rx_buflimit; i++)
+                       usb_kill_urb(xrusb->read_urbs[i]);
+               xrusb->control->needs_remote_wakeup = 0;
+               usb_autopm_put_interface(xrusb->control);
+       }
+       mutex_unlock(&xrusb->mutex);
+}
+
+static void xrusb_tty_cleanup(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       //dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+       tty_port_put(&xrusb->port);
+}
+
+static void xrusb_tty_hangup(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       //dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+       tty_port_hangup(&xrusb->port);
+}
+
+static void xrusb_tty_close(struct tty_struct *tty, struct file *filp)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       //dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+       tty_port_close(&xrusb->port, tty, filp);
+}
+
+static int xrusb_tty_write(struct tty_struct *tty,
+               const unsigned char *buf, int count)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       int stat;
+       unsigned long flags;
+       int wbn;
+       struct xrusb_wb *wb;
+
+       if (!count)
+               return 0;
+
+       //dev_vdbg(&xrusb->data->dev, "%s - count %d\n", __func__, count);
+
+       spin_lock_irqsave(&xrusb->write_lock, flags);
+       wbn = xrusb_wb_alloc(xrusb);
+       if (wbn < 0) {
+               spin_unlock_irqrestore(&xrusb->write_lock, flags);
+               return 0;
+       }
+       wb = &xrusb->wb[wbn];
+
+       if (!xrusb->dev) {
+               wb->use = 0;
+               spin_unlock_irqrestore(&xrusb->write_lock, flags);
+               return -ENODEV;
+       }
+
+       count = (count > xrusb->writesize) ? xrusb->writesize : count;
+       //dev_vdbg(&xrusb->data->dev, "%s - write %d\n", __func__, count);
+       memcpy(wb->buf, buf, count);
+       wb->len = count;
+
+       usb_autopm_get_interface_async(xrusb->control);
+       if (xrusb->susp_count) {
+               if (!xrusb->delayed_wb)
+                       xrusb->delayed_wb = wb;
+               else
+                       usb_autopm_put_interface_async(xrusb->control);
+               spin_unlock_irqrestore(&xrusb->write_lock, flags);
+               return count;   /* A white lie */
+       }
+       usb_mark_last_busy(xrusb->dev);
+
+       stat = xrusb_start_wb(xrusb, wb);
+       spin_unlock_irqrestore(&xrusb->write_lock, flags);
+
+       if (stat < 0)
+               return stat;
+       return count;
+}
+
+static int xrusb_tty_write_room(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       /*
+        * Do not let the line discipline to know that we have a reserve,
+        * or it might get too enthusiastic.
+        */
+       return xrusb_wb_is_avail(xrusb) ?
+               xrusb->writesize : 0;
+}
+
+static int xrusb_tty_chars_in_buffer(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       /*
+        * if the device was unplugged then any remaining characters fell out
+        * of the connector ;)
+        */
+       if (xrusb->disconnected)
+               return 0;
+       /*
+        * This is inaccurate (overcounts), but it works.
+        */
+       return (XRUSB_NW - xrusb_wb_is_avail(xrusb)) *
+               xrusb->writesize;
+}
+
+static void xrusb_tty_throttle(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+
+       spin_lock_irq(&xrusb->read_lock);
+       xrusb->throttle_req = 1;
+       spin_unlock_irq(&xrusb->read_lock);
+}
+
+static void xrusb_tty_unthrottle(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       unsigned int was_throttled;
+
+       spin_lock_irq(&xrusb->read_lock);
+       was_throttled = xrusb->throttled;
+       xrusb->throttled = 0;
+       xrusb->throttle_req = 0;
+       spin_unlock_irq(&xrusb->read_lock);
+
+       if (was_throttled)
+               xrusb_submit_read_urbs(xrusb, GFP_KERNEL);
+}
+
+static int xrusb_tty_break_ctl(struct tty_struct *tty, int state)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       int rv;
+
+       rv = xrusb_send_break(xrusb, state ? 0xffff : 0);
+       if (rv < 0)
+               dev_err(&xrusb->control->dev, "%s - send break failed\n",
+                       __func__);
+       return rv;
+}
+
+static int xrusb_tty_tiocmget(struct tty_struct *tty)
+{
+       struct xrusb *xrusb = tty->driver_data;
+
+       return xrusb_tiocmget(xrusb);
+}
+
+static int xrusb_tty_tiocmset(struct tty_struct *tty,
+                           unsigned int set, unsigned int clear)
+{
+       struct xrusb *xrusb = tty->driver_data;
+
+       return xrusb_tiocmset(xrusb, set, clear);
+}
+
+static int get_serial_info(struct xrusb *xrusb,
+               struct serial_struct __user *info)
+{
+       struct serial_struct tmp;
+
+       if (!info)
+               return -EINVAL;
+
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.flags = ASYNC_LOW_LATENCY;
+       tmp.xmit_fifo_size = xrusb->writesize;
+       tmp.baud_base = le32_to_cpu(xrusb->line.dwDTERate);
+       tmp.close_delay = xrusb->port.close_delay / 10;
+       tmp.closing_wait = xrusb->port.closing_wait ==
+               ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE :
+               xrusb->port.closing_wait / 10;
+
+       if (copy_to_user(info, &tmp, sizeof(tmp)))
+               return -EFAULT;
+       else
+               return 0;
+}
+
+static int set_serial_info(struct xrusb *xrusb,
+               struct serial_struct __user *newinfo)
+{
+       struct serial_struct new_serial;
+       unsigned int closing_wait, close_delay;
+       int rv = 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(&xrusb->port.mutex);
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((close_delay != xrusb->port.close_delay) ||
+                   (closing_wait != xrusb->port.closing_wait))
+                       rv = -EPERM;
+               else
+                       rv = -EOPNOTSUPP;
+       } else {
+               xrusb->port.close_delay  = close_delay;
+               xrusb->port.closing_wait = closing_wait;
+       }
+
+       mutex_unlock(&xrusb->port.mutex);
+       return rv;
+}
+
+static int xrusb_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+               unsigned long arg)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       int rv = -ENOIOCTLCMD;
+       unsigned int  channel, param_rw;
+       int baud_rate = 0;
+       struct usb_cdc_line_coding newline;
+       short reg_value;
+
+       channel = xrusb->channel;
+       switch (cmd) {
+       case TIOCGSERIAL: /* gets serial port data */
+               rv = get_serial_info(xrusb,
+                       (struct serial_struct __user *) arg);
+               break;
+       case TIOCSSERIAL:
+               rv = set_serial_info(xrusb,
+                       (struct serial_struct __user *) arg);
+               break;
+       case XRUSB_SET_BAUD:
+               if (get_user(baud_rate, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+               xrusb->line.dwDTERate = baud_rate;
+               memcpy(&newline, &(xrusb->line),
+                       sizeof(struct usb_cdc_line_coding));
+               xrusb_disable(xrusb);
+               rv = xrusb_set_line(xrusb, &newline);
+               xrusb_enable(xrusb);
+               break;
+       case XRUSB_LOOPBACK:
+               rv = xrusb_set_loopback(xrusb, channel);
+               if (rv < 0)
+                       return -EFAULT;
+               break;
+       case XRUSB_SET_GPIO_MODE:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode, param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_MODE:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode, &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "put_user error\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_DIR:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_dir,
+                       (int)param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_DIR:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_dir,
+                       &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "put_user error\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_STATE:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_set,
+                       (int)param_rw);
+
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_CLEAR_GPIO_STATE:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_clr,
+                       (int)param_rw);
+
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_STATE:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_status,
+                       &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "put_user error\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_INT_MASK:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_int_mask,
+                       (int)param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_INT_MASK:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_int_mask,
+                       &reg_value);
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "put_user error\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_PULL_UP:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_pull_up_enable,
+                       (int)param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_PULL_UP:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_pull_up_enable,
+                       &reg_value);
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "Cannot put user result\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_PULL_DOWN:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_pull_down_enable,
+                       (int)param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_PULL_DOWN:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_pull_down_enable,
+                       &reg_value);
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "Cannot put user result\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_SET_GPIO_OPEN_DRAIN:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_open_drain,
+                       (int)param_rw);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_GET_GPIO_OPEN_DRAIN:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_open_drain,
+                       &reg_value);
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev,
+                               "Cannot put user result\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+
+       case XRUSB_WIDE_MODE:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+               if (param_rw)
+                       xrusb->wide_mode = 1; // enable wide mode
+               else
+                       xrusb->wide_mode = 0; // disable wide mode
+               rv = xrusb_set_wide_mode(xrusb,
+                       xrusb->wide_mode);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_LOW_LATENCY_MODE:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev,
+                               "get_user error\n");
+                       return -EFAULT;
+               }
+               if (param_rw) {
+                       // enable low latency mode
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_low_latency, 1);
+                       if (rv < 0)
+                               return -EFAULT;
+
+               } else {
+                       // disable low latency mode
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_low_latency, 0);
+                       if (rv < 0)
+                               return -EFAULT;
+               }
+               xrusb->port_setting.low_latency_mode = param_rw;
+               rv = 0;
+               break;
+       case XRUSB_GET_TXCVR_MODE:
+               if (xrusb->have_txcvr_config) {
+                       if (copy_to_user((void __user *)arg,
+                               (void *)&(xrusb->port_setting),
+                               sizeof(struct xrusb_setting))) {
+                               return -EFAULT;
+                       }
+                       rv = 0;
+               }
+               break;
+       case XRUSB_SET_TXCVR_MODE:
+               if (copy_from_user((void *)&(xrusb->port_setting),
+                       (void __user *)arg, sizeof(struct xrusb_setting)))
+                       return -EFAULT;
+
+               rv = xrusb_disable_txt_rxt(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+
+               // Enable GPIOs 9-6 as outputs
+               rv = xrusb_gpio_dir_out(xrusb, 0x3c0);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = set_txcvr_mode(xrusb,
+                       xrusb->port_setting.txcvr_mode);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = update_portparamfile(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_SET_TERM_MODE:
+               if (copy_from_user((void *)&(xrusb->port_setting),
+                       (void __user *)arg, sizeof(struct xrusb_setting)))
+                       return -EFAULT;
+
+               rv = xrusb_disable_txt_rxt(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+
+               // Enable GPIOs 9-6 as outputs
+               rv = xrusb_gpio_dir_out(xrusb, 0x3c0);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = set_txcvr_term_mode(xrusb,
+                       xrusb->port_setting.term_mode);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = update_portparamfile(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_SET_SLEW_MODE:
+               if (copy_from_user((void *)&(xrusb->port_setting),
+                       (void __user *)arg, sizeof(struct xrusb_setting)))
+                       return -EFAULT;
+
+               rv = xrusb_disable_txt_rxt(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+
+               // Enable GPIOs 9-6 as outputs
+               rv = xrusb_gpio_dir_out(xrusb, 0x3c0);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = set_txcvr_slew_mode(xrusb,
+                       xrusb->port_setting.slew_mode);
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = update_portparamfile(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_SET_RS485_PIN:
+               if (copy_from_user((void *)&(xrusb->port_setting),
+                       (void __user *)arg, sizeof(struct xrusb_setting)))
+                       return -EFAULT;
+
+               if (xrusb->port_setting.txcvr_mode < 2)
+                       rv = set_rs485_pin(xrusb, 0);
+               else
+                       rv = set_rs485_pin(xrusb,
+                               xrusb->port_setting.txcvr_dir_pin);
+
+               if (rv < 0)
+                       return -EFAULT;
+
+               rv = update_portparamfile(xrusb);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_ENABLE_RS485_LOW:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode, &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               reg_value &= 0x3f7; // bit-3 = 0 for active low
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_gpio_mode,
+                       (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_RS485_DELAY:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev, "get_user error\n");
+                       return -EFAULT;
+               }
+               if (param_rw) {
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_rs485_delay, 15);
+                       if (rv < 0)
+                               return -EFAULT;
+               } else {
+                       rv = xrusb_set_reg(xrusb,
+                               xrusb->reg_map.uart_rs485_delay, 0);
+                       if (rv < 0)
+                               return -EFAULT;
+               }
+               rv = 0;
+               break;
+       case XRUSB_ENABLE_DTRDSR_FLOW:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev, "get_user error\n");
+                       return -EFAULT;
+               }
+               // Value = Read GPIO_MODE register & 0x300;
+               // GPIO_MODE = Value | 0x02;
+               // FLOW = 0x1;
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode, &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               reg_value &= 0x300;
+               reg_value |= 0x02;
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode,
+                       (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_flow,
+                       UART_FLOW_MODE_HW);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = 0;
+               break;
+       case XRUSB_DISABLE_FLOW_CONTROL:
+               if (get_user(param_rw, (int __user *)arg)) {
+                       dev_dbg(&xrusb->control->dev, "get_user error\n");
+                       return -EFAULT;
+               }
+               //      Value = Read GPIO_MODE register & 0x300;
+               //      GPIO_MODE = Value;
+               //      FLOW = 0x00;
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode, &reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               reg_value &= 0x300;
+               rv = xrusb_set_reg(xrusb,
+                       xrusb->reg_map.uart_gpio_mode,
+                       (int)reg_value);
+               if (rv < 0)
+                       return -EFAULT;
+               rv = xrusb_set_reg(xrusb, xrusb->reg_map.uart_flow,
+                       UART_FLOW_MODE_NONE);
+               if (rv < 0)
+                       return -EFAULT;
+               break;
+       case XRUSB_GET_FLOW_CONTROL:
+               rv = xrusb_get_reg(xrusb,
+                       xrusb->reg_map.uart_flow, &reg_value);
+               if (put_user(reg_value, (int __user *)arg)) {
+                       dev_err(&xrusb->control->dev, "put_user error\n");
+                       return -EFAULT;
+               }
+               rv = 0;
+               break;
+       }
+       return rv;
+}
+
+static void xrusb_tty_set_termios(struct tty_struct *tty,
+               struct ktermios *termios_old)
+{
+       struct xrusb *xrusb = tty->driver_data;
+       struct ktermios *termios = &tty->termios;
+       unsigned int   cflag = termios->c_cflag;
+       struct usb_cdc_line_coding newline;
+       int newctrl = xrusb->ctrlout;
+
+       xrusb_disable(xrusb);
+       newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
+       newline.bCharFormat = termios->c_cflag & CSTOPB ? 1 : 0;
+       newline.bParityType = termios->c_cflag & PARENB ?
+                               (termios->c_cflag & PARODD ? 1 : 2) +
+                               (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
+       xrusb->wide_mode = 0;
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:/*using CS5 replace of the 9 bit data mode*/
+               newline.bDataBits = 9;
+               xrusb->wide_mode = 1;
+               break;
+       case CS6:
+               newline.bDataBits = 6;
+               break;
+       case CS7:
+               newline.bDataBits = 7;
+               break;
+       case CS8:
+       default:
+               newline.bDataBits = 8;
+               break;
+       }
+       /* FIXME: Needs to clear unsupported bits in the termios */
+       xrusb->clocal = ((termios->c_cflag & CLOCAL) != 0);
+
+       if (!newline.dwDTERate) {
+               newline.dwDTERate = xrusb->line.dwDTERate;
+               newctrl &= ~XRUSB_CTRL_DTR;
+       } else
+               newctrl |=  XRUSB_CTRL_DTR;
+
+       if (newctrl != xrusb->ctrlout)
+               xrusb_set_control(xrusb,
+                       xrusb->ctrlout = newctrl);
+
+       xrusb_set_flow_mode(xrusb, tty, cflag);
+       if (xrusb->wide_mode)
+               xrusb_set_wide_mode(xrusb, 1);
+       else if (!xrusb->wide_mode)
+               xrusb_set_wide_mode(xrusb, 0);
+
+       if (memcmp(&xrusb->line, &newline, sizeof(newline))) {
+               memcpy(&xrusb->line, &newline, sizeof(newline));
+               xrusb_set_line(xrusb, &xrusb->line);
+       }
+       xrusb_enable(xrusb);
+}
+
+static const struct tty_port_operations xrusb_port_ops = {
+       .shutdown = xrusb_port_shutdown,
+       .activate = xrusb_port_activate,
+       .destruct = xrusb_port_destruct,
+};
+
+/*
+ * USB probe and disconnect routines.
+ */
+
+/* Little helpers: write/read buffers free */
+static void xrusb_write_buffers_free(struct xrusb *xrusb)
+{
+       int i;
+       struct xrusb_wb *wb;
+
+       for (wb = &xrusb->wb[0], i = 0; i < XRUSB_NW; i++, wb++)
+               usb_free_coherent(xrusb->dev,
+                       xrusb->writesize, wb->buf, wb->dmah);
+}
+
+static void xrusb_read_buffers_free(struct xrusb *xrusb)
+{
+       int i;
+
+       for (i = 0; i < xrusb->rx_buflimit; i++)
+               usb_free_coherent(xrusb->dev, xrusb->readsize,
+                         xrusb->read_buffers[i].base,
+                         xrusb->read_buffers[i].dma);
+}
+
+/* Little helper: write buffers allocate */
+static int xrusb_write_buffers_alloc(struct xrusb *xrusb)
+{
+       int i;
+       struct xrusb_wb *wb;
+
+       for (wb = &xrusb->wb[0], i = 0; i < XRUSB_NW; i++, wb++) {
+               wb->buf = usb_alloc_coherent(xrusb->dev,
+                       xrusb->writesize, GFP_KERNEL,
+                       &wb->dmah);
+               if (!wb->buf) {
+                       while (i != 0) {
+                               --i;
+                               --wb;
+                               usb_free_coherent(xrusb->dev,
+                                       xrusb->writesize,
+                                       wb->buf, wb->dmah);
+                       }
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+static int xrusb_probe(struct usb_interface *intf,
+                    const struct usb_device_id *id)
+{
+       struct usb_cdc_union_desc *union_header = NULL;
+       unsigned char *buffer = intf->altsetting->extra;
+       int buflen = intf->altsetting->extralen;
+       struct usb_interface *control_interface;
+       struct usb_interface *data_interface;
+       struct usb_endpoint_descriptor *epctrl = NULL;
+       struct usb_endpoint_descriptor *epread = NULL;
+       struct usb_endpoint_descriptor *epwrite = NULL;
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct xrusb *xrusb;
+       int minor;
+       int ctrlsize, readsize;
+       u8 *buf;
+       u8 ac_management_function = 0;
+       u8 call_management_function = 0;
+       int call_interface_num = -1;
+       int data_interface_num = -1;
+       unsigned long quirks;
+       int num_rx_buf;
+       int i;
+       int combined_interfaces = 0;
+       struct device *tty_dev;
+       int rv = -ENOMEM;
+
+       /* normal quirks */
+       quirks = (unsigned long)id->driver_info;
+
+       if (quirks == IGNORE_DEVICE)
+               return -ENODEV;
+
+       num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : XRUSB_NR;
+
+       /* handle quirks deadly to normal probing*/
+       if (quirks == NO_UNION_NORMAL) {
+               data_interface = usb_ifnum_to_if(usb_dev, 1);
+               control_interface = usb_ifnum_to_if(usb_dev, 0);
+               goto skip_normal_probe;
+       }
+
+       /* normal probing*/
+       if (!buffer) {
+               dev_err(&intf->dev, "Weird descriptor references\n");
+               return -EINVAL;
+       }
+
+       if (!buflen) {
+               if (intf->cur_altsetting->endpoint &&
+                               intf->cur_altsetting->endpoint->extralen &&
+                               intf->cur_altsetting->endpoint->extra) {
+                       dev_dbg(&intf->dev,
+                               "Seeking extra descriptors on endpoint\n");
+                       buflen = intf->cur_altsetting->endpoint->extralen;
+                       buffer = intf->cur_altsetting->endpoint->extra;
+               } else {
+                       dev_err(&intf->dev,
+                               "Zero length descriptor references\n");
+                       return -EINVAL;
+               }
+       }
+
+       while (buflen > 0) {
+               if (buffer[1] != USB_DT_CS_INTERFACE) {
+                       dev_err(&intf->dev, "skipping garbage\n");
+                       goto next_desc;
+               }
+
+               switch (buffer[2]) {
+               case USB_CDC_UNION_TYPE: /* we've found it */
+                       if (union_header) {
+                               dev_err(&intf->dev, "> 1  union descriptor");
+                               dev_err(&intf->dev, "skipping ...\n");
+                               goto next_desc;
+                       }
+                       union_header = (struct usb_cdc_union_desc *)buffer;
+                       break;
+               case USB_CDC_HEADER_TYPE: /* maybe check version */
+                       break; /* for now we ignore it */
+               case USB_CDC_ACM_TYPE:
+                       ac_management_function = buffer[3];
+                       break;
+               case USB_CDC_CALL_MANAGEMENT_TYPE:
+                       call_management_function = buffer[3];
+                       call_interface_num = buffer[4];
+                       //if ((quirks & NOT_A_MODEM) == 0 &&
+                       //      (call_management_function & 3) != 3)
+                       //      dev_err(&intf->dev, "This device cannot
+                       //              do calls on its own. It is
+                       //              not a modem.\n");
+                       break;
+               default:
+                       /* there are LOTS more CDC descriptors that
+                        * could legitimately be found here.
+                        */
+                       dev_dbg(&intf->dev, "Ignoring descriptor: ");
+                       dev_dbg(&intf->dev, "type %02x, length %d\n",
+                               buffer[2], buffer[0]);
+                       break;
+               }
+next_desc:
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       if (!union_header) {
+               if (call_interface_num > 0) {
+                       dev_dbg(&intf->dev, "No union descriptor, using");
+                       dev_dbg(&intf->dev, " call management descriptor\n");
+                       /* quirks for Droids MuIn LCD */
+                       if (quirks & NO_DATA_INTERFACE)
+                               data_interface = usb_ifnum_to_if(usb_dev, 0);
+                       else
+                               data_interface = usb_ifnum_to_if(usb_dev,
+                                       (data_interface_num =
+                                               call_interface_num));
+                       control_interface = intf;
+               } else {
+                       if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
+                               dev_dbg(&intf->dev, "No union descriptor,");
+                               dev_dbg(&intf->dev, "giving up\n");
+                               return -ENODEV;
+                       } else {
+                               dev_warn(&intf->dev, "No union descriptor, ");
+                               dev_warn(&intf->dev, "testing for castrated 
device\n");
+                               combined_interfaces = 1;
+                               control_interface = data_interface = intf;
+                               goto look_for_collapsed_interface;
+                       }
+               }
+       } else {
+               control_interface = usb_ifnum_to_if(usb_dev,
+                       union_header->bMasterInterface0);
+               data_interface = usb_ifnum_to_if(usb_dev,
+                       (data_interface_num = union_header->bSlaveInterface0));
+               if (!control_interface || !data_interface) {
+                       dev_dbg(&intf->dev, "no interfaces\n");
+                       return -ENODEV;
+               }
+       }
+
+       if (data_interface_num != call_interface_num) {
+               dev_dbg(&intf->dev, "Separate call control interface. ");
+               dev_dbg(&intf->dev, "That is not fully supported.\n");
+       }
+
+       if (control_interface == data_interface) {
+               /* some broken devices designed for windows work this way */
+               dev_warn(&intf->dev, "Control and data interfaces");
+               dev_warn(&intf->dev, "are not separated!\n");
+               combined_interfaces = 1;
+               /* a popular other OS doesn't use it */
+               quirks |= NO_CAP_LINE;
+               if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
+                       dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
+                       return -EINVAL;
+               }
+look_for_collapsed_interface:
+               for (i = 0; i < 3; i++) {
+                       struct usb_endpoint_descriptor *ep;
+
+                       ep = &data_interface->cur_altsetting->endpoint[i].desc;
+
+                       if (usb_endpoint_is_int_in(ep))
+                               epctrl = ep;
+                       else if (usb_endpoint_is_bulk_out(ep))
+                               epwrite = ep;
+                       else if (usb_endpoint_is_bulk_in(ep))
+                               epread = ep;
+                       else
+                               return -EINVAL;
+               }
+               if (!epctrl || !epread || !epwrite)
+                       return -ENODEV;
+               else
+                       goto made_compressed_probe;
+       }
+
+skip_normal_probe:
+
+       /*workaround for switched interfaces */
+       if (data_interface->cur_altsetting->desc.bInterfaceClass
+                                               != CDC_DATA_INTERFACE_TYPE) {
+               if (control_interface->cur_altsetting->desc.bInterfaceClass
+                                               == CDC_DATA_INTERFACE_TYPE) {
+                       struct usb_interface *t;
+
+                       dev_dbg(&intf->dev,
+                               "Your device has switched interfaces.\n");
+                       t = control_interface;
+                       control_interface = data_interface;
+                       data_interface = t;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       /* Accept probe requests only for the control interface */
+       if (!combined_interfaces && intf != control_interface)
+               return -ENODEV;
+
+       if (!combined_interfaces && usb_interface_claimed(data_interface)) {
+               /* valid in this context */
+               dev_dbg(&intf->dev, "The data interface isn't available\n");
+               return -EBUSY;
+       }
+
+
+       if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+           control_interface->cur_altsetting->desc.bNumEndpoints == 0)
+               return -EINVAL;
+
+       epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
+       epread = &data_interface->cur_altsetting->endpoint[0].desc;
+       epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
+
+
+       /* workaround for switched endpoints */
+       if (!usb_endpoint_dir_in(epread)) {
+               /* descriptors are swapped */
+               struct usb_endpoint_descriptor *t;
+
+               dev_dbg(&intf->dev,
+                       "The data interface has switched endpoints\n");
+               t = epread;
+               epread = epwrite;
+               epwrite = t;
+       }
+made_compressed_probe:
+       dev_dbg(&intf->dev, "interfaces are valid\n");
+
+       xrusb = kzalloc(sizeof(struct xrusb), GFP_KERNEL);
+       if (xrusb == NULL) {
+               dev_err(&intf->dev, "out of memory (xrusb kzalloc)\n");
+               goto alloc_fail;
+       }
+
+       minor = xrusb_alloc_minor(xrusb);
+       if (minor == XRUSB_TTY_MINORS) {
+               dev_err(&intf->dev, "no more free xrusb devices\n");
+               kfree(xrusb);
+               return -ENODEV;
+       }
+
+       ctrlsize = usb_endpoint_maxp(epctrl);
+       readsize = usb_endpoint_maxp(epread) *
+                               (quirks == SINGLE_RX_URB ? 1 : 2);
+       xrusb->combined_interfaces = combined_interfaces;
+       xrusb->writesize = usb_endpoint_maxp(epwrite) * 20;
+       xrusb->control = control_interface;
+       xrusb->data = data_interface;
+       xrusb->minor = minor;
+       xrusb->dev = usb_dev;
+       xrusb->ctrl_caps = ac_management_function;
+       if (quirks & NO_CAP_LINE)
+               xrusb->ctrl_caps &= ~USB_CDC_CAP_LINE;
+       xrusb->ctrlsize = ctrlsize;
+       xrusb->readsize = readsize;
+       xrusb->rx_buflimit = num_rx_buf;
+       INIT_WORK(&xrusb->work, xrusb_softint);
+       spin_lock_init(&xrusb->write_lock);
+       spin_lock_init(&xrusb->read_lock);
+       mutex_init(&xrusb->mutex);
+       xrusb->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+       xrusb->is_int_ep = usb_endpoint_xfer_int(epread);
+       if (xrusb->is_int_ep)
+               xrusb->bInterval = epread->bInterval;
+       tty_port_init(&xrusb->port);
+       xrusb->port.ops = &xrusb_port_ops;
+       xrusb->DeviceVendor = id->idVendor;
+       xrusb->DeviceProduct = id->idProduct;
+
+       xrusb->channel = epwrite->bEndpointAddress;
+       //dev_err(&intf->dev, "epwrite->bEndpointAddress =%d\n",
+       //      epwrite->bEndpointAddress);
+
+       buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL,
+               &xrusb->ctrl_dma);
+       if (!buf) {
+               dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
+               goto alloc_fail2;
+       }
+       xrusb->ctrl_buffer = buf;
+
+       if (xrusb_write_buffers_alloc(xrusb) < 0) {
+               dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
+               goto alloc_fail4;
+       }
+
+       xrusb->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!xrusb->ctrlurb) {
+               dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+               goto alloc_fail5;
+       }
+       for (i = 0; i < num_rx_buf; i++) {
+               struct xrusb_rb *rb = &(xrusb->read_buffers[i]);
+               struct urb *urb;
+
+               rb->base = usb_alloc_coherent(xrusb->dev, readsize, GFP_KERNEL,
+                                                               &rb->dma);
+               if (!rb->base) {
+                       dev_err(&intf->dev, "out of memory ");
+                       dev_err(&intf->dev, "(read bufs usb_alloc_coherent)\n");
+                       goto alloc_fail6;
+               }
+               rb->index = i;
+               rb->instance = xrusb;
+
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       dev_err(&intf->dev, "out of memory ");
+                       dev_err(&intf->dev, "(read bufs usb_alloc_urb)\n");
+                       goto alloc_fail6;
+               }
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               urb->transfer_dma = rb->dma;
+               if (xrusb->is_int_ep) {
+                       usb_fill_int_urb(urb, xrusb->dev,
+                                        xrusb->rx_endpoint,
+                                        rb->base,
+                                        xrusb->readsize,
+                                        xrusb_read_bulk_callback, rb,
+                                        xrusb->bInterval);
+               } else {
+                       usb_fill_bulk_urb(urb, xrusb->dev,
+                                         xrusb->rx_endpoint,
+                                         rb->base,
+                                         xrusb->readsize,
+                                         xrusb_read_bulk_callback, rb);
+               }
+
+               xrusb->read_urbs[i] = urb;
+               __set_bit(i, &xrusb->read_urbs_free);
+       }
+       for (i = 0; i < XRUSB_NW; i++) {
+               struct xrusb_wb *snd = &(xrusb->wb[i]);
+
+               snd->urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (snd->urb == NULL) {
+                       dev_err(&intf->dev, "out of memory ");
+                       dev_err(&intf->dev, "(write urbs usb_alloc_urb)\n");
+                       goto alloc_fail7;
+               }
+
+               if (usb_endpoint_xfer_int(epwrite))
+                       usb_fill_int_urb(snd->urb,
+                               usb_dev,
+                               usb_sndintpipe(usb_dev,
+                                       epwrite->bEndpointAddress),
+                               NULL,
+                               xrusb->writesize,
+                               xrusb_write_bulk,
+                               snd,
+                               epwrite->bInterval);
+               else
+                       usb_fill_bulk_urb(snd->urb,
+                               usb_dev,
+                               usb_sndbulkpipe(usb_dev,
+                                       epwrite->bEndpointAddress),
+                               NULL,
+                               xrusb->writesize,
+                               xrusb_write_bulk,
+                               snd);
+               snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               snd->instance = xrusb;
+       }
+
+       usb_set_intfdata(intf, xrusb);
+
+       i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
+       if (i < 0)
+               goto alloc_fail7;
+
+       usb_fill_int_urb(xrusb->ctrlurb, usb_dev,
+                        usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
+                        xrusb->ctrl_buffer, ctrlsize, xrusb_ctrl_irq, xrusb,
+                        /* works around buggy devices */
+                        epctrl->bInterval ? epctrl->bInterval : 0xff);
+       xrusb->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       xrusb->ctrlurb->transfer_dma = xrusb->ctrl_dma;
+
+       //dev_info(&intf->dev, "ttyXRUSB%d: USB XRUSB device\n", minor);
+
+       xrusb_reg_init(xrusb);
+
+       xrusb_set_control(xrusb, xrusb->ctrlout);
+
+       xrusb->line.dwDTERate = cpu_to_le32(9600);
+       xrusb->line.bDataBits = 8;
+       xrusb_set_line(xrusb, &xrusb->line);
+
+       usb_driver_claim_interface(&xrusb_driver, data_interface, xrusb);
+       usb_set_intfdata(data_interface, xrusb);
+
+       usb_get_intf(control_interface);
+
+       tty_dev = tty_port_register_device(&xrusb->port,
+               xrusb_tty_driver, minor, &control_interface->dev);
+
+       if (IS_ERR(tty_dev)) {
+               rv = PTR_ERR(tty_dev);
+               goto alloc_fail8;
+       }
+
+       return 0;
+
+alloc_fail8:
+       device_remove_file(&xrusb->control->dev, &dev_attr_bmCapabilities);
+alloc_fail7:
+       usb_set_intfdata(intf, NULL);
+       for (i = 0; i < XRUSB_NW; i++)
+               usb_free_urb(xrusb->wb[i].urb);
+alloc_fail6:
+       for (i = 0; i < num_rx_buf; i++)
+               usb_free_urb(xrusb->read_urbs[i]);
+       xrusb_read_buffers_free(xrusb);
+       usb_free_urb(xrusb->ctrlurb);
+alloc_fail5:
+       xrusb_write_buffers_free(xrusb);
+alloc_fail4:
+       usb_free_coherent(usb_dev, ctrlsize,
+               xrusb->ctrl_buffer, xrusb->ctrl_dma);
+alloc_fail2:
+       xrusb_release_minor(xrusb);
+       kfree(xrusb);
+alloc_fail:
+       return rv;
+}
+
+static void stop_data_traffic(struct xrusb *xrusb)
+{
+       int i;
+
+       dev_dbg(&xrusb->control->dev, "%s\n", __func__);
+
+       usb_kill_urb(xrusb->ctrlurb);
+       for (i = 0; i < XRUSB_NW; i++)
+               usb_kill_urb(xrusb->wb[i].urb);
+       for (i = 0; i < xrusb->rx_buflimit; i++)
+               usb_kill_urb(xrusb->read_urbs[i]);
+
+       cancel_work_sync(&xrusb->work);
+}
+
+static void xrusb_disconnect(struct usb_interface *intf)
+{
+       struct xrusb *xrusb = usb_get_intfdata(intf);
+       struct tty_struct *tty;
+
+       /* sibling interface is already cleaning up */
+       if (!xrusb)
+               return;
+
+       mutex_lock(&xrusb->mutex);
+       xrusb->disconnected = true;
+
+       device_remove_file(&xrusb->control->dev,
+               &dev_attr_bmCapabilities);
+       usb_set_intfdata(xrusb->control, NULL);
+       usb_set_intfdata(xrusb->data, NULL);
+       mutex_unlock(&xrusb->mutex);
+
+       tty = tty_port_tty_get(&xrusb->port);
+       if (tty) {
+               tty_vhangup(tty);
+               tty_kref_put(tty);
+       }
+
+       stop_data_traffic(xrusb);
+
+       tty_unregister_device(xrusb_tty_driver, xrusb->minor);
+
+       xrusb_write_buffers_free(xrusb);
+       usb_free_coherent(xrusb->dev, xrusb->ctrlsize,
+               xrusb->ctrl_buffer, xrusb->ctrl_dma);
+       xrusb_read_buffers_free(xrusb);
+
+       if (!xrusb->combined_interfaces)
+               usb_driver_release_interface(&xrusb_driver,
+                       intf == xrusb->control ?
+                       xrusb->data : xrusb->control);
+
+       tty_port_put(&xrusb->port);
+}
+
+#ifdef CONFIG_PM
+static int xrusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct xrusb *xrusb = usb_get_intfdata(intf);
+       int cnt;
+
+       if (PMSG_IS_AUTO(message)) {
+               int b;
+
+               spin_lock_irq(&xrusb->write_lock);
+               b = xrusb->transmitting;
+               spin_unlock_irq(&xrusb->write_lock);
+               if (b)
+                       return -EBUSY;
+       }
+
+       spin_lock_irq(&xrusb->read_lock);
+       spin_lock(&xrusb->write_lock);
+       cnt = xrusb->susp_count++;
+       spin_unlock(&xrusb->write_lock);
+       spin_unlock_irq(&xrusb->read_lock);
+
+       if (cnt)
+               return 0;
+
+       if (test_bit(ASYNCB_INITIALIZED, &xrusb->port.flags))
+               stop_data_traffic(xrusb);
+
+       return 0;
+}
+
+static int xrusb_resume(struct usb_interface *intf)
+{
+       struct xrusb *xrusb = usb_get_intfdata(intf);
+       struct xrusb_wb *wb;
+       int rv = 0;
+       int cnt;
+
+       xrusb_reg_init(xrusb);
+
+       spin_lock_irq(&xrusb->read_lock);
+       xrusb->susp_count -= 1;
+       cnt = xrusb->susp_count;
+       spin_unlock_irq(&xrusb->read_lock);
+
+       if (cnt)
+               return 0;
+
+       if (test_bit(ASYNCB_INITIALIZED, &xrusb->port.flags)) {
+               rv = usb_submit_urb(xrusb->ctrlurb, GFP_NOIO);
+
+               spin_lock_irq(&xrusb->write_lock);
+               if (xrusb->delayed_wb) {
+                       wb = xrusb->delayed_wb;
+                       xrusb->delayed_wb = NULL;
+                       spin_unlock_irq(&xrusb->write_lock);
+                       xrusb_start_wb(xrusb, wb);
+               } else {
+                       spin_unlock_irq(&xrusb->write_lock);
+               }
+
+               /*
+                * delayed error checking because we must
+                * do the write path at all cost
+                */
+               if (rv < 0)
+                       goto err_out;
+
+               rv = xrusb_submit_read_urbs(xrusb, GFP_NOIO);
+       }
+
+err_out:
+       return rv;
+}
+
+static int xrusb_reset_resume(struct usb_interface *intf)
+{
+       struct xrusb *xrusb = usb_get_intfdata(intf);
+
+       if (test_bit(ASYNCB_INITIALIZED, &xrusb->port.flags))
+               tty_port_tty_hangup(&xrusb->port, false);
+       return xrusb_resume(intf);
+}
+
+#endif /* CONFIG_PM */
+
+/*
+ * USB driver structure.
+ */
+static const struct usb_device_id xrusb_ids[] = {
+       { USB_DEVICE(0x04e2, 0x1410)},
+       { USB_DEVICE(0x04e2, 0x1411)},
+       { USB_DEVICE(0x04e2, 0x1412)},
+       { USB_DEVICE(0x04e2, 0x1414)},
+       { USB_DEVICE(0x04e2, 0x1420)},
+       { USB_DEVICE(0x04e2, 0x1422)},
+       { USB_DEVICE(0x04e2, 0x1424)},
+       { USB_DEVICE(0x04e2, 0x1400)}, // XR2280x ch A
+       { USB_DEVICE(0x04e2, 0x1401)}, // XR2280x ch B
+       { USB_DEVICE(0x04e2, 0x1402)}, // XR2280x ch C
+       { USB_DEVICE(0x04e2, 0x1403)}, // XR2280x ch D
+       { }
+};
+
+MODULE_DEVICE_TABLE(usb, xrusb_ids);
+
+static struct usb_driver xrusb_driver = {
+       .name =                 "xrusb_serial",
+       .probe =                xrusb_probe,
+       .disconnect =           xrusb_disconnect,
+#ifdef CONFIG_PM
+       .suspend =              xrusb_suspend,
+       .resume =               xrusb_resume,
+       .reset_resume =         xrusb_reset_resume,
+#endif
+       .id_table =             xrusb_ids,
+#ifdef CONFIG_PM
+       .supports_autosuspend = 1,
+#endif
+       .disable_hub_initiated_lpm = 1,
+};
+
+/*
+ * TTY driver structures.
+ */
+
+static const struct tty_operations xrusb_ops = {
+       .install =              xrusb_tty_install,
+       .open =                 xrusb_tty_open,
+       .close =                xrusb_tty_close,
+       .cleanup =              xrusb_tty_cleanup,
+       .hangup =               xrusb_tty_hangup,
+       .write =                xrusb_tty_write,
+       .write_room =           xrusb_tty_write_room,
+       .ioctl =                xrusb_tty_ioctl,
+       .throttle =             xrusb_tty_throttle,
+       .unthrottle =           xrusb_tty_unthrottle,
+       .chars_in_buffer =      xrusb_tty_chars_in_buffer,
+       .break_ctl =            xrusb_tty_break_ctl,
+       .set_termios =          xrusb_tty_set_termios,
+       .tiocmget =             xrusb_tty_tiocmget,
+       .tiocmset =             xrusb_tty_tiocmset,
+};
+
+/*
+ * Init / exit.
+ */
+
+static int __init xrusb_init(void)
+{
+       int rv;
+
+       xrusb_tty_driver = alloc_tty_driver(XRUSB_TTY_MINORS);
+       if (!xrusb_tty_driver)
+               return -ENOMEM;
+       xrusb_tty_driver->driver_name = "xrusb",
+       xrusb_tty_driver->name = "ttyXRUSB",
+       xrusb_tty_driver->major = XRUSB_TTY_MAJOR,
+       xrusb_tty_driver->minor_start = 0,
+       xrusb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+       xrusb_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+       xrusb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       xrusb_tty_driver->init_termios = tty_std_termios;
+       xrusb_tty_driver->init_termios.c_cflag = B9600 | CS8 |
+               CREAD | HUPCL | CLOCAL;
+       tty_set_operations(xrusb_tty_driver, &xrusb_ops);
+
+       rv = tty_register_driver(xrusb_tty_driver);
+       if (rv) {
+               put_tty_driver(xrusb_tty_driver);
+               return rv;
+       }
+
+       rv = usb_register(&xrusb_driver);
+       if (rv) {
+               tty_unregister_driver(xrusb_tty_driver);
+               put_tty_driver(xrusb_tty_driver);
+               return rv;
+       }
+
+       printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
+
+       return 0;
+}
+
+static void __exit xrusb_exit(void)
+{
+       usb_deregister(&xrusb_driver);
+       tty_unregister_driver(xrusb_tty_driver);
+       put_tty_driver(xrusb_tty_driver);
+}
+
+module_init(xrusb_init);
+module_exit(xrusb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(XRUSB_TTY_MAJOR);
diff --git a/drivers/usb/serial/xrusb_serial.h 
b/drivers/usb/serial/xrusb_serial.h
new file mode 100644
index 000000000000..c4b214b3e576
--- /dev/null
+++ b/drivers/usb/serial/xrusb_serial.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Includes for xrusb_serial.c
+ */
+
+/*
+ * CMSPAR, some architectures can't have space and mark parity.
+ */
+
+#ifndef CMSPAR
+#define CMSPAR         0
+#endif
+
+/*
+ * Major and minor numbers.
+ */
+
+#define XRUSB_TTY_MAJOR                266
+#define XRUSB_TTY_MINORS       256
+
+/*
+ * Requests.
+ */
+
+#define USB_RT_XRUSB           (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+/*
+ * Output control lines.
+ */
+
+#define XRUSB_CTRL_DTR         0x01
+#define XRUSB_CTRL_RTS         0x02
+
+/*
+ * Input control lines and line errors.
+ */
+
+#define XRUSB_CTRL_DCD         0x01
+#define XRUSB_CTRL_DSR         0x02
+#define XRUSB_CTRL_BRK         0x04
+#define XRUSB_CTRL_RI          0x08
+
+#define XRUSB_CTRL_FRAMING     0x10
+#define XRUSB_CTRL_PARITY      0x20
+#define XRUSB_CTRL_OVERRUN     0x40
+
+/*
+ * Internal driver structures.
+ */
+
+/*
+ * The only reason to have several buffers is to accommodate assumptions
+ * in line disciplines. They ask for empty space amount, receive our URB size,
+ * and proceed to issue several 1-character writes, assuming they will fit.
+ * The very first write takes a complete URB. Fortunately, this only happens
+ * when processing onlcr, so we only need 2 buffers. These values must be
+ * powers of 2.
+ */
+
+#define XRUSB_NW  16
+#define XRUSB_NR  16
+
+#define WIDE_MODE_PARITY       0x1
+#define WIDE_MODE_BREAK                0x2
+#define WIDE_MODE_FRAME                0x4
+#define WIDE_MODE_OVERRUN      0x8
+
+struct xrusb_wb {
+       unsigned char *buf;
+       dma_addr_t dmah;
+       int len;
+       int use;
+       struct urb      *urb;
+       struct xrusb    *instance;
+};
+
+struct xrusb_rb {
+       int     size;
+       unsigned char   *base;
+       dma_addr_t      dma;
+       int     index;
+       struct xrusb    *instance;
+};
+
+struct reg_addr_map {
+       unsigned int uart_enable;
+       unsigned int uart_format;
+       unsigned int uart_flow;
+       unsigned int uart_xon_char;
+       unsigned int uart_xoff_char;
+       unsigned int uart_tx_break;
+       unsigned int uart_rs485_delay;
+       unsigned int uart_gpio_mode;
+       unsigned int uart_gpio_dir;
+       unsigned int uart_gpio_set;
+       unsigned int uart_gpio_clr;
+       unsigned int uart_gpio_status;
+       unsigned int uart_gpio_int_mask;
+       unsigned int uart_customized_int;
+       unsigned int uart_gpio_open_drain;
+       unsigned int uart_gpio_pull_up_enable;
+       unsigned int uart_gpio_pull_down_enable;
+       unsigned int uart_loopback;
+       unsigned int uart_low_latency;
+       unsigned int uart_custom_driver;
+};
+
+struct xrusb_setting {
+       unsigned int txcvr_mode1_pin;
+       unsigned int txcvr_mode0_pin;
+       unsigned int txcvr_dir_pin;
+       unsigned int txcvr_term_pin;
+       unsigned int txcvr_slew_pin;
+       unsigned int txcvr_mode;
+       unsigned int term_mode;
+       unsigned int slew_mode;
+       unsigned int low_latency_mode;
+};
+
+struct xrusb {
+       struct usb_device *dev;                         /* the corresponding 
usb device */
+       struct usb_interface *control;                  /* control interface */
+       struct usb_interface *data;                     /* data interface */
+       struct tty_port port;                           /* our tty port data */
+       struct urb *ctrlurb;                            /* urbs */
+       u8 *ctrl_buffer;                                /* buffers of urbs */
+       dma_addr_t ctrl_dma;                            /* dma handles of 
buffers */
+       struct xrusb_wb wb[XRUSB_NW];
+       unsigned long read_urbs_free;
+       struct urb *read_urbs[XRUSB_NR];
+       struct xrusb_rb read_buffers[XRUSB_NR];
+       struct xrusb_wb *putbuffer;
+       int rx_buflimit;
+       int rx_endpoint;
+       spinlock_t read_lock;
+       int transmitting;
+       spinlock_t write_lock;
+       struct mutex mutex;
+       bool disconnected;
+       struct usb_cdc_line_coding line;                /* bits, stop, parity */
+       struct work_struct work;                        /* work queue entry for 
line discipline waking up */
+       unsigned int ctrlin;                            /* input control lines 
(DCD, DSR, RI, break, overruns) */
+       unsigned int ctrlout;                           /* output control lines 
(DTR, RTS) */
+       unsigned int writesize;                         /* max packet size for 
the output bulk endpoint */
+       unsigned int readsize, ctrlsize;                        /* buffer sizes 
for freeing */
+       unsigned int minor;                             /* xrusb minor number */
+       unsigned char clocal;                           /* termios CLOCAL */
+       unsigned int ctrl_caps;                         /* control capabilities 
from the class specific header */
+       unsigned int susp_count;                        /* number of suspended 
interfaces */
+       unsigned int combined_interfaces:1;             /* control and data 
collapsed */
+       unsigned int is_int_ep:1;                       /* interrupt endpoints 
contrary to spec used */
+       unsigned int throttled:1;                       /* actually throttled */
+       unsigned int throttle_req:1;                    /* throttle requested */
+       u8 bInterval;
+
+       struct xrusb_wb *delayed_wb;            /* write queued for a device 
about to be woken */
+       unsigned int channel;
+       int wide_mode;                                  /* USB: wide mode, TTY: 
flags per character */
+       int have_extra_byte;
+       int extra_byte;
+
+       unsigned short DeviceVendor;
+       unsigned short DeviceProduct;
+
+       int found_smbios_caracalla_config;              /* Modes pre-programmed 
in BIOS for Caracalla board */
+       unsigned char channel_config;                   /* Mode setting for 
each channel */
+
+       struct reg_addr_map reg_map;
+       struct xrusb_setting port_setting;
+       unsigned char have_txcvr_config;
+};
+
+/*
+ * SMBIOS code snippets below borrowed from
+ * https://sourceforge.net/projects/smbios/
+ *
+ */
+
+#ifndef __SM_BIOS_H__
+#define __SM_BIOS_H__
+
+#ifdef _EN_DEBUG_
+#define SM_BIOS_DEBUG(fmt, args...) printk(KERN_DEBUG "smbios: " fmt, ## args)
+#else
+#define SM_BIOS_DEBUG(fmt, args...) /* not debugging: nothing */
+#endif
+
+/*
+ *   Magic numbers
+ */
+
+/** start address of BIOS segment to scanned for SM-BIOS and DMI-BIOS */
+#define BIOS_START_ADDRESS      0xF0000
+/** length of the scanned BIOS area for SM-BIOS and DMI-BIOS */
+#define BIOS_MAP_LENGTH         0x10000
+/** magic 4 bytes to identify SM-BIOS entry point, paragraph boundary */
+#define SMBIOS_MAGIC_DWORD      0x5F4D535F /* anchor string "_SM_" */
+/** identifier for SM-BIOS structures within SM-BIOS entry point */
+#define DMI_STRING              "_DMI_"
+
+/*
+ *   Structures
+ */
+
+/** SM-BIOS entry point structure
+ * the SMBIOS Entry Point structure described below can be located by
+ * application software by searching for the anchor string on paragraph
+ * (16 byte) boundaries within the physical memory address range 000F0000h to
+ * 000FFFFFh. This entry point encapsulates an intermediate anchor string
+ * that is used by some existing DMI browsers.
+ *
+ * @note While the SMBIOS Major and Minor Versions (offsets 06h and 07h)
+ * currently duplicate the information present in the SMBIOS BCD Revision
+ * (offset 1Dh), they provide a path for future growth in this specification.
+ * The BCD Revision, for example, provides only a single digit for each of
+ * the major and minor version numbers.
+ */
+struct smbios_entry_point_struct {
+       /** "_SM_", specified as four ASCII characters (5F 53 4D 5F) */
+       __u32 anchor_string;
+       /** checksum of the Entry Point Structure (EPS). This value, when added 
to
+        * all other bytes in the EPS, will result in the value 00h (using 8 bit
+        * addition calculations). Values in the EPS are summed starting at 
offset
+        * 00h, for Entry Point Length bytes.
+        */
+       __u8  entry_point_checksum;
+       /** Length of the Entry Point Structure, starting with the Anchor String
+        * field, in bytes, currently 1Fh.
+        */
+       __u8  entry_point_length;
+       /** identifies the major version of this specification implemented in
+        * the table structures, e.g. the value will be 0Ah for revision 10.22
+        * and 02h for revision 2.1
+        */
+       __u8  major_version;
+       /** identifies the minor version of this specification implemented in
+        * the table structures, e.g. the value will be 16h for revision 10.22
+        * and 01h for revision 2.1
+        */
+       __u8  minor_version;
+       /** size of the largest SMBIOS structure, in bytes, and encompasses the
+        * structure's formatted area and text strings. This is the value 
returned
+        * as StructureSize from the Plug-n-Play 'Get SMBIOS Information' 
function
+        */
+       __u16 max_struct_size;
+       /** identifies the EPS revision implemented in this structure and 
identifies
+        * the formatting of offsets 0Bh to 0Fh, one of:
+        * 00h     Entry Point based on SMBIOS 2.1 definition; formatted area is
+        *         reserved and set to all 00h.
+        * 01h-FFh reserved for assignment via this specification
+        */
+       __u8  revision;
+       /** the value present in the Entry Point Revision field defines the
+        * interpretation to be placed upon these5 bytes.
+        */
+       __u8  formated_area[5];
+       /** "_DMI_", specified as five ASCII characters (5F 44 4D 49 5F) */
+       __u8  intermediate_string[5];
+       /** checksum of the Intermediate Entry Point Structure (IEPS). This 
value,
+        * when added to all other bytes in the IEPS, will result in the value 
00h
+        * (using 8 bit addition calculations). Values in the IEPS are summed
+        * starting at offset 10h, for 0Fh bytes
+        */
+       __u8  intermediate_checksum;
+       /** the 32 bit physical starting address of the read-only SMBIOS 
Structure
+        * Table, that can start at any 32 bit address. This area contains all 
of the
+        * SMBIOS structures fully packed together. These structures can then be
+        * parsed to produce exactly the same format as that returned from a 
'Get
+        * SMBIOS Structure' function call.
+        */
+       __u16 struct_table_length;
+       __u32 struct_table_address;
+       __u16 no_of_structures;
+       __u8  bcd_revision;
+} __attribute__ ((packed));
+
+/** SM-BIOS structure header */
+struct smbios_struct {
+       __u8  type;
+       __u8  length;
+       __u16 handle;
+       __u8  subtype;
+/* ... other fields are structure dependend ... */
+} __attribute__ ((packed));
+
+extern void *smbios_structures_base;                       /* base of SMBIOS 
raw structures */
+extern unsigned char smbios_types_with_subtypes[];
+extern char smbios_version_string[32];                      /* e.g. V2.31 */
+
+/*
+ *   Functions
+ */
+
+/* for the description see the implementation file */
+struct smbios_entry_point_struct *smbios_find_entry_point(void *base);
+struct dmibios_entry_point_struct *dmibios_find_entry_point(void *base);
+unsigned char smbios_check_entry_point(void *addr);
+int smbios_type_has_subtype(unsigned char type);
+int smbios_get_struct_length(struct smbios_struct *struct_ptr);
+int dmibios_get_struct_length(struct smbios_struct *struct_ptr);
+unsigned int smbios_get_readable_name_ext(char *readable_name, struct 
smbios_struct *struct_ptr);
+unsigned int smbios_get_readable_name(char *readable_name, struct 
smbios_struct *struct_ptr);
+int smbios_check_if_have_exar_config(unsigned char *config0, unsigned char 
*config1);
+
+
+#endif /* __BIOS_H__ */
+
+static void *smbios_base;
+struct smbios_entry_point_struct *smbios_entry_point;  /** SM-BIOS entry point 
structure */
+void *smbios_structures_base;
+
+#define CDC_DATA_INTERFACE_TYPE        0x0a
+
+/* constants describing various quirks and errors */
+#define NO_UNION_NORMAL                        1
+#define SINGLE_RX_URB                  2
+#define NO_CAP_LINE                    4
+#define NOT_A_MODEM                    8
+#define NO_DATA_INTERFACE              16
+#define IGNORE_DEVICE                  32
+
+/* USB Requests */
+#define XRUSB_GET_CHIP_ID              0xFF
+#define XRUSB_SET_XR2280X              5
+#define XRUSB_GET_XR2280X              5
+#define XRUSB_SET_XR21B142X            0
+#define XRUSB_GET_XR21B142X            0
+#define XRUSB_SET_XR21V141X            0
+#define XRUSB_GET_XR21V141X            1
+#define XRUSB_SET_XR21B1411            0
+#define XRUSB_GET_XR21B1411            1
+
+/* XR21V141x Baud Rate Generator Registers */
+#define XR21V141X_CLOCK_DIVISOR_0      0x004
+#define XR21V141X_CLOCK_DIVISOR_1      0x005
+#define XR21V141X_CLOCK_DIVISOR_2      0x006
+#define XR21V141X_TX_CLOCK_MASK_0      0x007
+#define XR21V141X_TX_CLOCK_MASK_1      0x008
+#define XR21V141X_RX_CLOCK_MASK_0      0x009
+#define XR21V141X_RX_CLOCK_MASK_1      0x00a
+
+/* XR21V141x UART Block Registers */
+#define XR21V141x_URM_REG_BLOCK                4
+#define XR21V141x_UART_CUSTOM_BLOCK    0x66
+
+/* XR21V141x UART Manager Register values */
+#define XR21V141x_URM_FIFO_ENABLE_REG  0x10
+#define XR21V141x_URM_ENABLE_TX_FIFO   0x1
+#define XR21V141x_URM_ENABLE_RX_FIFO   0x2
+#define XR21V141x_URM_RX_FIFO_RESET    0x18
+#define XR21V141x_URM_TX_FIFO_RESET    0x1C
+
+#define UART_ENABLE_TX                 1
+#define UART_ENABLE_RX                 2
+
+/* GPIOs */
+#define GPIO9_RXT                      0x200
+#define GPIO8_TXT                      0x100
+#define GPIO7_XEN                      0x80
+#define GPIO6_CLK                      0x40
+#define GPIO5_RTS                      0x20
+#define GPIO4_CTS                      0x10
+#define GPIO3_DTR                      0x8
+#define GPIO2_DSR                      0x4
+#define GPIO1_CD                       0x2
+#define GPIO0_RI                       0x1
+
+/* Loopback Register Values */
+
+#define LOOP_ENABLE                    0x7
+#define XR21V141x_LOOP_ENABLE_A                0x40
+#define XR21V141x_LOOP_ENABLE_B                0x41
+#define XR21V141x_LOOP_ENABLE_C                0x42
+#define XR21V141x_LOOP_ENABLE_D                0x43
+
+/* FLOW_MODE Register Values */
+
+#define UART_FLOW_MODE_NONE            0x0
+#define UART_FLOW_MODE_HW              0x1
+#define UART_FLOW_MODE_SW              0x2
+
+/* GPIO_MODE Register Values */
+
+#define UART_GPIO_MODE_SEL_GPIO                0x0
+#define UART_GPIO_MODE_SEL_RTS_CTS     0x1
+#define UART_GPIO_MODE_DIR_RTS_HI      0xb
+#define UART_GPIO_MODE_DIR_RTS_LOW     0x3
+
+/* Additional GPIO_MODE Register Values - XR21B142x */
+
+#define UART_GPIO_MODE_DIR_XEN_HI      0x38
+#define UART_GPIO_MODE_DIR_XEN_LOW     0x30
+#define UART_GPIO_MODE_RXT             0x200
+#define UART_GPIO_MODE_TXT             0x100
+
+/* Randomly located registers */
+#define XR21V141X_WIDE_MODE_REG         3
+
+#define XR21B1411_UART_ENABLE          0xC00
+#define XR21B1411_WIDE_MODE_REG                0xD02
+
+#define XR21B142x_UART_ENABLE          0x00
+#define XR21B142X_TX_WIDE_MODE_REG     0x42
+#define XR21B142X_RX_WIDE_MODE_REG     0x45
+
+#define XR2280X_TX_WIDE_MODE_REG       0x62
+#define XR2280X_RX_WIDE_MODE_REG       0x65
+
+#define XR2280x_FUNC_MGR_OFFSET                0x40
+
+#define XRUSB_IOC_MAGIC                        'v'
+
+#define XRUSB_ENABLE                   _IO(XRUSB_IOC_MAGIC, 3)
+#define XRUSB_SET_BAUD                 _IO(XRUSB_IOC_MAGIC, 4)
+#define XRUSB_LOOPBACK                 _IO(XRUSB_IOC_MAGIC, 5)
+
+#define XRUSB_SET_GPIO_MODE            _IO(XRUSB_IOC_MAGIC, 7)
+#define        XRUSB_GET_GPIO_MODE             _IO(XRUSB_IOC_MAGIC, 8)
+#define        XRUSB_SET_GPIO_STATE            _IO(XRUSB_IOC_MAGIC, 9)
+#define        XRUSB_GET_GPIO_STATE            _IO(XRUSB_IOC_MAGIC, 10)
+#define XRUSB_CLEAR_GPIO_STATE         _IO(XRUSB_IOC_MAGIC, 11)
+#define        XRUSB_SET_GPIO_INT_MASK         _IO(XRUSB_IOC_MAGIC, 12)
+#define        XRUSB_GET_GPIO_INT_MASK         _IO(XRUSB_IOC_MAGIC, 13)
+#define XRUSB_SET_GPIO_PULL_UP         _IO(XRUSB_IOC_MAGIC, 14)
+#define        XRUSB_GET_GPIO_PULL_UP          _IO(XRUSB_IOC_MAGIC, 15)
+#define XRUSB_SET_GPIO_PULL_DOWN       _IO(XRUSB_IOC_MAGIC, 16)
+#define        XRUSB_GET_GPIO_PULL_DOWN        _IO(XRUSB_IOC_MAGIC, 17)
+#define XRUSB_SET_GPIO_OPEN_DRAIN      _IO(XRUSB_IOC_MAGIC, 18)
+#define        XRUSB_GET_GPIO_OPEN_DRAIN       _IO(XRUSB_IOC_MAGIC, 19)
+
+#define XRUSB_WIDE_MODE                        _IO(XRUSB_IOC_MAGIC, 20)
+#define XRUSB_MULTIDROP_MODE           _IO(XRUSB_IOC_MAGIC, 21)
+#define XRUSB_LOW_LATENCY_MODE         _IO(XRUSB_IOC_MAGIC, 22)
+#define XRUSB_SET_RS485_PIN            _IO(XRUSB_IOC_MAGIC, 23)
+#define XRUSB_RS485_PIN_OPTION         _IO(XRUSB_IOC_MAGIC, 25)
+#define XRUSB_RS485_DELAY              _IO(XRUSB_IOC_MAGIC, 26)
+#define XRUSB_ENABLE_RS485_LOW         _IO(XRUSB_IOC_MAGIC, 27)
+
+#define XRUSB_ENABLE_DTRDSR_FLOW       _IO(XRUSB_IOC_MAGIC, 28)
+#define XRUSB_DISABLE_FLOW_CONTROL     _IO(XRUSB_IOC_MAGIC, 29)
+#define XRUSB_GET_FLOW_CONTROL         _IO(XRUSB_IOC_MAGIC, 30)
+#define XRUSB_SET_GPIO_DIR             _IO(XRUSB_IOC_MAGIC, 31)
+#define XRUSB_GET_GPIO_DIR             _IO(XRUSB_IOC_MAGIC, 32)
+#define XRUSB_SET_TXCVR_MODE           _IO(XRUSB_IOC_MAGIC, 33)
+#define XRUSB_GET_TXCVR_MODE           _IO(XRUSB_IOC_MAGIC, 34)
+#define XRUSB_SET_TERM_MODE            _IO(XRUSB_IOC_MAGIC, 35)
+#define XRUSB_SET_SLEW_MODE            _IO(XRUSB_IOC_MAGIC, 36)
+#define XRUSB_SET_GPIO_MAP_TABLE       _IO(XRUSB_IOC_MAGIC, 37)
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to