blob: 6279df905c14cee7374a103d1ef9ac1ffc48fb63 [file] [log] [blame]
Frank A Kingswood6ce76102007-08-22 20:48:58 +01001/*
2 * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
Werner Cornelius664d5df2009-01-16 21:02:41 +01003 * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
4 * Copyright 2009, Boris Hajduk <boris@hajduk.org>
Frank A Kingswood6ce76102007-08-22 20:48:58 +01005 *
6 * ch341.c implements a serial port driver for the Winchiphead CH341.
7 *
8 * The CH341 device can be used to implement an RS232 asynchronous
9 * serial port, an IEEE-1284 parallel printer port or a memory-like
10 * interface. In all cases the CH341 supports an I2C interface as well.
11 * This driver only supports the asynchronous serial interface.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License version
15 * 2 as published by the Free Software Foundation.
16 */
17
18#include <linux/kernel.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010019#include <linux/tty.h>
20#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010022#include <linux/usb.h>
23#include <linux/usb/serial.h>
24#include <linux/serial.h>
Johan Hovold5be796f2009-12-31 16:47:59 +010025#include <asm/unaligned.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010026
Werner Cornelius664d5df2009-01-16 21:02:41 +010027#define DEFAULT_BAUD_RATE 9600
Frank A Kingswood6ce76102007-08-22 20:48:58 +010028#define DEFAULT_TIMEOUT 1000
29
Werner Cornelius664d5df2009-01-16 21:02:41 +010030/* flags for IO-Bits */
31#define CH341_BIT_RTS (1 << 6)
32#define CH341_BIT_DTR (1 << 5)
33
34/******************************/
35/* interrupt pipe definitions */
36/******************************/
37/* always 4 interrupt bytes */
38/* first irq byte normally 0x08 */
39/* second irq byte base 0x7d + below */
40/* third irq byte base 0x94 + below */
41/* fourth irq byte normally 0xee */
42
43/* second interrupt byte */
44#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
45
46/* status returned in third interrupt answer byte, inverted in data
47 from irq */
48#define CH341_BIT_CTS 0x01
49#define CH341_BIT_DSR 0x02
50#define CH341_BIT_RI 0x04
51#define CH341_BIT_DCD 0x08
52#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
53
54/*******************************/
55/* baudrate calculation factor */
56/*******************************/
57#define CH341_BAUDBASE_FACTOR 1532620800
58#define CH341_BAUDBASE_DIVMAX 3
59
Tim Small492896f2009-08-17 13:21:57 +010060/* Break support - the information used to implement this was gleaned from
61 * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
62 */
63
Aidan Thornton6fde8d22016-10-22 22:02:23 +010064#define CH341_REQ_READ_VERSION 0x5F
Tim Small492896f2009-08-17 13:21:57 +010065#define CH341_REQ_WRITE_REG 0x9A
66#define CH341_REQ_READ_REG 0x95
Aidan Thornton6fde8d22016-10-22 22:02:23 +010067#define CH341_REQ_SERIAL_INIT 0xA1
68#define CH341_REQ_MODEM_CTRL 0xA4
Tim Small492896f2009-08-17 13:21:57 +010069
Aidan Thornton6fde8d22016-10-22 22:02:23 +010070#define CH341_REG_BREAK 0x05
71#define CH341_REG_LCR 0x18
72#define CH341_NBREAK_BITS 0x01
73
74#define CH341_LCR_ENABLE_RX 0x80
75#define CH341_LCR_ENABLE_TX 0x40
76#define CH341_LCR_MARK_SPACE 0x20
77#define CH341_LCR_PAR_EVEN 0x10
78#define CH341_LCR_ENABLE_PAR 0x08
79#define CH341_LCR_STOP_BITS_2 0x04
80#define CH341_LCR_CS8 0x03
81#define CH341_LCR_CS7 0x02
82#define CH341_LCR_CS6 0x01
83#define CH341_LCR_CS5 0x00
Tim Small492896f2009-08-17 13:21:57 +010084
Németh Márton7d40d7e2010-01-10 15:34:24 +010085static const struct usb_device_id id_table[] = {
Frank A Kingswood6ce76102007-08-22 20:48:58 +010086 { USB_DEVICE(0x4348, 0x5523) },
Michael F. Robbins82078232008-05-16 23:48:42 -040087 { USB_DEVICE(0x1a86, 0x7523) },
wangyanqingd0781382011-03-11 06:24:38 -080088 { USB_DEVICE(0x1a86, 0x5523) },
Frank A Kingswood6ce76102007-08-22 20:48:58 +010089 { },
90};
91MODULE_DEVICE_TABLE(usb, id_table);
92
93struct ch341_private {
Werner Cornelius664d5df2009-01-16 21:02:41 +010094 spinlock_t lock; /* access lock */
Werner Cornelius664d5df2009-01-16 21:02:41 +010095 unsigned baud_rate; /* set baud rate */
96 u8 line_control; /* set line control value RTS/DTR */
97 u8 line_status; /* active status of modem control inputs */
Frank A Kingswood6ce76102007-08-22 20:48:58 +010098};
99
Nicolas PLANELaa91def2015-03-01 13:47:22 -0500100static void ch341_set_termios(struct tty_struct *tty,
101 struct usb_serial_port *port,
102 struct ktermios *old_termios);
103
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100104static int ch341_control_out(struct usb_device *dev, u8 request,
105 u16 value, u16 index)
106{
107 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700108
109 dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
110 USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100111
112 r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
113 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
114 value, index, NULL, 0, DEFAULT_TIMEOUT);
115
116 return r;
117}
118
119static int ch341_control_in(struct usb_device *dev,
120 u8 request, u16 value, u16 index,
121 char *buf, unsigned bufsize)
122{
123 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700124
125 dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
126 USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
127 (int)bufsize);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100128
129 r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
130 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
131 value, index, buf, bufsize, DEFAULT_TIMEOUT);
132 return r;
133}
134
Aidan Thornton4e46c412016-10-22 22:02:24 +0100135static int ch341_init_set_baudrate(struct usb_device *dev,
136 struct ch341_private *priv, unsigned ctrl)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100137{
Aidan Thornton4e46c412016-10-22 22:02:24 +0100138 short a;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100139 int r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100140 unsigned long factor;
141 short divisor;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100142
Werner Cornelius664d5df2009-01-16 21:02:41 +0100143 if (!priv->baud_rate)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100144 return -EINVAL;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100145 factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
146 divisor = CH341_BAUDBASE_DIVMAX;
147
148 while ((factor > 0xfff0) && divisor) {
149 factor >>= 3;
150 divisor--;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100151 }
152
Werner Cornelius664d5df2009-01-16 21:02:41 +0100153 if (factor > 0xfff0)
154 return -EINVAL;
155
156 factor = 0x10000 - factor;
157 a = (factor & 0xff00) | divisor;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100158
Aidan Thornton4e46c412016-10-22 22:02:24 +0100159 /* 0x9c is "enable SFR_UART Control register and timer" */
160 r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
161 0x9c | (ctrl << 8), a | 0x80);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100162
163 return r;
164}
165
Werner Cornelius664d5df2009-01-16 21:02:41 +0100166static int ch341_set_handshake(struct usb_device *dev, u8 control)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100167{
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100168 return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100169}
170
Werner Cornelius664d5df2009-01-16 21:02:41 +0100171static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100172{
173 char *buffer;
174 int r;
175 const unsigned size = 8;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100176 unsigned long flags;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100177
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100178 buffer = kmalloc(size, GFP_KERNEL);
179 if (!buffer)
180 return -ENOMEM;
181
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100182 r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
Alan Coxc4d0f8c2008-04-29 14:35:39 +0100183 if (r < 0)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100184 goto out;
185
Werner Cornelius664d5df2009-01-16 21:02:41 +0100186 /* setup the private status if available */
187 if (r == 2) {
188 r = 0;
189 spin_lock_irqsave(&priv->lock, flags);
190 priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100191 spin_unlock_irqrestore(&priv->lock, flags);
192 } else
193 r = -EPROTO;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100194
195out: kfree(buffer);
196 return r;
197}
198
199/* -------------------------------------------------------------------------- */
200
Adrian Bunk93b64972007-09-09 22:25:04 +0200201static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100202{
203 char *buffer;
204 int r;
205 const unsigned size = 8;
206
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100207 buffer = kmalloc(size, GFP_KERNEL);
208 if (!buffer)
209 return -ENOMEM;
210
211 /* expect two bytes 0x27 0x00 */
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100212 r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100213 if (r < 0)
214 goto out;
Aidan Thorntona98b6902016-10-22 22:02:26 +0100215 dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100216
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100217 r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100218 if (r < 0)
219 goto out;
220
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100221 /* expect two bytes 0x56 0x00 */
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100222 r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100223 if (r < 0)
224 goto out;
225
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100226 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100227 if (r < 0)
228 goto out;
229
230 /* expect 0xff 0xee */
Werner Cornelius664d5df2009-01-16 21:02:41 +0100231 r = ch341_get_status(dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100232 if (r < 0)
233 goto out;
234
Aidan Thornton4e46c412016-10-22 22:02:24 +0100235 r = ch341_init_set_baudrate(dev, priv, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100236 if (r < 0)
237 goto out;
238
Werner Cornelius664d5df2009-01-16 21:02:41 +0100239 r = ch341_set_handshake(dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100240 if (r < 0)
241 goto out;
242
243 /* expect 0x9f 0xee */
Werner Cornelius664d5df2009-01-16 21:02:41 +0100244 r = ch341_get_status(dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100245
246out: kfree(buffer);
247 return r;
248}
249
Johan Hovold456c5be2012-10-25 10:29:03 +0200250static int ch341_port_probe(struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100251{
252 struct ch341_private *priv;
253 int r;
254
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100255 priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
256 if (!priv)
257 return -ENOMEM;
258
Werner Cornelius664d5df2009-01-16 21:02:41 +0100259 spin_lock_init(&priv->lock);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100260 priv->baud_rate = DEFAULT_BAUD_RATE;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100261
Johan Hovold456c5be2012-10-25 10:29:03 +0200262 r = ch341_configure(port->serial->dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100263 if (r < 0)
264 goto error;
265
Johan Hovold456c5be2012-10-25 10:29:03 +0200266 usb_set_serial_port_data(port, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100267 return 0;
268
269error: kfree(priv);
270 return r;
271}
272
Johan Hovold456c5be2012-10-25 10:29:03 +0200273static int ch341_port_remove(struct usb_serial_port *port)
274{
275 struct ch341_private *priv;
276
277 priv = usb_get_serial_port_data(port);
278 kfree(priv);
279
280 return 0;
281}
282
Alan Cox335f8512009-06-11 12:26:29 +0100283static int ch341_carrier_raised(struct usb_serial_port *port)
284{
285 struct ch341_private *priv = usb_get_serial_port_data(port);
286 if (priv->line_status & CH341_BIT_DCD)
287 return 1;
288 return 0;
289}
290
291static void ch341_dtr_rts(struct usb_serial_port *port, int on)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100292{
293 struct ch341_private *priv = usb_get_serial_port_data(port);
294 unsigned long flags;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100295
Alan Cox335f8512009-06-11 12:26:29 +0100296 /* drop DTR and RTS */
297 spin_lock_irqsave(&priv->lock, flags);
298 if (on)
299 priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
300 else
301 priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
302 spin_unlock_irqrestore(&priv->lock, flags);
303 ch341_set_handshake(port->serial->dev, priv->line_control);
Alan Cox335f8512009-06-11 12:26:29 +0100304}
305
306static void ch341_close(struct usb_serial_port *port)
307{
Johan Hovoldf26788d2010-03-17 23:00:45 +0100308 usb_serial_generic_close(port);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100309 usb_kill_urb(port->interrupt_in_urb);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100310}
311
312
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100313/* open this device, set default parameters */
Alan Coxa509a7e2009-09-19 13:13:26 -0700314static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100315{
316 struct usb_serial *serial = port->serial;
Johan Hovold456c5be2012-10-25 10:29:03 +0200317 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100318 int r;
319
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100320 r = ch341_configure(serial->dev, priv);
321 if (r)
322 goto out;
323
Nicolas PLANELaa91def2015-03-01 13:47:22 -0500324 if (tty)
325 ch341_set_termios(tty, port, NULL);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100326
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100327 dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100328 r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
329 if (r) {
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100330 dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
331 __func__, r);
Johan Hovold06946a62011-11-10 14:58:28 +0100332 goto out;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100333 }
334
Alan Coxa509a7e2009-09-19 13:13:26 -0700335 r = usb_serial_generic_open(tty, port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100336
337out: return r;
338}
339
340/* Old_termios contains the original termios settings and
341 * tty->termios contains the new setting to be used.
342 */
Alan Cox95da3102008-07-22 11:09:07 +0100343static void ch341_set_termios(struct tty_struct *tty,
344 struct usb_serial_port *port, struct ktermios *old_termios)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100345{
346 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100347 unsigned baud_rate;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100348 unsigned long flags;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100349 unsigned char ctrl;
350 int r;
351
352 /* redundant changes may cause the chip to lose bytes */
353 if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
354 return;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100355
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100356 baud_rate = tty_get_baud_rate(tty);
357
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100358 ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100359
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100360 switch (C_CSIZE(tty)) {
361 case CS5:
362 ctrl |= CH341_LCR_CS5;
363 break;
364 case CS6:
365 ctrl |= CH341_LCR_CS6;
366 break;
367 case CS7:
368 ctrl |= CH341_LCR_CS7;
369 break;
370 case CS8:
371 ctrl |= CH341_LCR_CS8;
372 break;
373 }
374
375 if (C_PARENB(tty)) {
376 ctrl |= CH341_LCR_ENABLE_PAR;
377 if (C_PARODD(tty) == 0)
378 ctrl |= CH341_LCR_PAR_EVEN;
379 if (C_CMSPAR(tty))
380 ctrl |= CH341_LCR_MARK_SPACE;
381 }
382
383 if (C_CSTOPB(tty))
384 ctrl |= CH341_LCR_STOP_BITS_2;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100385
Werner Cornelius664d5df2009-01-16 21:02:41 +0100386 if (baud_rate) {
387 spin_lock_irqsave(&priv->lock, flags);
388 priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
389 spin_unlock_irqrestore(&priv->lock, flags);
Johan Hovolda20047f2017-01-06 19:15:11 +0100390
391 priv->baud_rate = baud_rate;
392
Aidan Thornton4e46c412016-10-22 22:02:24 +0100393 r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
394 if (r < 0 && old_termios) {
395 priv->baud_rate = tty_termios_baud_rate(old_termios);
396 tty_termios_copy_hw(&tty->termios, old_termios);
397 }
Werner Cornelius664d5df2009-01-16 21:02:41 +0100398 } else {
399 spin_lock_irqsave(&priv->lock, flags);
400 priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
401 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100402 }
403
Werner Cornelius664d5df2009-01-16 21:02:41 +0100404 ch341_set_handshake(port->serial->dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100405
Werner Cornelius664d5df2009-01-16 21:02:41 +0100406}
Alan Cox73f59302007-10-18 01:24:18 -0700407
Tim Small492896f2009-08-17 13:21:57 +0100408static void ch341_break_ctl(struct tty_struct *tty, int break_state)
409{
410 const uint16_t ch341_break_reg =
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100411 ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
Tim Small492896f2009-08-17 13:21:57 +0100412 struct usb_serial_port *port = tty->driver_data;
413 int r;
414 uint16_t reg_contents;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100415 uint8_t *break_reg;
Tim Small492896f2009-08-17 13:21:57 +0100416
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100417 break_reg = kmalloc(2, GFP_KERNEL);
Johan Hovold10c642d2013-12-29 19:22:56 +0100418 if (!break_reg)
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100419 return;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100420
Tim Small492896f2009-08-17 13:21:57 +0100421 r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100422 ch341_break_reg, 0, break_reg, 2);
Tim Small492896f2009-08-17 13:21:57 +0100423 if (r < 0) {
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100424 dev_err(&port->dev, "%s - USB control read error (%d)\n",
425 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100426 goto out;
Tim Small492896f2009-08-17 13:21:57 +0100427 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700428 dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
429 __func__, break_reg[0], break_reg[1]);
Tim Small492896f2009-08-17 13:21:57 +0100430 if (break_state != 0) {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700431 dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100432 break_reg[0] &= ~CH341_NBREAK_BITS;
433 break_reg[1] &= ~CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100434 } else {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700435 dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100436 break_reg[0] |= CH341_NBREAK_BITS;
437 break_reg[1] |= CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100438 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700439 dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
440 __func__, break_reg[0], break_reg[1]);
Johan Hovold5be796f2009-12-31 16:47:59 +0100441 reg_contents = get_unaligned_le16(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100442 r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
443 ch341_break_reg, reg_contents);
444 if (r < 0)
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100445 dev_err(&port->dev, "%s - USB control write error (%d)\n",
446 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100447out:
448 kfree(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100449}
450
Alan Cox20b9d172011-02-14 16:26:50 +0000451static int ch341_tiocmset(struct tty_struct *tty,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100452 unsigned int set, unsigned int clear)
453{
454 struct usb_serial_port *port = tty->driver_data;
455 struct ch341_private *priv = usb_get_serial_port_data(port);
456 unsigned long flags;
457 u8 control;
458
459 spin_lock_irqsave(&priv->lock, flags);
460 if (set & TIOCM_RTS)
461 priv->line_control |= CH341_BIT_RTS;
462 if (set & TIOCM_DTR)
463 priv->line_control |= CH341_BIT_DTR;
464 if (clear & TIOCM_RTS)
465 priv->line_control &= ~CH341_BIT_RTS;
466 if (clear & TIOCM_DTR)
467 priv->line_control &= ~CH341_BIT_DTR;
468 control = priv->line_control;
469 spin_unlock_irqrestore(&priv->lock, flags);
470
471 return ch341_set_handshake(port->serial->dev, control);
472}
473
Johan Hovoldac035622014-01-02 22:49:28 +0100474static void ch341_update_line_status(struct usb_serial_port *port,
475 unsigned char *data, size_t len)
476{
477 struct ch341_private *priv = usb_get_serial_port_data(port);
Johan Hovoldb7700812014-01-02 22:49:29 +0100478 struct tty_struct *tty;
Johan Hovoldac035622014-01-02 22:49:28 +0100479 unsigned long flags;
Johan Hovoldb7700812014-01-02 22:49:29 +0100480 u8 status;
481 u8 delta;
Johan Hovoldac035622014-01-02 22:49:28 +0100482
483 if (len < 4)
484 return;
485
Johan Hovoldb7700812014-01-02 22:49:29 +0100486 status = ~data[2] & CH341_BITS_MODEM_STAT;
487
Johan Hovoldac035622014-01-02 22:49:28 +0100488 spin_lock_irqsave(&priv->lock, flags);
Johan Hovoldb7700812014-01-02 22:49:29 +0100489 delta = status ^ priv->line_status;
490 priv->line_status = status;
Johan Hovoldac035622014-01-02 22:49:28 +0100491 spin_unlock_irqrestore(&priv->lock, flags);
492
Johan Hovoldfd74b0b2014-01-02 22:49:30 +0100493 if (data[1] & CH341_MULT_STAT)
494 dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
495
Johan Hovoldd984fe92014-01-02 22:49:31 +0100496 if (!delta)
497 return;
498
Johan Hovold5e409a22014-01-02 22:49:32 +0100499 if (delta & CH341_BIT_CTS)
500 port->icount.cts++;
501 if (delta & CH341_BIT_DSR)
502 port->icount.dsr++;
503 if (delta & CH341_BIT_RI)
504 port->icount.rng++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100505 if (delta & CH341_BIT_DCD) {
Johan Hovold5e409a22014-01-02 22:49:32 +0100506 port->icount.dcd++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100507 tty = tty_port_tty_get(&port->port);
508 if (tty) {
Johan Hovoldac035622014-01-02 22:49:28 +0100509 usb_serial_handle_dcd_change(port, tty,
Johan Hovoldb7700812014-01-02 22:49:29 +0100510 status & CH341_BIT_DCD);
511 tty_kref_put(tty);
512 }
Johan Hovoldac035622014-01-02 22:49:28 +0100513 }
514
515 wake_up_interruptible(&port->port.delta_msr_wait);
516}
517
Werner Cornelius664d5df2009-01-16 21:02:41 +0100518static void ch341_read_int_callback(struct urb *urb)
519{
Johan Hovold271ec2d2014-01-02 22:49:33 +0100520 struct usb_serial_port *port = urb->context;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100521 unsigned char *data = urb->transfer_buffer;
Johan Hovold271ec2d2014-01-02 22:49:33 +0100522 unsigned int len = urb->actual_length;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100523 int status;
524
Werner Cornelius664d5df2009-01-16 21:02:41 +0100525 switch (urb->status) {
526 case 0:
527 /* success */
528 break;
529 case -ECONNRESET:
530 case -ENOENT:
531 case -ESHUTDOWN:
532 /* this urb is terminated, clean up */
Johan Hovold271ec2d2014-01-02 22:49:33 +0100533 dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700534 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100535 return;
536 default:
Johan Hovold271ec2d2014-01-02 22:49:33 +0100537 dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700538 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100539 goto exit;
540 }
541
Johan Hovold271ec2d2014-01-02 22:49:33 +0100542 usb_serial_debug_data(&port->dev, __func__, len, data);
543 ch341_update_line_status(port, data, len);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100544exit:
545 status = usb_submit_urb(urb, GFP_ATOMIC);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100546 if (status) {
547 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
Werner Cornelius664d5df2009-01-16 21:02:41 +0100548 __func__, status);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100549 }
Werner Cornelius664d5df2009-01-16 21:02:41 +0100550}
551
Alan Cox60b33c12011-02-14 16:26:14 +0000552static int ch341_tiocmget(struct tty_struct *tty)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100553{
554 struct usb_serial_port *port = tty->driver_data;
555 struct ch341_private *priv = usb_get_serial_port_data(port);
556 unsigned long flags;
557 u8 mcr;
558 u8 status;
559 unsigned int result;
560
Werner Cornelius664d5df2009-01-16 21:02:41 +0100561 spin_lock_irqsave(&priv->lock, flags);
562 mcr = priv->line_control;
563 status = priv->line_status;
564 spin_unlock_irqrestore(&priv->lock, flags);
565
566 result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
567 | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
568 | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
569 | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
570 | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
571 | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
572
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700573 dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100574
575 return result;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100576}
577
Greg Kroah-Hartman622b80c2012-05-15 15:41:47 -0700578static int ch341_reset_resume(struct usb_serial *serial)
Ming Lei1ded7ea2009-02-20 21:23:09 +0800579{
Ming Lei1ded7ea2009-02-20 21:23:09 +0800580 struct ch341_private *priv;
581
Ming Lei1ded7ea2009-02-20 21:23:09 +0800582 priv = usb_get_serial_port_data(serial->port[0]);
583
Greg Kroah-Hartman2bfd1c92012-05-07 14:10:27 -0700584 /* reconfigure ch341 serial port after bus-reset */
585 ch341_configure(serial->dev, priv);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800586
587 return 0;
588}
589
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100590static struct usb_serial_driver ch341_device = {
591 .driver = {
592 .owner = THIS_MODULE,
593 .name = "ch341-uart",
594 },
Werner Cornelius664d5df2009-01-16 21:02:41 +0100595 .id_table = id_table,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100596 .num_ports = 1,
597 .open = ch341_open,
Alan Cox335f8512009-06-11 12:26:29 +0100598 .dtr_rts = ch341_dtr_rts,
599 .carrier_raised = ch341_carrier_raised,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100600 .close = ch341_close,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100601 .set_termios = ch341_set_termios,
Tim Small492896f2009-08-17 13:21:57 +0100602 .break_ctl = ch341_break_ctl,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100603 .tiocmget = ch341_tiocmget,
604 .tiocmset = ch341_tiocmset,
Johan Hovold5e409a22014-01-02 22:49:32 +0100605 .tiocmiwait = usb_serial_generic_tiocmiwait,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100606 .read_int_callback = ch341_read_int_callback,
Johan Hovold456c5be2012-10-25 10:29:03 +0200607 .port_probe = ch341_port_probe,
608 .port_remove = ch341_port_remove,
Greg Kroah-Hartman1c1eaba2012-05-16 08:36:13 -0700609 .reset_resume = ch341_reset_resume,
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100610};
611
Alan Stern08a4f6b2012-02-23 14:56:17 -0500612static struct usb_serial_driver * const serial_drivers[] = {
613 &ch341_device, NULL
614};
615
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700616module_usb_serial_driver(serial_drivers, id_table);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100617
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100618MODULE_LICENSE("GPL");