Re: [PATCH] Driver for MaxLinear/Exar USB (UART) Serial Adapters

2018-08-16 Thread Patong Yang
On Thu, Aug 16, 2018 at 08:34:47AM +0200, Greg KH wrote:
> On Wed, Aug 15, 2018 at 10:56:47PM -0700, Patong Yang wrote:
> > Greg,
> > 
> > Please see my response inline below.
> > 
> > Patong
> > 
> > > But there is a bigger problem here:
> > > 
> > > > +   xrusb_tty_driver = alloc_tty_driver(XRUSB_TTY_MINORS);
> > > > +   if (!xrusb_tty_driver)
> > > > +   return -ENOMEM;
> > > 
> > > Why are you not using the usb serial core here?  You need to do that,
> > > not try to provide your own custom tty driver.  That way userspace
> > > programs will "just work" with your new device, no changes needed as
> > > your major/minor number and device name would be custom only for your
> > > device, which is not acceptable.
> > 
> > The MaxLinear/USB serial devices support the CDC-ACM commands.
> > Therefore, we used the cdc-acm driver instead of the usb serial driver
> > as the starting point for developing the driver.  We replaced "ACM" 
> > with "XRUSB" throughout the driver.  Would it be better if we just used 
> > the same major/minor number as the CDC-ACM driver since it was based on
> > the cdc-acm driver?  
> 
> No, just use the cdc-acm driver itself and add your product/device id to
> it and it should work just fine.  Why do you need to write a whole new
> driver at all?

The basic TX/RX functionality works fine now with the cdc-acm driver.
That's the driver that is loaded because it's advertised as a cdc-acm
compatible device in the device descriptors.

However, the cdc-acm driver (and spec) does not have support all of the 
features in the MaxLinear/Exar USB UARTs.  Hence the reason for a separate
and new driver.  

Features not supported by the cdc-acm driver are 
enabling/disabling flow control, enabling/disabling RS-485 mode, 
GPIOs and GPIO modes, etc. Support for these features had to be added in this
new driver.  

Please let me know if there are any futher questions and advise on how
to proceed.

Thanks,
Patong


[PATCH] Driver for MaxLinear/Exar USB (UART) Serial Adapters

2018-07-24 Thread Patong Yang
The original driver/patch was submitted on April 4, 2018.  This is the
second version based on the feedback received on the original patch.

v2: Removed custom IOCTLs, as suggested by Greg KH
Using standard Linux GPIO APIs, as suggested by Greg KH
Removed file reads/writes as suggested by Greg KH

Signed-off-by: Patong Yang 
---
 drivers/usb/serial/xrusb_serial.c | 2380 +
 drivers/usb/serial/xrusb_serial.h |  234 +++
 2 files changed, 2614 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 ..707c470d32db
--- /dev/null
+++ b/drivers/usb/serial/xrusb_serial.c
@@ -0,0 +1,2380 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * xrusb_serial.c
+ *
+ * Copyright (c) 2018 Patong Yang 
+ *
+ * USB Serial Driver based on the cdc-acm.c driver for the
+ * MaxLinear/Exar USB UARTs/Serial adapters
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "linux/version.h"
+#include "xrusb_serial.h"
+
+#define DRIVER_AUTHOR "Patong Yang "
+#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 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);
+
+/*
+ * 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(_table_lock);
+   xrusb = xrusb_table[index];
+   if (xrusb) {
+   mutex_lock(>mutex);
+   if (xrusb->disconnected) {
+   mutex_unlock(>mutex);
+   xrusb = NULL;
+   } else {
+   tty_port_get(>port);
+   mutex_unlock(>mutex);
+   }
+   }
+   mutex_unlock(_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(_table_lock);
+   for (minor = 0; minor < XRUSB_TTY_MINORS; minor++) {
+   if (!xrusb_table[minor]) {
+   xrusb_table[minor] = xrusb;
+   break;
+   }
+   }
+   mutex_unlock(_table_lock);
+
+   return minor;
+}
+
+/* Release the minor number associated with 'xrusb'.  */
+static void xrusb_release_minor(struct xrusb *xrusb)
+{
+   mutex_lock(_table_lock);
+   xrusb_table[xrusb->minor] = NULL;
+   mutex_unlock(_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);
+   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

[PATCH] Driver for MaxLinear/Exar USB (UART) Serial adapters.

2018-04-04 Thread Patong Yang
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 ..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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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 poin