blob: c5ecdcd51ffc6742e09af4954e5c3c272233c208 [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0
Frank A Kingswood6ce76102007-08-22 20:48:58 +01002/*
3 * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
Werner Cornelius664d5df2009-01-16 21:02:41 +01004 * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
5 * Copyright 2009, Boris Hajduk <boris@hajduk.org>
Frank A Kingswood6ce76102007-08-22 20:48:58 +01006 *
7 * ch341.c implements a serial port driver for the Winchiphead CH341.
8 *
9 * The CH341 device can be used to implement an RS232 asynchronous
10 * serial port, an IEEE-1284 parallel printer port or a memory-like
11 * interface. In all cases the CH341 supports an I2C interface as well.
12 * This driver only supports the asynchronous serial interface.
Frank A Kingswood6ce76102007-08-22 20:48:58 +010013 */
14
15#include <linux/kernel.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010016#include <linux/tty.h>
17#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010019#include <linux/usb.h>
20#include <linux/usb/serial.h>
21#include <linux/serial.h>
Johan Hovold5be796f2009-12-31 16:47:59 +010022#include <asm/unaligned.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010023
Werner Cornelius664d5df2009-01-16 21:02:41 +010024#define DEFAULT_BAUD_RATE 9600
Frank A Kingswood6ce76102007-08-22 20:48:58 +010025#define DEFAULT_TIMEOUT 1000
26
Werner Cornelius664d5df2009-01-16 21:02:41 +010027/* flags for IO-Bits */
28#define CH341_BIT_RTS (1 << 6)
29#define CH341_BIT_DTR (1 << 5)
30
31/******************************/
32/* interrupt pipe definitions */
33/******************************/
34/* always 4 interrupt bytes */
35/* first irq byte normally 0x08 */
36/* second irq byte base 0x7d + below */
37/* third irq byte base 0x94 + below */
38/* fourth irq byte normally 0xee */
39
40/* second interrupt byte */
41#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
42
43/* status returned in third interrupt answer byte, inverted in data
44 from irq */
45#define CH341_BIT_CTS 0x01
46#define CH341_BIT_DSR 0x02
47#define CH341_BIT_RI 0x04
48#define CH341_BIT_DCD 0x08
49#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
50
Tim Small492896f2009-08-17 13:21:57 +010051/* Break support - the information used to implement this was gleaned from
52 * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
53 */
54
Aidan Thornton6fde8d22016-10-22 22:02:23 +010055#define CH341_REQ_READ_VERSION 0x5F
Tim Small492896f2009-08-17 13:21:57 +010056#define CH341_REQ_WRITE_REG 0x9A
57#define CH341_REQ_READ_REG 0x95
Aidan Thornton6fde8d22016-10-22 22:02:23 +010058#define CH341_REQ_SERIAL_INIT 0xA1
59#define CH341_REQ_MODEM_CTRL 0xA4
Tim Small492896f2009-08-17 13:21:57 +010060
Aidan Thornton6fde8d22016-10-22 22:02:23 +010061#define CH341_REG_BREAK 0x05
62#define CH341_REG_LCR 0x18
63#define CH341_NBREAK_BITS 0x01
64
65#define CH341_LCR_ENABLE_RX 0x80
66#define CH341_LCR_ENABLE_TX 0x40
67#define CH341_LCR_MARK_SPACE 0x20
68#define CH341_LCR_PAR_EVEN 0x10
69#define CH341_LCR_ENABLE_PAR 0x08
70#define CH341_LCR_STOP_BITS_2 0x04
71#define CH341_LCR_CS8 0x03
72#define CH341_LCR_CS7 0x02
73#define CH341_LCR_CS6 0x01
74#define CH341_LCR_CS5 0x00
Tim Small492896f2009-08-17 13:21:57 +010075
Németh Márton7d40d7e2010-01-10 15:34:24 +010076static const struct usb_device_id id_table[] = {
Frank A Kingswood6ce76102007-08-22 20:48:58 +010077 { USB_DEVICE(0x4348, 0x5523) },
Michael F. Robbins82078232008-05-16 23:48:42 -040078 { USB_DEVICE(0x1a86, 0x7523) },
wangyanqingd0781382011-03-11 06:24:38 -080079 { USB_DEVICE(0x1a86, 0x5523) },
Frank A Kingswood6ce76102007-08-22 20:48:58 +010080 { },
81};
82MODULE_DEVICE_TABLE(usb, id_table);
83
84struct ch341_private {
Werner Cornelius664d5df2009-01-16 21:02:41 +010085 spinlock_t lock; /* access lock */
Werner Cornelius664d5df2009-01-16 21:02:41 +010086 unsigned baud_rate; /* set baud rate */
Johan Hovoldbeea33d2017-01-06 19:15:20 +010087 u8 mcr;
Johan Hovolde8024462017-01-06 19:15:21 +010088 u8 msr;
Johan Hovold3cca8622017-01-06 19:15:15 +010089 u8 lcr;
Frank A Kingswood6ce76102007-08-22 20:48:58 +010090};
91
Nicolas PLANELaa91def2015-03-01 13:47:22 -050092static void ch341_set_termios(struct tty_struct *tty,
93 struct usb_serial_port *port,
94 struct ktermios *old_termios);
95
Frank A Kingswood6ce76102007-08-22 20:48:58 +010096static int ch341_control_out(struct usb_device *dev, u8 request,
97 u16 value, u16 index)
98{
99 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700100
Johan Hovold91e0efc2017-01-06 19:15:19 +0100101 dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__,
102 request, value, index);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100103
104 r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
105 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
106 value, index, NULL, 0, DEFAULT_TIMEOUT);
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100107 if (r < 0)
108 dev_err(&dev->dev, "failed to send control message: %d\n", r);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100109
110 return r;
111}
112
113static int ch341_control_in(struct usb_device *dev,
114 u8 request, u16 value, u16 index,
115 char *buf, unsigned bufsize)
116{
117 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700118
Johan Hovold91e0efc2017-01-06 19:15:19 +0100119 dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__,
120 request, value, index, bufsize);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100121
122 r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
123 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
124 value, index, buf, bufsize, DEFAULT_TIMEOUT);
Dan Carpentere33eab92018-07-04 12:29:38 +0300125 if (r < (int)bufsize) {
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100126 if (r >= 0) {
127 dev_err(&dev->dev,
128 "short control message received (%d < %u)\n",
129 r, bufsize);
130 r = -EIO;
131 }
132
133 dev_err(&dev->dev, "failed to receive control message: %d\n",
134 r);
135 return r;
136 }
137
138 return 0;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100139}
140
Johan Hovold35714562019-11-01 18:24:10 +0100141#define CH341_CLKRATE 48000000
142#define CH341_CLK_DIV(ps, fact) (1 << (12 - 3 * (ps) - (fact)))
143#define CH341_MIN_RATE(ps) (CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512))
144
145static const speed_t ch341_min_rates[] = {
146 CH341_MIN_RATE(0),
147 CH341_MIN_RATE(1),
148 CH341_MIN_RATE(2),
149 CH341_MIN_RATE(3),
150};
151
152/*
153 * The device line speed is given by the following equation:
154 *
155 * baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where
156 *
157 * 0 <= ps <= 3,
158 * 0 <= fact <= 1,
159 * 2 <= div <= 256 if fact = 0, or
160 * 9 <= div <= 256 if fact = 1
161 */
162static int ch341_get_divisor(speed_t speed)
163{
164 unsigned int fact, div, clk_div;
165 int ps;
166
167 /*
168 * Clamp to supported range, this makes the (ps < 0) and (div < 2)
169 * sanity checks below redundant.
170 */
171 speed = clamp(speed, 46U, 3000000U);
172
173 /*
174 * Start with highest possible base clock (fact = 1) that will give a
175 * divisor strictly less than 512.
176 */
177 fact = 1;
178 for (ps = 3; ps >= 0; ps--) {
179 if (speed > ch341_min_rates[ps])
180 break;
181 }
182
183 if (ps < 0)
184 return -EINVAL;
185
186 /* Determine corresponding divisor, rounding down. */
187 clk_div = CH341_CLK_DIV(ps, fact);
188 div = CH341_CLKRATE / (clk_div * speed);
189
190 /* Halve base clock (fact = 0) if required. */
191 if (div < 9 || div > 255) {
192 div /= 2;
193 clk_div *= 2;
194 fact = 0;
195 }
196
197 if (div < 2)
198 return -EINVAL;
199
200 /*
201 * Pick next divisor if resulting rate is closer to the requested one,
202 * scale up to avoid rounding errors on low rates.
203 */
204 if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >=
205 16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1)))
206 div++;
207
Johan Hovold7c3d0222020-02-06 12:18:19 +0100208 /*
209 * Prefer lower base clock (fact = 0) if even divisor.
210 *
211 * Note that this makes the receiver more tolerant to errors.
212 */
213 if (fact == 1 && div % 2 == 0) {
214 div /= 2;
215 fact = 0;
216 }
217
Johan Hovold35714562019-11-01 18:24:10 +0100218 return (0x100 - div) << 8 | fact << 2 | ps;
219}
220
Johan Hovold55fa15b2017-01-06 19:15:16 +0100221static int ch341_set_baudrate_lcr(struct usb_device *dev,
222 struct ch341_private *priv, u8 lcr)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100223{
Johan Hovold35714562019-11-01 18:24:10 +0100224 int val;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100225 int r;
226
Werner Cornelius664d5df2009-01-16 21:02:41 +0100227 if (!priv->baud_rate)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100228 return -EINVAL;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100229
Johan Hovold35714562019-11-01 18:24:10 +0100230 val = ch341_get_divisor(priv->baud_rate);
231 if (val < 0)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100232 return -EINVAL;
233
Johan Hovold55fa15b2017-01-06 19:15:16 +0100234 /*
235 * CH341A buffers data until a full endpoint-size packet (32 bytes)
236 * has been received unless bit 7 is set.
237 */
Johan Hovold35714562019-11-01 18:24:10 +0100238 val |= BIT(7);
Johan Hovold55fa15b2017-01-06 19:15:16 +0100239
Johan Hovold35714562019-11-01 18:24:10 +0100240 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, val);
Johan Hovold55fa15b2017-01-06 19:15:16 +0100241 if (r)
242 return r;
243
244 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
245 if (r)
246 return r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100247
248 return r;
249}
250
Werner Cornelius664d5df2009-01-16 21:02:41 +0100251static int ch341_set_handshake(struct usb_device *dev, u8 control)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100252{
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100253 return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100254}
255
Werner Cornelius664d5df2009-01-16 21:02:41 +0100256static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100257{
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100258 const unsigned int size = 2;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100259 char *buffer;
260 int r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100261 unsigned long flags;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100262
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100263 buffer = kmalloc(size, GFP_KERNEL);
264 if (!buffer)
265 return -ENOMEM;
266
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100267 r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
Alan Coxc4d0f8c2008-04-29 14:35:39 +0100268 if (r < 0)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100269 goto out;
270
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100271 spin_lock_irqsave(&priv->lock, flags);
Johan Hovolde8024462017-01-06 19:15:21 +0100272 priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT;
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100273 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100274
275out: kfree(buffer);
276 return r;
277}
278
279/* -------------------------------------------------------------------------- */
280
Adrian Bunk93b64972007-09-09 22:25:04 +0200281static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100282{
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100283 const unsigned int size = 2;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100284 char *buffer;
285 int r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100286
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100287 buffer = kmalloc(size, GFP_KERNEL);
288 if (!buffer)
289 return -ENOMEM;
290
291 /* expect two bytes 0x27 0x00 */
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100292 r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100293 if (r < 0)
294 goto out;
Aidan Thorntona98b6902016-10-22 22:02:26 +0100295 dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100296
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100297 r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100298 if (r < 0)
299 goto out;
300
Johan Hovold55fa15b2017-01-06 19:15:16 +0100301 r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100302 if (r < 0)
303 goto out;
304
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100305 r = ch341_set_handshake(dev, priv->mcr);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100306
307out: kfree(buffer);
308 return r;
309}
310
Johan Hovold456c5be2012-10-25 10:29:03 +0200311static int ch341_port_probe(struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100312{
313 struct ch341_private *priv;
314 int r;
315
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100316 priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
317 if (!priv)
318 return -ENOMEM;
319
Werner Cornelius664d5df2009-01-16 21:02:41 +0100320 spin_lock_init(&priv->lock);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100321 priv->baud_rate = DEFAULT_BAUD_RATE;
Johan Hovold7c61b0d2017-01-06 19:15:23 +0100322 /*
323 * Some CH340 devices appear unable to change the initial LCR
324 * settings, so set a sane 8N1 default.
325 */
326 priv->lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100327
Johan Hovold456c5be2012-10-25 10:29:03 +0200328 r = ch341_configure(port->serial->dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100329 if (r < 0)
330 goto error;
331
Johan Hovold456c5be2012-10-25 10:29:03 +0200332 usb_set_serial_port_data(port, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100333 return 0;
334
335error: kfree(priv);
336 return r;
337}
338
Johan Hovold456c5be2012-10-25 10:29:03 +0200339static int ch341_port_remove(struct usb_serial_port *port)
340{
341 struct ch341_private *priv;
342
343 priv = usb_get_serial_port_data(port);
344 kfree(priv);
345
346 return 0;
347}
348
Alan Cox335f8512009-06-11 12:26:29 +0100349static int ch341_carrier_raised(struct usb_serial_port *port)
350{
351 struct ch341_private *priv = usb_get_serial_port_data(port);
Johan Hovolde8024462017-01-06 19:15:21 +0100352 if (priv->msr & CH341_BIT_DCD)
Alan Cox335f8512009-06-11 12:26:29 +0100353 return 1;
354 return 0;
355}
356
357static void ch341_dtr_rts(struct usb_serial_port *port, int on)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100358{
359 struct ch341_private *priv = usb_get_serial_port_data(port);
360 unsigned long flags;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100361
Alan Cox335f8512009-06-11 12:26:29 +0100362 /* drop DTR and RTS */
363 spin_lock_irqsave(&priv->lock, flags);
364 if (on)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100365 priv->mcr |= CH341_BIT_RTS | CH341_BIT_DTR;
Alan Cox335f8512009-06-11 12:26:29 +0100366 else
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100367 priv->mcr &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
Alan Cox335f8512009-06-11 12:26:29 +0100368 spin_unlock_irqrestore(&priv->lock, flags);
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100369 ch341_set_handshake(port->serial->dev, priv->mcr);
Alan Cox335f8512009-06-11 12:26:29 +0100370}
371
372static void ch341_close(struct usb_serial_port *port)
373{
Johan Hovoldf26788d2010-03-17 23:00:45 +0100374 usb_serial_generic_close(port);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100375 usb_kill_urb(port->interrupt_in_urb);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100376}
377
378
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100379/* open this device, set default parameters */
Alan Coxa509a7e2009-09-19 13:13:26 -0700380static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100381{
Johan Hovold456c5be2012-10-25 10:29:03 +0200382 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100383 int r;
384
Nicolas PLANELaa91def2015-03-01 13:47:22 -0500385 if (tty)
386 ch341_set_termios(tty, port, NULL);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100387
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100388 dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100389 r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
390 if (r) {
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100391 dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
392 __func__, r);
Johan Hovoldf2950b72017-01-06 19:15:13 +0100393 return r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100394 }
395
Johan Hovolda0467a92017-01-06 19:15:17 +0100396 r = ch341_get_status(port->serial->dev, priv);
397 if (r < 0) {
398 dev_err(&port->dev, "failed to read modem status: %d\n", r);
399 goto err_kill_interrupt_urb;
400 }
401
Alan Coxa509a7e2009-09-19 13:13:26 -0700402 r = usb_serial_generic_open(tty, port);
Johan Hovoldf2950b72017-01-06 19:15:13 +0100403 if (r)
404 goto err_kill_interrupt_urb;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100405
Johan Hovoldf2950b72017-01-06 19:15:13 +0100406 return 0;
407
408err_kill_interrupt_urb:
409 usb_kill_urb(port->interrupt_in_urb);
410
411 return r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100412}
413
414/* Old_termios contains the original termios settings and
415 * tty->termios contains the new setting to be used.
416 */
Alan Cox95da3102008-07-22 11:09:07 +0100417static void ch341_set_termios(struct tty_struct *tty,
418 struct usb_serial_port *port, struct ktermios *old_termios)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100419{
420 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100421 unsigned baud_rate;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100422 unsigned long flags;
Johan Hovold448b6dc2017-01-06 19:15:22 +0100423 u8 lcr;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100424 int r;
425
426 /* redundant changes may cause the chip to lose bytes */
427 if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
428 return;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100429
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100430 baud_rate = tty_get_baud_rate(tty);
431
Johan Hovold448b6dc2017-01-06 19:15:22 +0100432 lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100433
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100434 switch (C_CSIZE(tty)) {
435 case CS5:
Johan Hovold448b6dc2017-01-06 19:15:22 +0100436 lcr |= CH341_LCR_CS5;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100437 break;
438 case CS6:
Johan Hovold448b6dc2017-01-06 19:15:22 +0100439 lcr |= CH341_LCR_CS6;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100440 break;
441 case CS7:
Johan Hovold448b6dc2017-01-06 19:15:22 +0100442 lcr |= CH341_LCR_CS7;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100443 break;
444 case CS8:
Johan Hovold448b6dc2017-01-06 19:15:22 +0100445 lcr |= CH341_LCR_CS8;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100446 break;
447 }
448
449 if (C_PARENB(tty)) {
Johan Hovold448b6dc2017-01-06 19:15:22 +0100450 lcr |= CH341_LCR_ENABLE_PAR;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100451 if (C_PARODD(tty) == 0)
Johan Hovold448b6dc2017-01-06 19:15:22 +0100452 lcr |= CH341_LCR_PAR_EVEN;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100453 if (C_CMSPAR(tty))
Johan Hovold448b6dc2017-01-06 19:15:22 +0100454 lcr |= CH341_LCR_MARK_SPACE;
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100455 }
456
457 if (C_CSTOPB(tty))
Johan Hovold448b6dc2017-01-06 19:15:22 +0100458 lcr |= CH341_LCR_STOP_BITS_2;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100459
Werner Cornelius664d5df2009-01-16 21:02:41 +0100460 if (baud_rate) {
Johan Hovolda20047f2017-01-06 19:15:11 +0100461 priv->baud_rate = baud_rate;
462
Johan Hovold448b6dc2017-01-06 19:15:22 +0100463 r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
Aidan Thornton4e46c412016-10-22 22:02:24 +0100464 if (r < 0 && old_termios) {
465 priv->baud_rate = tty_termios_baud_rate(old_termios);
466 tty_termios_copy_hw(&tty->termios, old_termios);
Johan Hovold3cca8622017-01-06 19:15:15 +0100467 } else if (r == 0) {
Johan Hovold448b6dc2017-01-06 19:15:22 +0100468 priv->lcr = lcr;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100469 }
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100470 }
471
Johan Hovold030ee7a2017-01-06 19:15:12 +0100472 spin_lock_irqsave(&priv->lock, flags);
473 if (C_BAUD(tty) == B0)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100474 priv->mcr &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
Johan Hovold030ee7a2017-01-06 19:15:12 +0100475 else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100476 priv->mcr |= (CH341_BIT_DTR | CH341_BIT_RTS);
Johan Hovold030ee7a2017-01-06 19:15:12 +0100477 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100478
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100479 ch341_set_handshake(port->serial->dev, priv->mcr);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100480}
Alan Cox73f59302007-10-18 01:24:18 -0700481
Tim Small492896f2009-08-17 13:21:57 +0100482static void ch341_break_ctl(struct tty_struct *tty, int break_state)
483{
484 const uint16_t ch341_break_reg =
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100485 ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
Tim Small492896f2009-08-17 13:21:57 +0100486 struct usb_serial_port *port = tty->driver_data;
487 int r;
488 uint16_t reg_contents;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100489 uint8_t *break_reg;
Tim Small492896f2009-08-17 13:21:57 +0100490
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100491 break_reg = kmalloc(2, GFP_KERNEL);
Johan Hovold10c642d2013-12-29 19:22:56 +0100492 if (!break_reg)
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100493 return;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100494
Tim Small492896f2009-08-17 13:21:57 +0100495 r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100496 ch341_break_reg, 0, break_reg, 2);
Tim Small492896f2009-08-17 13:21:57 +0100497 if (r < 0) {
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100498 dev_err(&port->dev, "%s - USB control read error (%d)\n",
499 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100500 goto out;
Tim Small492896f2009-08-17 13:21:57 +0100501 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700502 dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
503 __func__, break_reg[0], break_reg[1]);
Tim Small492896f2009-08-17 13:21:57 +0100504 if (break_state != 0) {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700505 dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100506 break_reg[0] &= ~CH341_NBREAK_BITS;
507 break_reg[1] &= ~CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100508 } else {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700509 dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100510 break_reg[0] |= CH341_NBREAK_BITS;
511 break_reg[1] |= CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100512 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700513 dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
514 __func__, break_reg[0], break_reg[1]);
Johan Hovold5be796f2009-12-31 16:47:59 +0100515 reg_contents = get_unaligned_le16(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100516 r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
517 ch341_break_reg, reg_contents);
518 if (r < 0)
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100519 dev_err(&port->dev, "%s - USB control write error (%d)\n",
520 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100521out:
522 kfree(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100523}
524
Alan Cox20b9d172011-02-14 16:26:50 +0000525static int ch341_tiocmset(struct tty_struct *tty,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100526 unsigned int set, unsigned int clear)
527{
528 struct usb_serial_port *port = tty->driver_data;
529 struct ch341_private *priv = usb_get_serial_port_data(port);
530 unsigned long flags;
531 u8 control;
532
533 spin_lock_irqsave(&priv->lock, flags);
534 if (set & TIOCM_RTS)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100535 priv->mcr |= CH341_BIT_RTS;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100536 if (set & TIOCM_DTR)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100537 priv->mcr |= CH341_BIT_DTR;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100538 if (clear & TIOCM_RTS)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100539 priv->mcr &= ~CH341_BIT_RTS;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100540 if (clear & TIOCM_DTR)
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100541 priv->mcr &= ~CH341_BIT_DTR;
542 control = priv->mcr;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100543 spin_unlock_irqrestore(&priv->lock, flags);
544
545 return ch341_set_handshake(port->serial->dev, control);
546}
547
Johan Hovolde8024462017-01-06 19:15:21 +0100548static void ch341_update_status(struct usb_serial_port *port,
Johan Hovoldac035622014-01-02 22:49:28 +0100549 unsigned char *data, size_t len)
550{
551 struct ch341_private *priv = usb_get_serial_port_data(port);
Johan Hovoldb7700812014-01-02 22:49:29 +0100552 struct tty_struct *tty;
Johan Hovoldac035622014-01-02 22:49:28 +0100553 unsigned long flags;
Johan Hovoldb7700812014-01-02 22:49:29 +0100554 u8 status;
555 u8 delta;
Johan Hovoldac035622014-01-02 22:49:28 +0100556
557 if (len < 4)
558 return;
559
Johan Hovoldb7700812014-01-02 22:49:29 +0100560 status = ~data[2] & CH341_BITS_MODEM_STAT;
561
Johan Hovoldac035622014-01-02 22:49:28 +0100562 spin_lock_irqsave(&priv->lock, flags);
Johan Hovolde8024462017-01-06 19:15:21 +0100563 delta = status ^ priv->msr;
564 priv->msr = status;
Johan Hovoldac035622014-01-02 22:49:28 +0100565 spin_unlock_irqrestore(&priv->lock, flags);
566
Johan Hovoldfd74b0b2014-01-02 22:49:30 +0100567 if (data[1] & CH341_MULT_STAT)
568 dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
569
Johan Hovoldd984fe92014-01-02 22:49:31 +0100570 if (!delta)
571 return;
572
Johan Hovold5e409a22014-01-02 22:49:32 +0100573 if (delta & CH341_BIT_CTS)
574 port->icount.cts++;
575 if (delta & CH341_BIT_DSR)
576 port->icount.dsr++;
577 if (delta & CH341_BIT_RI)
578 port->icount.rng++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100579 if (delta & CH341_BIT_DCD) {
Johan Hovold5e409a22014-01-02 22:49:32 +0100580 port->icount.dcd++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100581 tty = tty_port_tty_get(&port->port);
582 if (tty) {
Johan Hovoldac035622014-01-02 22:49:28 +0100583 usb_serial_handle_dcd_change(port, tty,
Johan Hovoldb7700812014-01-02 22:49:29 +0100584 status & CH341_BIT_DCD);
585 tty_kref_put(tty);
586 }
Johan Hovoldac035622014-01-02 22:49:28 +0100587 }
588
589 wake_up_interruptible(&port->port.delta_msr_wait);
590}
591
Werner Cornelius664d5df2009-01-16 21:02:41 +0100592static void ch341_read_int_callback(struct urb *urb)
593{
Johan Hovold271ec2d2014-01-02 22:49:33 +0100594 struct usb_serial_port *port = urb->context;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100595 unsigned char *data = urb->transfer_buffer;
Johan Hovold271ec2d2014-01-02 22:49:33 +0100596 unsigned int len = urb->actual_length;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100597 int status;
598
Werner Cornelius664d5df2009-01-16 21:02:41 +0100599 switch (urb->status) {
600 case 0:
601 /* success */
602 break;
603 case -ECONNRESET:
604 case -ENOENT:
605 case -ESHUTDOWN:
606 /* this urb is terminated, clean up */
Johan Hovold271ec2d2014-01-02 22:49:33 +0100607 dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700608 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100609 return;
610 default:
Johan Hovold271ec2d2014-01-02 22:49:33 +0100611 dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700612 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100613 goto exit;
614 }
615
Johan Hovold271ec2d2014-01-02 22:49:33 +0100616 usb_serial_debug_data(&port->dev, __func__, len, data);
Johan Hovolde8024462017-01-06 19:15:21 +0100617 ch341_update_status(port, data, len);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100618exit:
619 status = usb_submit_urb(urb, GFP_ATOMIC);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100620 if (status) {
621 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
Werner Cornelius664d5df2009-01-16 21:02:41 +0100622 __func__, status);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100623 }
Werner Cornelius664d5df2009-01-16 21:02:41 +0100624}
625
Alan Cox60b33c12011-02-14 16:26:14 +0000626static int ch341_tiocmget(struct tty_struct *tty)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100627{
628 struct usb_serial_port *port = tty->driver_data;
629 struct ch341_private *priv = usb_get_serial_port_data(port);
630 unsigned long flags;
631 u8 mcr;
632 u8 status;
633 unsigned int result;
634
Werner Cornelius664d5df2009-01-16 21:02:41 +0100635 spin_lock_irqsave(&priv->lock, flags);
Johan Hovoldbeea33d2017-01-06 19:15:20 +0100636 mcr = priv->mcr;
Johan Hovolde8024462017-01-06 19:15:21 +0100637 status = priv->msr;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100638 spin_unlock_irqrestore(&priv->lock, flags);
639
640 result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
641 | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
642 | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
643 | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
644 | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
645 | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
646
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700647 dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100648
649 return result;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100650}
651
Greg Kroah-Hartman622b80c2012-05-15 15:41:47 -0700652static int ch341_reset_resume(struct usb_serial *serial)
Ming Lei1ded7ea2009-02-20 21:23:09 +0800653{
Johan Hovoldce5e2922017-01-06 19:15:14 +0100654 struct usb_serial_port *port = serial->port[0];
Johan Hovold4d5ef532020-01-17 10:50:22 +0100655 struct ch341_private *priv;
Johan Hovoldce5e2922017-01-06 19:15:14 +0100656 int ret;
Ming Lei1ded7ea2009-02-20 21:23:09 +0800657
Johan Hovold4d5ef532020-01-17 10:50:22 +0100658 priv = usb_get_serial_port_data(port);
659 if (!priv)
660 return 0;
661
Greg Kroah-Hartman2bfd1c92012-05-07 14:10:27 -0700662 /* reconfigure ch341 serial port after bus-reset */
663 ch341_configure(serial->dev, priv);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800664
Johan Hovoldce5e2922017-01-06 19:15:14 +0100665 if (tty_port_initialized(&port->port)) {
666 ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
667 if (ret) {
668 dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
669 ret);
670 return ret;
671 }
Johan Hovolda0467a92017-01-06 19:15:17 +0100672
673 ret = ch341_get_status(port->serial->dev, priv);
674 if (ret < 0) {
675 dev_err(&port->dev, "failed to read modem status: %d\n",
676 ret);
677 }
Johan Hovoldce5e2922017-01-06 19:15:14 +0100678 }
679
680 return usb_serial_generic_resume(serial);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800681}
682
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100683static struct usb_serial_driver ch341_device = {
684 .driver = {
685 .owner = THIS_MODULE,
686 .name = "ch341-uart",
687 },
Werner Cornelius664d5df2009-01-16 21:02:41 +0100688 .id_table = id_table,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100689 .num_ports = 1,
690 .open = ch341_open,
Alan Cox335f8512009-06-11 12:26:29 +0100691 .dtr_rts = ch341_dtr_rts,
692 .carrier_raised = ch341_carrier_raised,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100693 .close = ch341_close,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100694 .set_termios = ch341_set_termios,
Tim Small492896f2009-08-17 13:21:57 +0100695 .break_ctl = ch341_break_ctl,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100696 .tiocmget = ch341_tiocmget,
697 .tiocmset = ch341_tiocmset,
Johan Hovold5e409a22014-01-02 22:49:32 +0100698 .tiocmiwait = usb_serial_generic_tiocmiwait,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100699 .read_int_callback = ch341_read_int_callback,
Johan Hovold456c5be2012-10-25 10:29:03 +0200700 .port_probe = ch341_port_probe,
701 .port_remove = ch341_port_remove,
Greg Kroah-Hartman1c1eaba2012-05-16 08:36:13 -0700702 .reset_resume = ch341_reset_resume,
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100703};
704
Alan Stern08a4f6b2012-02-23 14:56:17 -0500705static struct usb_serial_driver * const serial_drivers[] = {
706 &ch341_device, NULL
707};
708
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700709module_usb_serial_driver(serial_drivers, id_table);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100710
Johan Hovold627cfa82017-11-03 18:12:08 +0100711MODULE_LICENSE("GPL v2");