blob: 84c10b63732b29c026eba841d07c411071209a1f [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 */
Johan Hovold3cca8622017-01-06 19:15:15 +010098 u8 lcr;
Frank A Kingswood6ce76102007-08-22 20:48:58 +010099};
100
Nicolas PLANELaa91def2015-03-01 13:47:22 -0500101static void ch341_set_termios(struct tty_struct *tty,
102 struct usb_serial_port *port,
103 struct ktermios *old_termios);
104
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100105static int ch341_control_out(struct usb_device *dev, u8 request,
106 u16 value, u16 index)
107{
108 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700109
Johan Hovold91e0efc2017-01-06 19:15:19 +0100110 dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__,
111 request, value, index);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100112
113 r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
114 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
115 value, index, NULL, 0, DEFAULT_TIMEOUT);
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100116 if (r < 0)
117 dev_err(&dev->dev, "failed to send control message: %d\n", r);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100118
119 return r;
120}
121
122static int ch341_control_in(struct usb_device *dev,
123 u8 request, u16 value, u16 index,
124 char *buf, unsigned bufsize)
125{
126 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700127
Johan Hovold91e0efc2017-01-06 19:15:19 +0100128 dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__,
129 request, value, index, bufsize);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100130
131 r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
132 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
133 value, index, buf, bufsize, DEFAULT_TIMEOUT);
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100134 if (r < bufsize) {
135 if (r >= 0) {
136 dev_err(&dev->dev,
137 "short control message received (%d < %u)\n",
138 r, bufsize);
139 r = -EIO;
140 }
141
142 dev_err(&dev->dev, "failed to receive control message: %d\n",
143 r);
144 return r;
145 }
146
147 return 0;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100148}
149
Johan Hovold55fa15b2017-01-06 19:15:16 +0100150static int ch341_set_baudrate_lcr(struct usb_device *dev,
151 struct ch341_private *priv, u8 lcr)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100152{
Aidan Thornton4e46c412016-10-22 22:02:24 +0100153 short a;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100154 int r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100155 unsigned long factor;
156 short divisor;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100157
Werner Cornelius664d5df2009-01-16 21:02:41 +0100158 if (!priv->baud_rate)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100159 return -EINVAL;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100160 factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
161 divisor = CH341_BAUDBASE_DIVMAX;
162
163 while ((factor > 0xfff0) && divisor) {
164 factor >>= 3;
165 divisor--;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100166 }
167
Werner Cornelius664d5df2009-01-16 21:02:41 +0100168 if (factor > 0xfff0)
169 return -EINVAL;
170
171 factor = 0x10000 - factor;
172 a = (factor & 0xff00) | divisor;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100173
Johan Hovold55fa15b2017-01-06 19:15:16 +0100174 /*
175 * CH341A buffers data until a full endpoint-size packet (32 bytes)
176 * has been received unless bit 7 is set.
177 */
178 a |= BIT(7);
179
180 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
181 if (r)
182 return r;
183
184 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
185 if (r)
186 return r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100187
188 return r;
189}
190
Werner Cornelius664d5df2009-01-16 21:02:41 +0100191static int ch341_set_handshake(struct usb_device *dev, u8 control)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100192{
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100193 return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100194}
195
Werner Cornelius664d5df2009-01-16 21:02:41 +0100196static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100197{
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100198 const unsigned int size = 2;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100199 char *buffer;
200 int r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100201 unsigned long flags;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100202
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100203 buffer = kmalloc(size, GFP_KERNEL);
204 if (!buffer)
205 return -ENOMEM;
206
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100207 r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
Alan Coxc4d0f8c2008-04-29 14:35:39 +0100208 if (r < 0)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100209 goto out;
210
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100211 spin_lock_irqsave(&priv->lock, flags);
212 priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
213 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100214
215out: kfree(buffer);
216 return r;
217}
218
219/* -------------------------------------------------------------------------- */
220
Adrian Bunk93b64972007-09-09 22:25:04 +0200221static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100222{
Johan Hovold2d5a9c72017-01-06 19:15:18 +0100223 const unsigned int size = 2;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100224 char *buffer;
225 int r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100226
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100227 buffer = kmalloc(size, GFP_KERNEL);
228 if (!buffer)
229 return -ENOMEM;
230
231 /* expect two bytes 0x27 0x00 */
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100232 r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100233 if (r < 0)
234 goto out;
Aidan Thorntona98b6902016-10-22 22:02:26 +0100235 dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100236
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100237 r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100238 if (r < 0)
239 goto out;
240
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100241 /* expect two bytes 0x56 0x00 */
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100242 r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100243 if (r < 0)
244 goto out;
245
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100246 r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100247 if (r < 0)
248 goto out;
249
Johan Hovold55fa15b2017-01-06 19:15:16 +0100250 r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100251 if (r < 0)
252 goto out;
253
Werner Cornelius664d5df2009-01-16 21:02:41 +0100254 r = ch341_set_handshake(dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100255
256out: kfree(buffer);
257 return r;
258}
259
Johan Hovold456c5be2012-10-25 10:29:03 +0200260static int ch341_port_probe(struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100261{
262 struct ch341_private *priv;
263 int r;
264
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100265 priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
266 if (!priv)
267 return -ENOMEM;
268
Werner Cornelius664d5df2009-01-16 21:02:41 +0100269 spin_lock_init(&priv->lock);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100270 priv->baud_rate = DEFAULT_BAUD_RATE;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100271
Johan Hovold456c5be2012-10-25 10:29:03 +0200272 r = ch341_configure(port->serial->dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100273 if (r < 0)
274 goto error;
275
Johan Hovold456c5be2012-10-25 10:29:03 +0200276 usb_set_serial_port_data(port, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100277 return 0;
278
279error: kfree(priv);
280 return r;
281}
282
Johan Hovold456c5be2012-10-25 10:29:03 +0200283static int ch341_port_remove(struct usb_serial_port *port)
284{
285 struct ch341_private *priv;
286
287 priv = usb_get_serial_port_data(port);
288 kfree(priv);
289
290 return 0;
291}
292
Alan Cox335f8512009-06-11 12:26:29 +0100293static int ch341_carrier_raised(struct usb_serial_port *port)
294{
295 struct ch341_private *priv = usb_get_serial_port_data(port);
296 if (priv->line_status & CH341_BIT_DCD)
297 return 1;
298 return 0;
299}
300
301static void ch341_dtr_rts(struct usb_serial_port *port, int on)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100302{
303 struct ch341_private *priv = usb_get_serial_port_data(port);
304 unsigned long flags;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100305
Alan Cox335f8512009-06-11 12:26:29 +0100306 /* drop DTR and RTS */
307 spin_lock_irqsave(&priv->lock, flags);
308 if (on)
309 priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
310 else
311 priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
312 spin_unlock_irqrestore(&priv->lock, flags);
313 ch341_set_handshake(port->serial->dev, priv->line_control);
Alan Cox335f8512009-06-11 12:26:29 +0100314}
315
316static void ch341_close(struct usb_serial_port *port)
317{
Johan Hovoldf26788d2010-03-17 23:00:45 +0100318 usb_serial_generic_close(port);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100319 usb_kill_urb(port->interrupt_in_urb);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100320}
321
322
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100323/* open this device, set default parameters */
Alan Coxa509a7e2009-09-19 13:13:26 -0700324static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100325{
Johan Hovold456c5be2012-10-25 10:29:03 +0200326 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100327 int r;
328
Nicolas PLANELaa91def2015-03-01 13:47:22 -0500329 if (tty)
330 ch341_set_termios(tty, port, NULL);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100331
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100332 dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100333 r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
334 if (r) {
Johan Hovoldd9a38a82014-03-12 19:09:42 +0100335 dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
336 __func__, r);
Johan Hovoldf2950b72017-01-06 19:15:13 +0100337 return r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100338 }
339
Johan Hovolda0467a92017-01-06 19:15:17 +0100340 r = ch341_get_status(port->serial->dev, priv);
341 if (r < 0) {
342 dev_err(&port->dev, "failed to read modem status: %d\n", r);
343 goto err_kill_interrupt_urb;
344 }
345
Alan Coxa509a7e2009-09-19 13:13:26 -0700346 r = usb_serial_generic_open(tty, port);
Johan Hovoldf2950b72017-01-06 19:15:13 +0100347 if (r)
348 goto err_kill_interrupt_urb;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100349
Johan Hovoldf2950b72017-01-06 19:15:13 +0100350 return 0;
351
352err_kill_interrupt_urb:
353 usb_kill_urb(port->interrupt_in_urb);
354
355 return r;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100356}
357
358/* Old_termios contains the original termios settings and
359 * tty->termios contains the new setting to be used.
360 */
Alan Cox95da3102008-07-22 11:09:07 +0100361static void ch341_set_termios(struct tty_struct *tty,
362 struct usb_serial_port *port, struct ktermios *old_termios)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100363{
364 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100365 unsigned baud_rate;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100366 unsigned long flags;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100367 unsigned char ctrl;
368 int r;
369
370 /* redundant changes may cause the chip to lose bytes */
371 if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
372 return;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100373
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100374 baud_rate = tty_get_baud_rate(tty);
375
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100376 ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100377
Aidan Thorntonba781bd2016-10-22 22:02:25 +0100378 switch (C_CSIZE(tty)) {
379 case CS5:
380 ctrl |= CH341_LCR_CS5;
381 break;
382 case CS6:
383 ctrl |= CH341_LCR_CS6;
384 break;
385 case CS7:
386 ctrl |= CH341_LCR_CS7;
387 break;
388 case CS8:
389 ctrl |= CH341_LCR_CS8;
390 break;
391 }
392
393 if (C_PARENB(tty)) {
394 ctrl |= CH341_LCR_ENABLE_PAR;
395 if (C_PARODD(tty) == 0)
396 ctrl |= CH341_LCR_PAR_EVEN;
397 if (C_CMSPAR(tty))
398 ctrl |= CH341_LCR_MARK_SPACE;
399 }
400
401 if (C_CSTOPB(tty))
402 ctrl |= CH341_LCR_STOP_BITS_2;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100403
Werner Cornelius664d5df2009-01-16 21:02:41 +0100404 if (baud_rate) {
Johan Hovolda20047f2017-01-06 19:15:11 +0100405 priv->baud_rate = baud_rate;
406
Johan Hovold55fa15b2017-01-06 19:15:16 +0100407 r = ch341_set_baudrate_lcr(port->serial->dev, priv, ctrl);
Aidan Thornton4e46c412016-10-22 22:02:24 +0100408 if (r < 0 && old_termios) {
409 priv->baud_rate = tty_termios_baud_rate(old_termios);
410 tty_termios_copy_hw(&tty->termios, old_termios);
Johan Hovold3cca8622017-01-06 19:15:15 +0100411 } else if (r == 0) {
412 priv->lcr = ctrl;
Aidan Thornton4e46c412016-10-22 22:02:24 +0100413 }
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100414 }
415
Johan Hovold030ee7a2017-01-06 19:15:12 +0100416 spin_lock_irqsave(&priv->lock, flags);
417 if (C_BAUD(tty) == B0)
418 priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
419 else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
420 priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
421 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100422
Johan Hovold030ee7a2017-01-06 19:15:12 +0100423 ch341_set_handshake(port->serial->dev, priv->line_control);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100424}
Alan Cox73f59302007-10-18 01:24:18 -0700425
Tim Small492896f2009-08-17 13:21:57 +0100426static void ch341_break_ctl(struct tty_struct *tty, int break_state)
427{
428 const uint16_t ch341_break_reg =
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100429 ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
Tim Small492896f2009-08-17 13:21:57 +0100430 struct usb_serial_port *port = tty->driver_data;
431 int r;
432 uint16_t reg_contents;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100433 uint8_t *break_reg;
Tim Small492896f2009-08-17 13:21:57 +0100434
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100435 break_reg = kmalloc(2, GFP_KERNEL);
Johan Hovold10c642d2013-12-29 19:22:56 +0100436 if (!break_reg)
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100437 return;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100438
Tim Small492896f2009-08-17 13:21:57 +0100439 r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100440 ch341_break_reg, 0, break_reg, 2);
Tim Small492896f2009-08-17 13:21:57 +0100441 if (r < 0) {
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100442 dev_err(&port->dev, "%s - USB control read error (%d)\n",
443 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100444 goto out;
Tim Small492896f2009-08-17 13:21:57 +0100445 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700446 dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
447 __func__, break_reg[0], break_reg[1]);
Tim Small492896f2009-08-17 13:21:57 +0100448 if (break_state != 0) {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700449 dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100450 break_reg[0] &= ~CH341_NBREAK_BITS;
451 break_reg[1] &= ~CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100452 } else {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700453 dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
Aidan Thornton6fde8d22016-10-22 22:02:23 +0100454 break_reg[0] |= CH341_NBREAK_BITS;
455 break_reg[1] |= CH341_LCR_ENABLE_TX;
Tim Small492896f2009-08-17 13:21:57 +0100456 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700457 dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
458 __func__, break_reg[0], break_reg[1]);
Johan Hovold5be796f2009-12-31 16:47:59 +0100459 reg_contents = get_unaligned_le16(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100460 r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
461 ch341_break_reg, reg_contents);
462 if (r < 0)
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100463 dev_err(&port->dev, "%s - USB control write error (%d)\n",
464 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100465out:
466 kfree(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100467}
468
Alan Cox20b9d172011-02-14 16:26:50 +0000469static int ch341_tiocmset(struct tty_struct *tty,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100470 unsigned int set, unsigned int clear)
471{
472 struct usb_serial_port *port = tty->driver_data;
473 struct ch341_private *priv = usb_get_serial_port_data(port);
474 unsigned long flags;
475 u8 control;
476
477 spin_lock_irqsave(&priv->lock, flags);
478 if (set & TIOCM_RTS)
479 priv->line_control |= CH341_BIT_RTS;
480 if (set & TIOCM_DTR)
481 priv->line_control |= CH341_BIT_DTR;
482 if (clear & TIOCM_RTS)
483 priv->line_control &= ~CH341_BIT_RTS;
484 if (clear & TIOCM_DTR)
485 priv->line_control &= ~CH341_BIT_DTR;
486 control = priv->line_control;
487 spin_unlock_irqrestore(&priv->lock, flags);
488
489 return ch341_set_handshake(port->serial->dev, control);
490}
491
Johan Hovoldac035622014-01-02 22:49:28 +0100492static void ch341_update_line_status(struct usb_serial_port *port,
493 unsigned char *data, size_t len)
494{
495 struct ch341_private *priv = usb_get_serial_port_data(port);
Johan Hovoldb7700812014-01-02 22:49:29 +0100496 struct tty_struct *tty;
Johan Hovoldac035622014-01-02 22:49:28 +0100497 unsigned long flags;
Johan Hovoldb7700812014-01-02 22:49:29 +0100498 u8 status;
499 u8 delta;
Johan Hovoldac035622014-01-02 22:49:28 +0100500
501 if (len < 4)
502 return;
503
Johan Hovoldb7700812014-01-02 22:49:29 +0100504 status = ~data[2] & CH341_BITS_MODEM_STAT;
505
Johan Hovoldac035622014-01-02 22:49:28 +0100506 spin_lock_irqsave(&priv->lock, flags);
Johan Hovoldb7700812014-01-02 22:49:29 +0100507 delta = status ^ priv->line_status;
508 priv->line_status = status;
Johan Hovoldac035622014-01-02 22:49:28 +0100509 spin_unlock_irqrestore(&priv->lock, flags);
510
Johan Hovoldfd74b0b2014-01-02 22:49:30 +0100511 if (data[1] & CH341_MULT_STAT)
512 dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
513
Johan Hovoldd984fe92014-01-02 22:49:31 +0100514 if (!delta)
515 return;
516
Johan Hovold5e409a22014-01-02 22:49:32 +0100517 if (delta & CH341_BIT_CTS)
518 port->icount.cts++;
519 if (delta & CH341_BIT_DSR)
520 port->icount.dsr++;
521 if (delta & CH341_BIT_RI)
522 port->icount.rng++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100523 if (delta & CH341_BIT_DCD) {
Johan Hovold5e409a22014-01-02 22:49:32 +0100524 port->icount.dcd++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100525 tty = tty_port_tty_get(&port->port);
526 if (tty) {
Johan Hovoldac035622014-01-02 22:49:28 +0100527 usb_serial_handle_dcd_change(port, tty,
Johan Hovoldb7700812014-01-02 22:49:29 +0100528 status & CH341_BIT_DCD);
529 tty_kref_put(tty);
530 }
Johan Hovoldac035622014-01-02 22:49:28 +0100531 }
532
533 wake_up_interruptible(&port->port.delta_msr_wait);
534}
535
Werner Cornelius664d5df2009-01-16 21:02:41 +0100536static void ch341_read_int_callback(struct urb *urb)
537{
Johan Hovold271ec2d2014-01-02 22:49:33 +0100538 struct usb_serial_port *port = urb->context;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100539 unsigned char *data = urb->transfer_buffer;
Johan Hovold271ec2d2014-01-02 22:49:33 +0100540 unsigned int len = urb->actual_length;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100541 int status;
542
Werner Cornelius664d5df2009-01-16 21:02:41 +0100543 switch (urb->status) {
544 case 0:
545 /* success */
546 break;
547 case -ECONNRESET:
548 case -ENOENT:
549 case -ESHUTDOWN:
550 /* this urb is terminated, clean up */
Johan Hovold271ec2d2014-01-02 22:49:33 +0100551 dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700552 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100553 return;
554 default:
Johan Hovold271ec2d2014-01-02 22:49:33 +0100555 dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700556 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100557 goto exit;
558 }
559
Johan Hovold271ec2d2014-01-02 22:49:33 +0100560 usb_serial_debug_data(&port->dev, __func__, len, data);
561 ch341_update_line_status(port, data, len);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100562exit:
563 status = usb_submit_urb(urb, GFP_ATOMIC);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100564 if (status) {
565 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
Werner Cornelius664d5df2009-01-16 21:02:41 +0100566 __func__, status);
Johan Hovold271ec2d2014-01-02 22:49:33 +0100567 }
Werner Cornelius664d5df2009-01-16 21:02:41 +0100568}
569
Alan Cox60b33c12011-02-14 16:26:14 +0000570static int ch341_tiocmget(struct tty_struct *tty)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100571{
572 struct usb_serial_port *port = tty->driver_data;
573 struct ch341_private *priv = usb_get_serial_port_data(port);
574 unsigned long flags;
575 u8 mcr;
576 u8 status;
577 unsigned int result;
578
Werner Cornelius664d5df2009-01-16 21:02:41 +0100579 spin_lock_irqsave(&priv->lock, flags);
580 mcr = priv->line_control;
581 status = priv->line_status;
582 spin_unlock_irqrestore(&priv->lock, flags);
583
584 result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
585 | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
586 | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
587 | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
588 | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
589 | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
590
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700591 dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100592
593 return result;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100594}
595
Greg Kroah-Hartman622b80c2012-05-15 15:41:47 -0700596static int ch341_reset_resume(struct usb_serial *serial)
Ming Lei1ded7ea2009-02-20 21:23:09 +0800597{
Johan Hovoldce5e2922017-01-06 19:15:14 +0100598 struct usb_serial_port *port = serial->port[0];
599 struct ch341_private *priv = usb_get_serial_port_data(port);
600 int ret;
Ming Lei1ded7ea2009-02-20 21:23:09 +0800601
Greg Kroah-Hartman2bfd1c92012-05-07 14:10:27 -0700602 /* reconfigure ch341 serial port after bus-reset */
603 ch341_configure(serial->dev, priv);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800604
Johan Hovoldce5e2922017-01-06 19:15:14 +0100605 if (tty_port_initialized(&port->port)) {
606 ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
607 if (ret) {
608 dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
609 ret);
610 return ret;
611 }
Johan Hovolda0467a92017-01-06 19:15:17 +0100612
613 ret = ch341_get_status(port->serial->dev, priv);
614 if (ret < 0) {
615 dev_err(&port->dev, "failed to read modem status: %d\n",
616 ret);
617 }
Johan Hovoldce5e2922017-01-06 19:15:14 +0100618 }
619
620 return usb_serial_generic_resume(serial);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800621}
622
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100623static struct usb_serial_driver ch341_device = {
624 .driver = {
625 .owner = THIS_MODULE,
626 .name = "ch341-uart",
627 },
Werner Cornelius664d5df2009-01-16 21:02:41 +0100628 .id_table = id_table,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100629 .num_ports = 1,
630 .open = ch341_open,
Alan Cox335f8512009-06-11 12:26:29 +0100631 .dtr_rts = ch341_dtr_rts,
632 .carrier_raised = ch341_carrier_raised,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100633 .close = ch341_close,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100634 .set_termios = ch341_set_termios,
Tim Small492896f2009-08-17 13:21:57 +0100635 .break_ctl = ch341_break_ctl,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100636 .tiocmget = ch341_tiocmget,
637 .tiocmset = ch341_tiocmset,
Johan Hovold5e409a22014-01-02 22:49:32 +0100638 .tiocmiwait = usb_serial_generic_tiocmiwait,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100639 .read_int_callback = ch341_read_int_callback,
Johan Hovold456c5be2012-10-25 10:29:03 +0200640 .port_probe = ch341_port_probe,
641 .port_remove = ch341_port_remove,
Greg Kroah-Hartman1c1eaba2012-05-16 08:36:13 -0700642 .reset_resume = ch341_reset_resume,
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100643};
644
Alan Stern08a4f6b2012-02-23 14:56:17 -0500645static struct usb_serial_driver * const serial_drivers[] = {
646 &ch341_device, NULL
647};
648
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700649module_usb_serial_driver(serial_drivers, id_table);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100650
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100651MODULE_LICENSE("GPL");