On Sat, 2015-11-07 at 19:52 +0200, Stefano Rivera wrote: > Hi Ben (2015.11.07_19:33:52_+0200) > > What's blocking this from going into mainline? > > Apparently: > 17:49 <@mithro> tumbleweed: but what happened is that the exar guys effectively forked an in > tree kernel driver and made it work with their crappy device > 17:50 <@mithro> tumbleweed: shenki then fixed it to work with modern kernels > 17:51 <@mithro> tumbleweed: It needs to be rewritten as something that can be merged into the > upstream driver. > 17:51 <@mithro> (The CDC-ACM driver) > 17:52 <@mithro> That is never going to happen as nobody has the time or will power to do it > > Less than ideal for the archive, but useful if you want to muck around with > this hardware. I compared this with the current cdc-acm driver, reverted what I suspect are unnecessary changes, and ended up with this: https://git.decadent.org.uk/gitweb/?p=exar-uart-driver.git Perhaps you could check whether that works (with Linux 4.3)? However, as this device doesn't really seem to follow the CDC-ACM class at all, I suspect that the way to support it upstream is with a custom USB serial driver. I've attached a patch against Linux 4.3 which implements that. This involved a certain amount of guesswork as I have no experience with serial drivers, but I think it's worth trying. Ben. -- Ben Hutchings All the simple programs have been written, and all the good names taken.
From 18baceea7099045f111a79cb2f01b9e5c94c63aa Mon Sep 17 00:00:00 2001 From: Ben Hutchings <ben@decadent.org.uk> Date: Tue, 10 Nov 2015 21:40:43 +0000 Subject: [PATCH] usb-serial: Add vizzini driver for Exar USB 2 serial adapters Signed-off-by: Ben Hutchings <ben@decadent.org.uk> --- drivers/usb/serial/Kconfig | 9 ++ drivers/usb/serial/Makefile | 1 + drivers/usb/serial/vizzini.c | 356 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 drivers/usb/serial/vizzini.c diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 56ecb8b..6911677 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -713,4 +713,13 @@ config USB_SERIAL_DEBUG To compile this driver as a module, choose M here: the module will be called usb-debug. +config USB_SERIAL_VIZZINI + tristate "USB Exar XR21V141x 'Vizzini' Serial Driver" + help + Say Y here if you want to use the Exar XR21V1410/1412/141 + USB 2 serial dapters. + + To compile this driver as a module, choose M here: the + module will be called vizzini. + endif # USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 349d9df..371efa3 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o +obj-$(CONFIG_USB_SERIAL_VIZZINI) += vizzini.o obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o diff --git a/drivers/usb/serial/vizzini.c b/drivers/usb/serial/vizzini.c new file mode 100644 index 0000000..b462cb6 --- /dev/null +++ b/drivers/usb/serial/vizzini.c @@ -0,0 +1,356 @@ +/* + * Based on vizzini driver: + * + * Copyright (c) 2013 Exar Corporation, Inc. + * + * Based on USB Serial "Simple" driver + * + * Copyright (C) 2001-2006,2008,2013 Greg Kroah-Hartman <greg@kroah.com> + * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net) + * Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de> + * Copyright (C) 2009 Outpost Embedded, LLC + * Copyright (C) 2010 Zilogic Systems <code@zilogic.com> + * Copyright (C) 2013 Wei Shuai <cpuwolf@gmail.com> + * Copyright (C) 2013 Linux Foundation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +#define XR_SET_REG 0 + +#define URM_REG_BLOCK 4 +#define EPLOCALS_REG_BLOCK 0x66 + +#define MEM_EP_LOCALS_SIZE_S 3 +#define MEM_EP_LOCALS_SIZE (1 << MEM_EP_LOCALS_SIZE_S) + +#define EP_WIDE_MODE 0x03 + +#define UART_GPIO_MODE 0x01a + +#define UART_GPIO_MODE_SEL_M 0x7 +#define UART_GPIO_MODE_SEL_S 0 +#define UART_GPIO_MODE_SEL 0x007 + +#define UART_GPIO_MODE_SEL_GPIO (0x0 << UART_GPIO_MODE_SEL_S) +#define UART_GPIO_MODE_SEL_RTS_CTS (0x1 << UART_GPIO_MODE_SEL_S) +#define UART_GPIO_MODE_SEL_DTR_DSR (0x2 << UART_GPIO_MODE_SEL_S) + +#define UART_ENABLE 0x003 +#define UART_ENABLE_TX_M 0x1 +#define UART_ENABLE_TX_S 0 +#define UART_ENABLE_TX 0x001 +#define UART_ENABLE_RX_M 0x1 +#define UART_ENABLE_RX_S 1 +#define UART_ENABLE_RX 0x002 + +#define UART_CLOCK_DIVISOR_0 0x004 +#define UART_CLOCK_DIVISOR_1 0x005 +#define UART_CLOCK_DIVISOR_2 0x006 + +#define UART_TX_CLOCK_MASK_0 0x007 +#define UART_TX_CLOCK_MASK_1 0x008 + +#define UART_RX_CLOCK_MASK_0 0x009 +#define UART_RX_CLOCK_MASK_1 0x00a + +#define UART_FORMAT 0x00b + +#define UART_FORMAT_SIZE_M 0xf +#define UART_FORMAT_SIZE_S 0 +#define UART_FORMAT_SIZE 0x00f + +#define UART_FORMAT_SIZE_7 (0x7 << UART_FORMAT_SIZE_S) +#define UART_FORMAT_SIZE_8 (0x8 << UART_FORMAT_SIZE_S) +#define UART_FORMAT_SIZE_9 (0x9 << UART_FORMAT_SIZE_S) + +#define UART_FORMAT_PARITY_M 0x7 +#define UART_FORMAT_PARITY_S 4 +#define UART_FORMAT_PARITY 0x070 + +#define UART_FORMAT_PARITY_NONE (0x0 << UART_FORMAT_PARITY_S) +#define UART_FORMAT_PARITY_ODD (0x1 << UART_FORMAT_PARITY_S) +#define UART_FORMAT_PARITY_EVEN (0x2 << UART_FORMAT_PARITY_S) +#define UART_FORMAT_PARITY_1 (0x3 << UART_FORMAT_PARITY_S) +#define UART_FORMAT_PARITY_0 (0x4 << UART_FORMAT_PARITY_S) + +#define UART_FORMAT_STOP_M 0x1 +#define UART_FORMAT_STOP_S 7 +#define UART_FORMAT_STOP 0x080 + +#define UART_FORMAT_STOP_1 (0x0 << UART_FORMAT_STOP_S) +#define UART_FORMAT_STOP_2 (0x1 << UART_FORMAT_STOP_S) + +#define UART_FLOW 0x00c + +#define UART_FLOW_MODE_M 0x7 +#define UART_FLOW_MODE_S 0 +#define UART_FLOW_MODE 0x007 + +#define UART_FLOW_MODE_NONE (0x0 << UART_FLOW_MODE_S) +#define UART_FLOW_MODE_HW (0x1 << UART_FLOW_MODE_S) +#define UART_FLOW_MODE_SW (0x2 << UART_FLOW_MODE_S) + +#define UART_XON_CHAR 0x010 +#define UART_XOFF_CHAR 0x011 + +#define URM_ENABLE_BASE 0x010 +#define URM_ENABLE_0 0x010 +#define URM_ENABLE_0_TX 0x001 +#define URM_ENABLE_0_RX 0x002 + +struct vizzini_baud_rate +{ + unsigned int tx; + unsigned int rx0; + unsigned int rx1; +}; + +static struct vizzini_baud_rate vizzini_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 vizzini_set_reg(struct usb_serial_port *port, + unsigned int block, unsigned int regnum, + unsigned int value) +{ + dev_dbg(&port->serial->dev->dev, "%s 0x%02x:0x%02x = 0x%02x\n", + __func__, block, regnum, value); + + return usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + XR_SET_REG, + USB_DIR_OUT | USB_TYPE_VENDOR, + value, regnum | (block << 8), + NULL, 0, + 5000); +} + +static void vizzini_disable(struct usb_serial_port *port) +{ + unsigned int block = port->bulk_out_endpointAddress - 1; + + vizzini_set_reg(port, block, UART_ENABLE, 0); + vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block, 0); +} + +static void vizzini_enable(struct usb_serial_port *port) +{ + unsigned int block = port->bulk_out_endpointAddress - 1; + + vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block, + URM_ENABLE_0_TX); + vizzini_set_reg(port, block, UART_ENABLE, + UART_ENABLE_TX | UART_ENABLE_RX); + vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block, + URM_ENABLE_0_TX | URM_ENABLE_0_RX); +} + +static void vizzini_set_baud_rate(struct usb_serial_port *port, + unsigned int rate) +{ + int block = port->bulk_out_endpointAddress - 1; + unsigned int divisor = 48000000 / rate; + unsigned int i = ((32 * 48000000) / rate) & 0x1f; + unsigned int tx_mask = vizzini_baud_rates[i].tx; + unsigned int rx_mask = ((divisor & 1) ? vizzini_baud_rates[i].rx1 : + vizzini_baud_rates[i].rx0); + + dev_dbg(&port->serial->dev->dev, + "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n", + rate, i, divisor, tx_mask, rx_mask); + + vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_0, + (divisor >> 0) & 0xff); + vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_1, + (divisor >> 8) & 0xff); + vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_2, + (divisor >> 16) & 0xff); + + vizzini_set_reg(port, block, UART_TX_CLOCK_MASK_0, + (tx_mask >> 0) & 0xff); + vizzini_set_reg(port, block, UART_TX_CLOCK_MASK_1, + (tx_mask >> 8) & 0xff); + + vizzini_set_reg(port, block, UART_RX_CLOCK_MASK_0, + (rx_mask >> 0) & 0xff); + vizzini_set_reg(port, block, UART_RX_CLOCK_MASK_1, + (rx_mask >> 8) & 0xff); +} + +static void vizzini_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *termios_old) +{ + unsigned int cflag, block; + speed_t rate; + unsigned int format_size, format_parity, format_stop, flow, gpio_mode; + + cflag = tty->termios.c_cflag; + + block = port->bulk_out_endpointAddress - 1; + + vizzini_disable(port); + + if ((cflag & CSIZE) == CS7) + format_size = UART_FORMAT_SIZE_7; + else if ((cflag & CSIZE) == CS5) + /* Enabling 5-bit mode is really 9-bit mode! */ + format_size = UART_FORMAT_SIZE_9; + else + format_size = UART_FORMAT_SIZE_8; + + if (cflag & PARENB) { + if (cflag & PARODD) { + if (cflag & CMSPAR) + format_parity = UART_FORMAT_PARITY_1; + else + format_parity = UART_FORMAT_PARITY_ODD; + } else { + if (cflag & CMSPAR) + format_parity = UART_FORMAT_PARITY_0; + else + format_parity = UART_FORMAT_PARITY_EVEN; + } + } else { + format_parity = UART_FORMAT_PARITY_NONE; + } + + if (cflag & CSTOPB) + format_stop = UART_FORMAT_STOP_2; + else + format_stop = UART_FORMAT_STOP_1; + + vizzini_set_reg(port, block, UART_FORMAT, + format_size | format_parity | format_stop); + + 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; + + vizzini_set_reg(port, block, UART_XON_CHAR, start_char); + vizzini_set_reg(port, block, UART_XOFF_CHAR, stop_char); + } else { + flow = UART_FLOW_MODE_NONE; + gpio_mode = UART_GPIO_MODE_SEL_GPIO; + } + + vizzini_set_reg(port, block, UART_FLOW, flow); + vizzini_set_reg(port, block, UART_GPIO_MODE, gpio_mode); + + vizzini_set_reg(port, EPLOCALS_REG_BLOCK, + (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, + format_size == UART_FORMAT_SIZE_9); + + rate = tty_get_baud_rate(tty); + if (rate) + vizzini_set_baud_rate(port, rate); + + vizzini_enable(port); +} + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x04e2, 0x1410), }, + { USB_DEVICE(0x04e2, 0x1412), }, + { USB_DEVICE(0x04e2, 0x1414), }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static const struct usb_device_id vizzini_1410_id_table[] = { + { USB_DEVICE(0x04e2, 0x1410), }, + { }, +}; +static struct usb_serial_driver vizzini_1410_device = { + .driver = { + .owner = THIS_MODULE, + .name = "vizzini_1410", + }, + .id_table = vizzini_1410_id_table, + .num_ports = 1, + .set_termios = vizzini_set_termios, +}; + +static const struct usb_device_id vizzini_1412_id_table[] = { + { USB_DEVICE(0x04e2, 0x1412), }, + { }, +}; +static struct usb_serial_driver vizzini_1412_device = { + .driver = { + .owner = THIS_MODULE, + .name = "vizzini_1412", + }, + .id_table = vizzini_1412_id_table, + .num_ports = 2, + .set_termios = vizzini_set_termios, +}; + +static const struct usb_device_id vizzini_1414_id_table[] = { + { USB_DEVICE(0x04e2, 0x1414), }, + { }, +}; +static struct usb_serial_driver vizzini_1414_device = { + .driver = { + .owner = THIS_MODULE, + .name = "vizzini_1414", + }, + .id_table = vizzini_1414_id_table, + .num_ports = 4, + .set_termios = vizzini_set_termios, +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &vizzini_1410_device, + &vizzini_1412_device, + &vizzini_1414_device, + NULL +}; + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_LICENSE("GPL");
Attachment:
signature.asc
Description: This is a digitally signed message part