From 6d2bca3cda09fca9c0a2a2c84dbf64c6090b3da5 Mon Sep 17 00:00:00 2001
From: Johan Hovold <johan@kernel.org>
Date: Mon, 21 Nov 2016 16:28:22 +0100
Subject: [PATCH] tmp: ch341: replace with vendor driver
---
drivers/usb/serial/ch341.c | 1238 ++++++++++++++++++++++++++++----------------
1 file changed, 803 insertions(+), 435 deletions(-)
@@ -1,11 +1,15 @@
/*
+ * Winchiphead CH34x USB to serial adaptor driver
+ *
* Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
* Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
* Copyright 2009, Boris Hajduk <boris@hajduk.org>
+ * Copyright 2016, WCH Tech Group <tech@winchiphead.com>
*
- * ch341.c implements a serial port driver for the Winchiphead CH341.
+ * ch34x.c implements a serial port driver for the Winchiphead CH340 and
+ * CH341.
*
- * The CH341 device can be used to implement an RS232 asynchronous
+ * The CH34x device can be used to implement an RS232 asynchronous
* serial port, an IEEE-1284 parallel printer port or a memory-like
* interface. In all cases the CH341 supports an I2C interface as well.
* This driver only supports the asynchronous serial interface.
@@ -16,134 +20,270 @@
*/
#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
#include <linux/module.h>
-#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
-#include <linux/serial.h>
-#include <asm/unaligned.h>
-
-#define DEFAULT_BAUD_RATE 9600
-#define DEFAULT_TIMEOUT 1000
-
-/* flags for IO-Bits */
-#define CH341_BIT_RTS (1 << 6)
-#define CH341_BIT_DTR (1 << 5)
-
-/******************************/
-/* interrupt pipe definitions */
-/******************************/
-/* always 4 interrupt bytes */
-/* first irq byte normally 0x08 */
-/* second irq byte base 0x7d + below */
-/* third irq byte base 0x94 + below */
-/* fourth irq byte normally 0xee */
-
-/* second interrupt byte */
-#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
-
-/* status returned in third interrupt answer byte, inverted in data
- from irq */
-#define CH341_BIT_CTS 0x01
-#define CH341_BIT_DSR 0x02
-#define CH341_BIT_RI 0x04
-#define CH341_BIT_DCD 0x08
-#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
-
-/*******************************/
-/* baudrate calculation factor */
-/*******************************/
-#define CH341_BAUDBASE_FACTOR 1532620800
-#define CH341_BAUDBASE_DIVMAX 3
-
-/* Break support - the information used to implement this was gleaned from
- * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
- */
-#define CH341_REQ_READ_VERSION 0x5F
-#define CH341_REQ_WRITE_REG 0x9A
-#define CH341_REQ_READ_REG 0x95
-#define CH341_REQ_SERIAL_INIT 0xA1
-#define CH341_REQ_MODEM_CTRL 0xA4
-
-#define CH341_REG_BREAK 0x05
-#define CH341_REG_LCR 0x18
-#define CH341_NBREAK_BITS 0x01
-
-#define CH341_LCR_ENABLE_RX 0x80
-#define CH341_LCR_ENABLE_TX 0x40
-#define CH341_LCR_MARK_SPACE 0x20
-#define CH341_LCR_PAR_EVEN 0x10
-#define CH341_LCR_ENABLE_PAR 0x08
-#define CH341_LCR_STOP_BITS_2 0x04
-#define CH341_LCR_CS8 0x03
-#define CH341_LCR_CS7 0x02
-#define CH341_LCR_CS6 0x01
-#define CH341_LCR_CS5 0x00
-
-static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0x4348, 0x5523) },
- { USB_DEVICE(0x1a86, 0x7523) },
- { USB_DEVICE(0x1a86, 0x5523) },
- { },
+#define DRIVER_DESC "WCH CH34x USB to serial adaptor driver"
+#define DRIVER_AUTHOR "WCH Tech Group <tech@winchiphead.com>"
+
+#define CH34x_VENDOR_ID 0x1A86
+#define CH340_PRODUCT_ID 0x7523
+#define CH341_PRODUCT_ID 0x5523
+
+#define CH34x_CLOSING_WAIT (30 * HZ)
+
+#define CH34x_BUF_SIZE 1024
+#define CH34x_TMP_BUF_SIZE 1024
+
+#define VENDOR_WRITE_TYPE 0x40
+#define VENDOR_READ_TYPE 0xC0
+
+#define VENDOR_READ 0x95
+#define VENDOR_WRITE 0x9A
+#define VENDOR_SERIAL_INIT 0xA1
+#define VENDOR_MODEM_OUT 0xA4
+#define VENDOR_VERSION 0x5F
+
+/* for command 0xA4 */
+#define UART_CTS 0x01
+#define UART_DSR 0x02
+#define UART_RING 0x04
+#define UART_DCD 0x08
+#define CONTROL_OUT 0x10
+#define CONTROL_DTR 0x20
+#define CONTROL_RTS 0x40
+
+/* uart state */
+#define UART_STATE 0x00
+#define UART_OVERRUN_ERROR 0x01
+#define UART_BREAK_ERROR
+#define UART_PARITY_ERROR 0x02
+#define UART_FRAME_ERROR 0x06
+#define UART_RECV_ERROR 0x02
+#define UART_STATE_TRANSIENT_MASK 0x07
+
+/* port state */
+#define PORTA_STATE 0x01
+#define PORTB_STATE 0x02
+#define PORTC_STATE 0x03
+
+/* Baud Rate */
+#define CH34x_BAUDRATE_FACTOR 1532620800
+#define CH34x_BAUDRATE_DIVMAX 3
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+static int wait_flag;
+
+struct ch34x_buf {
+ unsigned int buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
+struct ch34x_private {
+ spinlock_t lock; /* access lock */
+ struct ch34x_buf *buf;
+ int write_urb_in_use;
+ unsigned int baud_rate;
+ wait_queue_head_t delta_msr_wait;
+ u8 line_control; /* set line control value RTS/DTR */
+ u8 line_status; /* active status of modem control inputs */
+ u8 termios_initialized;
};
-MODULE_DEVICE_TABLE(usb, id_table);
-struct ch341_private {
- spinlock_t lock; /* access lock */
- unsigned baud_rate; /* set baud rate */
- u8 line_control; /* set line control value RTS/DTR */
- u8 line_status; /* active status of modem control inputs */
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(CH34x_VENDOR_ID, CH340_PRODUCT_ID) },
+ { USB_DEVICE(CH34x_VENDOR_ID, CH341_PRODUCT_ID) },
+ { }
};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct ch34x_buf *ch34x_buf_alloc(unsigned int size)
+{
+ struct ch34x_buf *pb;
+
+ if (size == 0)
+ return NULL;
+
+ pb = kmalloc(sizeof(struct ch34x_buf), GFP_KERNEL);
+ if (pb == NULL)
+ return NULL;
+
+ pb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (pb->buf_buf == NULL) {
+ kfree(pb);
+ return NULL;
+ }
+
+ pb->buf_size = size;
+ pb->buf_get = pb->buf_put = pb->buf_buf;
+
+ return pb;
+}
+
+static void ch34x_buf_free(struct ch34x_buf *pb)
+{
+ if (pb) {
+ kfree(pb->buf_buf);
+ kfree(pb);
+ }
+}
+
+static void ch34x_buf_clear(struct ch34x_buf *pb)
+{
+ if (pb != NULL)
+ pb->buf_get = pb->buf_put;
+}
+
+static unsigned int ch34x_buf_data_avail(struct ch34x_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+}
+
+static unsigned int ch34x_buf_space_avail(struct ch34x_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_get - pb->buf_put - 1)
+ % pb->buf_size);
+}
+
+static unsigned int ch34x_buf_put(struct ch34x_buf *pb,
+ const char *buf, unsigned int count)
+{
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = ch34x_buf_space_avail(pb);
+ if (count > len)
+ count = len;
+ else if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_put;
+ if (count > len) {
+ memcpy(pb->buf_put, buf, len);
+ memcpy(pb->buf_buf, buf+len, count - len);
+ pb->buf_put = pb->buf_buf + count - len;
+ } else {
+ memcpy(pb->buf_put, buf, count);
+ if (count < len)
+ pb->buf_put += count;
+ else if (count == len)
+ pb->buf_put = pb->buf_buf;
+ }
-static void ch341_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port,
- struct ktermios *old_termios);
+ return count;
+}
-static int ch341_control_out(struct usb_device *dev, u8 request,
- u16 value, u16 index)
+static unsigned int ch34x_buf_get(struct ch34x_buf *pb,
+ char *buf, unsigned int count)
{
- int r;
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = ch34x_buf_data_avail(pb);
+ if (count > len)
+ count = len;
+ else if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_get;
+ if (count > len) {
+ memcpy(buf, pb->buf_get, len);
+ memcpy(buf + len, pb->buf_buf, count - len);
+ pb->buf_get = pb->buf_buf + count - len;
+ } else {
+ memcpy(buf, pb->buf_get, count);
+ if (count < len)
+ pb->buf_get += count;
+ else if (count == len)
+ pb->buf_get = pb->buf_buf;
+ }
+
+ return count;
+}
- dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
- USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
+static int ch34x_vendor_read(__u8 request,
+ __u16 value,
+ __u16 index,
+ struct usb_serial *serial,
+ unsigned char *buf,
+ __u16 len)
+{
+ int retval;
- r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
- value, index, NULL, 0, DEFAULT_TIMEOUT);
+ retval = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ request,
+ VENDOR_READ_TYPE,
+ value, index, buf, len, 1000);
- return r;
+ return retval;
}
-static int ch341_control_in(struct usb_device *dev,
- u8 request, u16 value, u16 index,
- char *buf, unsigned bufsize)
+static int ch34x_vendor_write(__u8 request,
+ __u16 value,
+ __u16 index,
+ struct usb_serial *serial,
+ unsigned char *buf,
+ __u16 len)
{
- int r;
+ int retval;
- dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
- USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
- (int)bufsize);
+ retval = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ request,
+ VENDOR_WRITE_TYPE,
+ value, index, buf, len, 1000);
- r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- value, index, buf, bufsize, DEFAULT_TIMEOUT);
- return r;
+ return retval;
}
-static int ch341_init_set_baudrate(struct usb_device *dev,
- struct ch341_private *priv, unsigned ctrl)
+static int set_control_lines(struct usb_serial_port *port,
+ struct usb_serial *serial, u8 value)
{
- short a;
- int r;
- unsigned long factor;
- short divisor;
+ int retval;
+
+ retval = ch34x_vendor_write(VENDOR_MODEM_OUT, (unsigned short)value,
+ 0x0000, serial, NULL, 0x00);
+ dev_dbg(&port->dev, "%s - value=%d, retval=%d",
+ __func__, value, retval);
+
+ return retval;
+}
- if (!priv->baud_rate)
+static int ch34x_get_baud_rate(unsigned int baud_rate,
+ unsigned char *a, unsigned char *b)
+{
+ unsigned long factor = 0;
+ short divisor = 0;
+
+ if (!baud_rate)
return -EINVAL;
- factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
- divisor = CH341_BAUDBASE_DIVMAX;
+
+ factor = CH34x_BAUDRATE_FACTOR / baud_rate;
+ divisor = CH34x_BAUDRATE_DIVMAX;
while ((factor > 0xfff0) && divisor) {
factor >>= 3;
@@ -154,464 +294,692 @@ static int ch341_init_set_baudrate(struct usb_device *dev,
return -EINVAL;
factor = 0x10000 - factor;
- a = (factor & 0xff00) | divisor;
-
- /* 0x9c is "enable SFR_UART Control register and timer" */
- r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
- 0x9c | (ctrl << 8), a | 0x80);
+ *a = (factor & 0xff00) >> 8;
+ *b = divisor;
- return r;
+ return 0;
}
-static int ch341_set_handshake(struct usb_device *dev, u8 control)
+static void ch34x_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
{
- return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
-}
+ struct usb_serial *serial = port->serial;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ struct ktermios *termios = &tty->termios;
-static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
-{
- char *buffer;
- int r;
- const unsigned size = 8;
+ unsigned int baud_rate;
+ unsigned int cflag;
unsigned long flags;
+ u8 control;
- buffer = kmalloc(size, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
+ unsigned char divisor = 0;
+ unsigned char reg_count = 0;
+ unsigned char factor = 0;
+ unsigned char reg_value = 0;
+ unsigned short value = 0;
+ unsigned short index = 0;
- r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
- if (r < 0)
- goto out;
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
- /* setup the private status if available */
- if (r == 2) {
- r = 0;
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- r = -EPROTO;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!priv->termios_initialized) {
+ *termios = tty_std_termios;
+ termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ termios->c_ispeed = 9600;
+ termios->c_ospeed = 9600;
+ priv->termios_initialized = 1;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
-out: kfree(buffer);
- return r;
-}
+ /*
+ * The ch34x is reported to lose bytes if you change serial setting
+ * even to the same vaules as before. Thus we actually need to filter
+ * in this specific case.
+ */
+ if (!tty_termios_hw_change(termios, old_termios))
+ return;
-/* -------------------------------------------------------------------------- */
+ cflag = termios->c_cflag;
-static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
-{
- char *buffer;
- int r;
- const unsigned size = 8;
-
- buffer = kmalloc(size, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- /* expect two bytes 0x27 0x00 */
- r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
- if (r < 0)
- goto out;
- dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
-
- r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
- if (r < 0)
- goto out;
-
- /* expect two bytes 0x56 0x00 */
- r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
- if (r < 0)
- goto out;
-
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050);
- if (r < 0)
- goto out;
-
- /* expect 0xff 0xee */
- r = ch341_get_status(dev, priv);
- if (r < 0)
- goto out;
-
- r = ch341_init_set_baudrate(dev, priv, 0);
- if (r < 0)
- goto out;
-
- r = ch341_set_handshake(dev, priv->line_control);
- if (r < 0)
- goto out;
-
- /* expect 0x9f 0xee */
- r = ch341_get_status(dev, priv);
-
-out: kfree(buffer);
- return r;
-}
+ dev_dbg(&port->dev, "%s(%d) cflag = 0x%x\n",
+ __func__, port->port_number, cflag);
-static int ch341_port_probe(struct usb_serial_port *port)
-{
- struct ch341_private *priv;
- int r;
+ /* Get the byte size */
+ switch (cflag & CSIZE) {
+ case CS5:
+ reg_value |= 0x00;
+ break;
+ case CS6:
+ reg_value |= 0x01;
+ break;
+ case CS7:
+ reg_value |= 0x02;
+ break;
+ case CS8:
+ reg_value |= 0x03;
+ break;
+ default:
+ reg_value |= 0x03;
+ break;
+ }
+ dev_dbg(&port->dev, "%s - data bits = %d", __func__, reg_value + 0x05);
- priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (cflag & CSTOPB) {
+ reg_value |= 0x04;
+ dev_dbg(&port->dev, "%s - stop bits = 2", __func__);
+ } else
+ dev_dbg(&port->dev, "%s - stop bits = 1", __func__);
+
+ if (cflag & PARENB) {
+ if (cflag & PARODD) {
+ reg_value |= 0x08 | 0x00;
+ dev_dbg(&port->dev, "%s - parity = odd", __func__);
+ } else {
+ reg_value |= 0x08 | 0x10;
+ dev_dbg(&port->dev, "%s - parity = even", __func__);
+ }
+ } else
+ dev_dbg(&port->dev, "%s - parity = none", __func__);
- spin_lock_init(&priv->lock);
- priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+ baud_rate = tty_get_baud_rate(tty);
+ dev_dbg(&port->dev, "%s = baud_rate = %d", __func__, baud_rate);
+ ch34x_get_baud_rate(baud_rate, &factor, &divisor);
+ dev_dbg(&port->dev, "----->>>> baud_rate = %d, factor:0x%x, divisor:0x%x",
+ baud_rate, factor, divisor);
+
+ /* enable SFR_UART RX and TX */
+ reg_value |= 0xc0;
+ /* enable SFR_UART Control register and timer */
+ reg_count |= 0x9c;
+
+ value |= reg_count;
+ value |= (unsigned short)reg_value << 8;
+ index |= 0x80 | divisor;
+ index |= (unsigned short)factor << 8;
+ ch34x_vendor_write(VENDOR_SERIAL_INIT, value, index, serial, NULL, 0);
+
+ /* change control lines if we are switching to or from B0 */
+ spin_lock_irqsave(&priv->lock, flags);
+ control = priv->line_control;
+ if ((cflag & CBAUD) == B0)
+ priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+ else
+ priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
- r = ch341_configure(port->serial->dev, priv);
- if (r < 0)
- goto error;
+ if (control != priv->line_control) {
+ control = priv->line_control;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_control_lines(port, serial, control);
+ } else
+ spin_unlock_irqrestore(&priv->lock, flags);
- usb_set_serial_port_data(port, priv);
- return 0;
+ if (cflag & CRTSCTS)
+ ch34x_vendor_write(VENDOR_WRITE, 0x2727, 0x0101,
+ serial, NULL, 0);
-error: kfree(priv);
- return r;
+ /* FIXME: Need to read back resulting baud rate */
+ if (baud_rate)
+ tty_encode_baud_rate(tty, baud_rate, baud_rate);
}
-static int ch341_port_remove(struct usb_serial_port *port)
+static int ch34x_tiocmget(struct tty_struct *tty)
{
- struct ch341_private *priv;
+ struct usb_serial_port *port = tty->driver_data;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int mcr;
+ unsigned int retval;
- priv = usb_get_serial_port_data(port);
- kfree(priv);
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
+ if (!usb_get_intfdata(port->serial->interface))
+ return -ENODEV;
- return 0;
-}
+ spin_lock_irqsave(&priv->lock, flags);
+ mcr = priv->line_control;
+ spin_unlock_irqrestore(&priv->lock, flags);
-static int ch341_carrier_raised(struct usb_serial_port *port)
-{
- struct ch341_private *priv = usb_get_serial_port_data(port);
- if (priv->line_status & CH341_BIT_DCD)
- return 1;
- return 0;
+ retval = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) |
+ ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) |
+ ((mcr & UART_CTS) ? TIOCM_CTS : 0) |
+ ((mcr & UART_DSR) ? TIOCM_DSR : 0) |
+ ((mcr & UART_RING) ? TIOCM_RI : 0) |
+ ((mcr & UART_DCD) ? TIOCM_CD : 0);
+
+ dev_dbg(&port->dev, "%s-retval = 0x%x", __func__, retval);
+
+ return retval;
}
-static void ch341_dtr_rts(struct usb_serial_port *port, int on)
+static void ch34x_close(struct usb_serial_port *port)
{
- struct ch341_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty = port->port.tty;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
+ unsigned int c_cflag;
- /* drop DTR and RTS */
+ /* clear out any remaining data in the buffer */
spin_lock_irqsave(&priv->lock, flags);
- if (on)
- priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
- else
- priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
+ ch34x_buf_clear(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
- ch341_set_handshake(port->serial->dev, priv->line_control);
-}
-static void ch341_close(struct usb_serial_port *port)
-{
- usb_serial_generic_close(port);
+ /* kill our urbs */
usb_kill_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->write_urb);
+
+ if (tty) {
+ c_cflag = tty->termios.c_cflag;
+ if (c_cflag & HUPCL) {
+ /* drop DTR and RTS */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_control = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_control_lines(port, port->serial, 0);
+ }
+ }
}
-
-/* open this device, set default parameters */
-static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
+static int ch34x_open(struct tty_struct *tty,
+ struct usb_serial_port *port)
{
+ struct ktermios tmp_termios;
struct usb_serial *serial = port->serial;
- struct ch341_private *priv = usb_get_serial_port_data(port);
- int r;
+ int retval;
+
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
- r = ch341_configure(serial->dev, priv);
- if (r)
- goto out;
+ usb_clear_halt(serial->dev, port->write_urb->pipe);
+ usb_clear_halt(serial->dev, port->read_urb->pipe);
if (tty)
- ch341_set_termios(tty, port, NULL);
-
- dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
- r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
- if (r) {
- dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
- __func__, r);
- goto out;
+ ch34x_set_termios(tty, port, &tmp_termios);
+
+ dev_dbg(&port->dev, "%s - submit read urb", __func__);
+ port->read_urb->dev = serial->dev;
+ retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&port->dev, "%s - failed submit read urb,error %d\n",
+ __func__, retval);
+ ch34x_close(port);
+ goto err_out;
}
- r = usb_serial_generic_open(tty, port);
+ dev_dbg(&port->dev, "%s - submit interrupt urb", __func__);
+ port->interrupt_in_urb->dev = serial->dev;
+ retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&port->dev, "%s - failed submit interrupt urb,error %d\n",
+ __func__, retval);
+ ch34x_close(port);
+ goto err_out;
+ }
-out: return r;
+err_out:
+ return retval;
}
-/* Old_termios contains the original termios settings and
- * tty->termios contains the new setting to be used.
- */
-static void ch341_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port, struct ktermios *old_termios)
+static int ch34x_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
{
- struct ch341_private *priv = usb_get_serial_port_data(port);
- unsigned baud_rate;
+ struct usb_serial_port *port = tty->driver_data;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- unsigned char ctrl;
- int r;
+ u8 control;
- /* redundant changes may cause the chip to lose bytes */
- if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
- return;
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
- baud_rate = tty_get_baud_rate(tty);
+ if (!usb_get_intfdata(port->serial->interface))
+ return -ENODEV;
- priv->baud_rate = baud_rate;
- ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (set & TIOCM_RTS)
+ priv->line_control |= CONTROL_RTS;
+ if (set & TIOCM_DTR)
+ priv->line_control |= CONTROL_DTR;
+ if (clear & TIOCM_RTS)
+ priv->line_control &= ~CONTROL_RTS;
+ if (clear & TIOCM_DTR)
+ priv->line_control &= ~CONTROL_DTR;
+ control = priv->line_control;
+ spin_unlock_irqrestore(&priv->lock, flags);
- switch (C_CSIZE(tty)) {
- case CS5:
- ctrl |= CH341_LCR_CS5;
- break;
- case CS6:
- ctrl |= CH341_LCR_CS6;
- break;
- case CS7:
- ctrl |= CH341_LCR_CS7;
- break;
- case CS8:
- ctrl |= CH341_LCR_CS8;
- break;
- }
+ return set_control_lines(port, port->serial, control);
+}
- if (C_PARENB(tty)) {
- ctrl |= CH341_LCR_ENABLE_PAR;
- if (C_PARODD(tty) == 0)
- ctrl |= CH341_LCR_PAR_EVEN;
- if (C_CMSPAR(tty))
- ctrl |= CH341_LCR_MARK_SPACE;
- }
+static int wait_modem_info(struct usb_serial_port *port,
+ unsigned int arg)
+{
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int prevstatus;
+ unsigned int status;
+ unsigned int changed;
- if (C_CSTOPB(tty))
- ctrl |= CH341_LCR_STOP_BITS_2;
+ dev_dbg(&port->dev, "%s -port:%d", __func__, port->port_number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ prevstatus = priv->line_status;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ while (1) {
+ wait_event_interruptible(wq, wait_flag != 0);
+ wait_flag = 0;
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
- if (baud_rate) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
- r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
- if (r < 0 && old_termios) {
- priv->baud_rate = tty_termios_baud_rate(old_termios);
- tty_termios_copy_hw(&tty->termios, old_termios);
- }
- } else {
spin_lock_irqsave(&priv->lock, flags);
- priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+ status = priv->line_status;
spin_unlock_irqrestore(&priv->lock, flags);
- }
- ch341_set_handshake(port->serial->dev, priv->line_control);
+ changed = prevstatus ^ status;
+
+ if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+ ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+ ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
+ ((arg & TIOCM_CTS) && (changed & UART_CTS)))
+ return 0;
+ prevstatus = status;
+ }
}
-static void ch341_break_ctl(struct tty_struct *tty, int break_state)
+static int ch34x_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
{
- const uint16_t ch341_break_reg =
- ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
- int r;
- uint16_t reg_contents;
- uint8_t *break_reg;
- break_reg = kmalloc(2, GFP_KERNEL);
- if (!break_reg)
+ dev_dbg(&port->dev, "%s - port:%d, cmd=0x%04x",
+ __func__, port->port_number, cmd);
+
+ switch (cmd) {
+ case TIOCMIWAIT:
+ dev_dbg(&port->dev, "%s - port:%d TIOCMIWAIT",
+ __func__, port->port_number);
+ return wait_modem_info(port, arg);
+ default:
+ dev_dbg(&port->dev, "%s not supported=0x%04x", __func__, cmd);
+ break;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static void ch34x_send(struct usb_serial_port *port)
+{
+ int count;
+ int retval;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->write_urb_in_use) {
+ spin_unlock_irqrestore(&priv->lock, flags);
return;
+ }
- r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
- ch341_break_reg, 0, break_reg, 2);
- if (r < 0) {
- dev_err(&port->dev, "%s - USB control read error (%d)\n",
- __func__, r);
- goto out;
+ count = ch34x_buf_get(priv->buf, port->write_urb->transfer_buffer,
+ port->bulk_out_size);
+ if (count == 0) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
}
- dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
- __func__, break_reg[0], break_reg[1]);
- if (break_state != 0) {
- dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
- break_reg[0] &= ~CH341_NBREAK_BITS;
- break_reg[1] &= ~CH341_LCR_ENABLE_TX;
- } else {
- dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
- break_reg[0] |= CH341_NBREAK_BITS;
- break_reg[1] |= CH341_LCR_ENABLE_TX;
+
+ priv->write_urb_in_use = 1;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ usb_serial_debug_data(&port->dev, __func__, count,
+ port->write_urb->transfer_buffer);
+
+ port->write_urb->transfer_buffer_length = count;
+ port->write_urb->dev = port->serial->dev;
+ retval = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_err(&port->dev, "%s - failed submitting write urb,error %d\n"
+ , __func__, retval);
+ priv->write_urb_in_use = 0;
}
- dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
- __func__, break_reg[0], break_reg[1]);
- reg_contents = get_unaligned_le16(break_reg);
- r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
- ch341_break_reg, reg_contents);
- if (r < 0)
- dev_err(&port->dev, "%s - USB control write error (%d)\n",
- __func__, r);
-out:
- kfree(break_reg);
+
+ usb_serial_port_softint(port);
}
-static int ch341_tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear)
+static int ch34x_write(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ const unsigned char *buf,
+ int count)
{
- struct usb_serial_port *port = tty->driver_data;
- struct ch341_private *priv = usb_get_serial_port_data(port);
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- u8 control;
+
+ dev_dbg(&port->dev, "%s - port:%d, %d bytes",
+ __func__, port->port_number, count);
+
+ if (!count)
+ return count;
spin_lock_irqsave(&priv->lock, flags);
- if (set & TIOCM_RTS)
- priv->line_control |= CH341_BIT_RTS;
- if (set & TIOCM_DTR)
- priv->line_control |= CH341_BIT_DTR;
- if (clear & TIOCM_RTS)
- priv->line_control &= ~CH341_BIT_RTS;
- if (clear & TIOCM_DTR)
- priv->line_control &= ~CH341_BIT_DTR;
- control = priv->line_control;
+ count = ch34x_buf_put(priv->buf, buf, count);
spin_unlock_irqrestore(&priv->lock, flags);
- return ch341_set_handshake(port->serial->dev, control);
+ ch34x_send(port);
+
+ return count;
}
-static void ch341_update_line_status(struct usb_serial_port *port,
- unsigned char *data, size_t len)
+static int ch34x_write_room(struct tty_struct *tty)
{
- struct ch341_private *priv = usb_get_serial_port_data(port);
- struct tty_struct *tty;
+ struct usb_serial_port *port = tty->driver_data;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ int room = 0;
unsigned long flags;
- u8 status;
- u8 delta;
- if (len < 4)
- return;
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
- status = ~data[2] & CH341_BITS_MODEM_STAT;
+ spin_lock_irqsave(&priv->lock, flags);
+ room = ch34x_buf_space_avail(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dev_dbg(&port->dev, "%s - room:%d", __func__, room);
+ return room;
+}
+
+static int ch34x_chars_in_buffer(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ int chars = 0;
+ unsigned long flags;
+
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
spin_lock_irqsave(&priv->lock, flags);
- delta = status ^ priv->line_status;
- priv->line_status = status;
+ chars = ch34x_buf_data_avail(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
- if (data[1] & CH341_MULT_STAT)
- dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
+ dev_dbg(&port->dev, "%s - chars:%d", __func__, chars);
- if (!delta)
- return;
+ return chars;
+}
- if (delta & CH341_BIT_CTS)
- port->icount.cts++;
- if (delta & CH341_BIT_DSR)
- port->icount.dsr++;
- if (delta & CH341_BIT_RI)
- port->icount.rng++;
- if (delta & CH341_BIT_DCD) {
- port->icount.dcd++;
- tty = tty_port_tty_get(&port->port);
- if (tty) {
- usb_serial_handle_dcd_change(port, tty,
- status & CH341_BIT_DCD);
- tty_kref_put(tty);
+static int ch34x_attach(struct usb_serial *serial)
+{
+ struct ch34x_private *priv;
+ int i;
+ char buf[8];
+
+ dev_dbg(&serial->interface->dev, "%s", __func__);
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ priv = kzalloc(sizeof(struct ch34x_private), GFP_KERNEL);
+ if (!priv)
+ goto cleanup;
+ spin_lock_init(&priv->lock);
+ priv->buf = ch34x_buf_alloc(CH34x_BUF_SIZE);
+ if (priv->buf == NULL) {
+ kfree(priv);
+ goto cleanup;
}
+ init_waitqueue_head(&priv->delta_msr_wait);
+ usb_set_serial_port_data(serial->port[i], priv);
+ }
+
+ ch34x_vendor_read(VENDOR_VERSION, 0x0000, 0x0000,
+ serial, buf, 0x02);
+ ch34x_vendor_write(VENDOR_SERIAL_INIT, 0x0000, 0x0000,
+ serial, NULL, 0x00);
+ ch34x_vendor_write(VENDOR_WRITE, 0x1312, 0xD982,
+ serial, NULL, 0x00);
+ ch34x_vendor_write(VENDOR_WRITE, 0x0F2C, 0x0004,
+ serial, NULL, 0x00);
+ ch34x_vendor_read(VENDOR_READ, 0x2518, 0x0000,
+ serial, buf, 0x02);
+ ch34x_vendor_write(VENDOR_WRITE, 0x2727, 0x0000,
+ serial, NULL, 0x00);
+ ch34x_vendor_write(VENDOR_MODEM_OUT, 0x009F, 0x0000,
+ serial, NULL, 0x00);
+
+ return 0;
+
+cleanup:
+ for (--i; i >= 0; --i) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+ ch34x_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
}
- wake_up_interruptible(&port->port.delta_msr_wait);
+ return -ENOMEM;
}
-static void ch341_read_int_callback(struct urb *urb)
+static void ch34x_update_line_status(struct usb_serial_port *port,
+ unsigned char *data, unsigned int actual_length)
{
- struct usb_serial_port *port = urb->context;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ u8 length = UART_STATE + 0x04;
+
+ if (actual_length < length)
+ return;
+
+ /* Save off the uart status for others to look up */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_status = data[UART_STATE];
+ priv->line_control = data[PORTB_STATE];
+ spin_unlock_irqrestore(&priv->lock, flags);
+ wait_flag = 1;
+ wake_up_interruptible(&priv->delta_msr_wait);
+}
+
+static void ch34x_read_int_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
unsigned char *data = urb->transfer_buffer;
- unsigned int len = urb->actual_length;
- int status;
+ unsigned int actual_length = urb->actual_length;
+ int status = urb->status;
+ int retval;
+
+ dev_dbg(&port->dev, "%s port:%d", __func__, port->port_number);
- switch (urb->status) {
+ switch (status) {
+ /* success */
case 0:
- /* success */
break;
case -ECONNRESET:
case -ENOENT:
+ /* this urb is terminated, clean up */
case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
- __func__, urb->status);
+ dev_dbg(&port->dev, "%s - urb shutting down with status:%d",
+ __func__, status);
return;
default:
- dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
- __func__, urb->status);
+ dev_dbg(&port->dev, "%s - nonzero urb status received:%d",
+ __func__, status);
goto exit;
}
- usb_serial_debug_data(&port->dev, __func__, len, data);
- ch341_update_line_status(port, data, len);
+ usb_serial_debug_data(&port->dev, __func__,
+ urb->actual_length, urb->transfer_buffer);
+ ch34x_update_line_status(port, data, actual_length);
+
exit:
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
- __func__, status);
- }
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+ __func__, retval);
}
-static int ch341_tiocmget(struct tty_struct *tty)
+static void ch34x_read_bulk_callback(struct urb *urb)
{
- struct usb_serial_port *port = tty->driver_data;
- struct ch341_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
unsigned long flags;
- u8 mcr;
- u8 status;
- unsigned int result;
+ int i;
+ int retval;
+ int status = urb->status;
+ u8 line_status;
+ char tty_flag;
+
+ dev_dbg(&urb->dev->dev, "%s - port:%d", __func__, port->port_number);
+
+ if (status) {
+ dev_dbg(&urb->dev->dev, "%s - urb status=%d", __func__, status);
+ if (status == -EPROTO) {
+ /*
+ * CH34x mysteriously fails with -EPROTO
+ * reschedule the read
+ */
+ dev_err(&urb->dev->dev, "%s - caught -EPROTO, \
+ resubmitting the urb", __func__);
+ urb->dev = port->serial->dev;
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval) {
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __func__, retval);
+ return;
+ }
+ }
+
+ dev_dbg(&urb->dev->dev, "%s - unable to \
+ handle the error", __func__);
+ return;
+ }
+
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+
+ /* get tty_flag from status */
+ tty_flag = TTY_NORMAL;
spin_lock_irqsave(&priv->lock, flags);
- mcr = priv->line_control;
- status = priv->line_status;
+ line_status = priv->line_status;
+ priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
+ wait_flag = 1;
+ wake_up_interruptible(&priv->delta_msr_wait);
+
+ /*
+ * break takes precedence over parity,
+ * which takes precedence over framing errors.
+ */
+ if (line_status & UART_PARITY_ERROR)
+ tty_flag = TTY_PARITY;
+ else if (line_status & UART_OVERRUN_ERROR)
+ tty_flag = TTY_OVERRUN;
+ else if (line_status & UART_FRAME_ERROR)
+ tty_flag = TTY_FRAME;
+ dev_dbg(&urb->dev->dev, "%s - tty_flag=%d", __func__, tty_flag);
+
+ tty = port->port.tty;
+
+ if (tty && urb->actual_length) {
+ tty_buffer_request_room(tty->port, urb->actual_length + 1);
+
+ /* overrun is special, not associated with a char */
+ if (line_status & UART_OVERRUN_ERROR)
+ tty_insert_flip_char(tty->port, 0, TTY_OVERRUN);
+
+ for (i = 0; i < urb->actual_length; ++i)
+ tty_insert_flip_char(tty->port, data[i], tty_flag);
+
+ tty_flip_buffer_push(tty->port);
+ }
- result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
- | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
- | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
- | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
- | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
- | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
-
- dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
+ /* schedule the next read _if_ we are still open */
- return result;
+ urb->dev = port->serial->dev;
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&urb->dev->dev,
+ "%s - fialed resubmitting read urb, error %d\n",
+ __func__, retval);
}
-static int ch341_reset_resume(struct usb_serial *serial)
+static void ch34x_write_bulk_callback(struct urb *urb)
{
- struct ch341_private *priv;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct ch34x_private *priv = usb_get_serial_port_data(port);
+ int retval;
+ int status = urb->status;
- priv = usb_get_serial_port_data(serial->port[0]);
+ dev_dbg(&port->dev, "%s - port:%d", __func__, port->port_number);
- /* reconfigure ch341 serial port after bus-reset */
- ch341_configure(serial->dev, priv);
+ switch (status) {
+ /* success */
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(&port->dev, "%s - urb shutting \
+ down with status:%d", __func__, status);
+ priv->write_urb_in_use = 0;
+ return;
+ default:
+ /* error in the urb, so we have to resubmit it */
+ dev_dbg(&port->dev, "%s - Overflow in write", __func__);
+ dev_dbg(&port->dev, "%s - nonzero write bulk \
+ status received:%d", __func__, status);
+ port->write_urb->transfer_buffer_length = 1;
+ port->write_urb->dev = port->serial->dev;
+ retval = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting write urv, error:%d\n",
+ __func__, retval);
+ else
+ return;
+ }
+ priv->write_urb_in_use = 0;
- return 0;
+ /* send any buffered data */
+ ch34x_send(port);
}
-static struct usb_serial_driver ch341_device = {
+static struct usb_serial_driver ch34x_device = {
.driver = {
.owner = THIS_MODULE,
- .name = "ch341-uart",
+ .name = "ch34x",
},
- .id_table = id_table,
- .num_ports = 1,
- .open = ch341_open,
- .dtr_rts = ch341_dtr_rts,
- .carrier_raised = ch341_carrier_raised,
- .close = ch341_close,
- .set_termios = ch341_set_termios,
- .break_ctl = ch341_break_ctl,
- .tiocmget = ch341_tiocmget,
- .tiocmset = ch341_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
- .read_int_callback = ch341_read_int_callback,
- .port_probe = ch341_port_probe,
- .port_remove = ch341_port_remove,
- .reset_resume = ch341_reset_resume,
+ .id_table = id_table,
+ .num_ports = 1,
+ .open = ch34x_open,
+ .close = ch34x_close,
+ .write = ch34x_write,
+ .ioctl = ch34x_ioctl,
+ .set_termios = ch34x_set_termios,
+ .tiocmget = ch34x_tiocmget,
+ .tiocmset = ch34x_tiocmset,
+ .read_bulk_callback = ch34x_read_bulk_callback,
+ .read_int_callback = ch34x_read_int_callback,
+ .write_bulk_callback = ch34x_write_bulk_callback,
+ .write_room = ch34x_write_room,
+ .chars_in_buffer = ch34x_chars_in_buffer,
+ .attach = ch34x_attach,
};
-static struct usb_serial_driver * const serial_drivers[] = {
- &ch341_device, NULL
+static struct usb_serial_driver *const serial_driver[] = {
+ &ch34x_device, NULL
};
-module_usb_serial_driver(serial_drivers, id_table);
+static int __init ch34x_init(void)
+{
+ return usb_serial_register_drivers(serial_driver,
+ KBUILD_MODNAME, id_table);
+}
+static void __exit ch34x_exit(void)
+{
+ usb_serial_deregister_drivers(serial_driver);
+}
+
+module_init(ch34x_init);
+module_exit(ch34x_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
+
--
2.10.2