blob: ac21618ace190e67ce3a9b454120780a51299250 [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>
19#include <linux/init.h>
20#include <linux/tty.h>
21#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010023#include <linux/usb.h>
24#include <linux/usb/serial.h>
25#include <linux/serial.h>
Johan Hovold5be796f2009-12-31 16:47:59 +010026#include <asm/unaligned.h>
Frank A Kingswood6ce76102007-08-22 20:48:58 +010027
Werner Cornelius664d5df2009-01-16 21:02:41 +010028#define DEFAULT_BAUD_RATE 9600
Frank A Kingswood6ce76102007-08-22 20:48:58 +010029#define DEFAULT_TIMEOUT 1000
30
Werner Cornelius664d5df2009-01-16 21:02:41 +010031/* flags for IO-Bits */
32#define CH341_BIT_RTS (1 << 6)
33#define CH341_BIT_DTR (1 << 5)
34
35/******************************/
36/* interrupt pipe definitions */
37/******************************/
38/* always 4 interrupt bytes */
39/* first irq byte normally 0x08 */
40/* second irq byte base 0x7d + below */
41/* third irq byte base 0x94 + below */
42/* fourth irq byte normally 0xee */
43
44/* second interrupt byte */
45#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
46
47/* status returned in third interrupt answer byte, inverted in data
48 from irq */
49#define CH341_BIT_CTS 0x01
50#define CH341_BIT_DSR 0x02
51#define CH341_BIT_RI 0x04
52#define CH341_BIT_DCD 0x08
53#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
54
55/*******************************/
56/* baudrate calculation factor */
57/*******************************/
58#define CH341_BAUDBASE_FACTOR 1532620800
59#define CH341_BAUDBASE_DIVMAX 3
60
Tim Small492896f2009-08-17 13:21:57 +010061/* Break support - the information used to implement this was gleaned from
62 * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
63 */
64
65#define CH341_REQ_WRITE_REG 0x9A
66#define CH341_REQ_READ_REG 0x95
67#define CH341_REG_BREAK1 0x05
68#define CH341_REG_BREAK2 0x18
69#define CH341_NBREAK_BITS_REG1 0x01
70#define CH341_NBREAK_BITS_REG2 0x40
71
72
Németh Márton7d40d7e2010-01-10 15:34:24 +010073static const struct usb_device_id id_table[] = {
Frank A Kingswood6ce76102007-08-22 20:48:58 +010074 { USB_DEVICE(0x4348, 0x5523) },
Michael F. Robbins82078232008-05-16 23:48:42 -040075 { USB_DEVICE(0x1a86, 0x7523) },
wangyanqingd0781382011-03-11 06:24:38 -080076 { USB_DEVICE(0x1a86, 0x5523) },
Frank A Kingswood6ce76102007-08-22 20:48:58 +010077 { },
78};
79MODULE_DEVICE_TABLE(usb, id_table);
80
81struct ch341_private {
Werner Cornelius664d5df2009-01-16 21:02:41 +010082 spinlock_t lock; /* access lock */
Werner Cornelius664d5df2009-01-16 21:02:41 +010083 unsigned baud_rate; /* set baud rate */
84 u8 line_control; /* set line control value RTS/DTR */
85 u8 line_status; /* active status of modem control inputs */
Frank A Kingswood6ce76102007-08-22 20:48:58 +010086};
87
88static int ch341_control_out(struct usb_device *dev, u8 request,
89 u16 value, u16 index)
90{
91 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -070092
93 dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
94 USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
Frank A Kingswood6ce76102007-08-22 20:48:58 +010095
96 r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
97 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
98 value, index, NULL, 0, DEFAULT_TIMEOUT);
99
100 return r;
101}
102
103static int ch341_control_in(struct usb_device *dev,
104 u8 request, u16 value, u16 index,
105 char *buf, unsigned bufsize)
106{
107 int r;
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700108
109 dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
110 USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
111 (int)bufsize);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100112
113 r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
114 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
115 value, index, buf, bufsize, DEFAULT_TIMEOUT);
116 return r;
117}
118
Adrian Bunk93b64972007-09-09 22:25:04 +0200119static int ch341_set_baudrate(struct usb_device *dev,
120 struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100121{
122 short a, b;
123 int r;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100124 unsigned long factor;
125 short divisor;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100126
Werner Cornelius664d5df2009-01-16 21:02:41 +0100127 if (!priv->baud_rate)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100128 return -EINVAL;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100129 factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
130 divisor = CH341_BAUDBASE_DIVMAX;
131
132 while ((factor > 0xfff0) && divisor) {
133 factor >>= 3;
134 divisor--;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100135 }
136
Werner Cornelius664d5df2009-01-16 21:02:41 +0100137 if (factor > 0xfff0)
138 return -EINVAL;
139
140 factor = 0x10000 - factor;
141 a = (factor & 0xff00) | divisor;
142 b = factor & 0xff;
143
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100144 r = ch341_control_out(dev, 0x9a, 0x1312, a);
145 if (!r)
146 r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
147
148 return r;
149}
150
Werner Cornelius664d5df2009-01-16 21:02:41 +0100151static int ch341_set_handshake(struct usb_device *dev, u8 control)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100152{
Werner Cornelius664d5df2009-01-16 21:02:41 +0100153 return ch341_control_out(dev, 0xa4, ~control, 0);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100154}
155
Werner Cornelius664d5df2009-01-16 21:02:41 +0100156static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100157{
158 char *buffer;
159 int r;
160 const unsigned size = 8;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100161 unsigned long flags;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100162
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100163 buffer = kmalloc(size, GFP_KERNEL);
164 if (!buffer)
165 return -ENOMEM;
166
167 r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
Alan Coxc4d0f8c2008-04-29 14:35:39 +0100168 if (r < 0)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100169 goto out;
170
Werner Cornelius664d5df2009-01-16 21:02:41 +0100171 /* setup the private status if available */
172 if (r == 2) {
173 r = 0;
174 spin_lock_irqsave(&priv->lock, flags);
175 priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100176 spin_unlock_irqrestore(&priv->lock, flags);
177 } else
178 r = -EPROTO;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100179
180out: kfree(buffer);
181 return r;
182}
183
184/* -------------------------------------------------------------------------- */
185
Adrian Bunk93b64972007-09-09 22:25:04 +0200186static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100187{
188 char *buffer;
189 int r;
190 const unsigned size = 8;
191
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100192 buffer = kmalloc(size, GFP_KERNEL);
193 if (!buffer)
194 return -ENOMEM;
195
196 /* expect two bytes 0x27 0x00 */
197 r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
198 if (r < 0)
199 goto out;
200
201 r = ch341_control_out(dev, 0xa1, 0, 0);
202 if (r < 0)
203 goto out;
204
205 r = ch341_set_baudrate(dev, priv);
206 if (r < 0)
207 goto out;
208
209 /* expect two bytes 0x56 0x00 */
210 r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
211 if (r < 0)
212 goto out;
213
214 r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
215 if (r < 0)
216 goto out;
217
218 /* expect 0xff 0xee */
Werner Cornelius664d5df2009-01-16 21:02:41 +0100219 r = ch341_get_status(dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100220 if (r < 0)
221 goto out;
222
223 r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
224 if (r < 0)
225 goto out;
226
227 r = ch341_set_baudrate(dev, priv);
228 if (r < 0)
229 goto out;
230
Werner Cornelius664d5df2009-01-16 21:02:41 +0100231 r = ch341_set_handshake(dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100232 if (r < 0)
233 goto out;
234
235 /* expect 0x9f 0xee */
Werner Cornelius664d5df2009-01-16 21:02:41 +0100236 r = ch341_get_status(dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100237
238out: kfree(buffer);
239 return r;
240}
241
Johan Hovold456c5be2012-10-25 10:29:03 +0200242static int ch341_port_probe(struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100243{
244 struct ch341_private *priv;
245 int r;
246
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100247 priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
248 if (!priv)
249 return -ENOMEM;
250
Werner Cornelius664d5df2009-01-16 21:02:41 +0100251 spin_lock_init(&priv->lock);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100252 priv->baud_rate = DEFAULT_BAUD_RATE;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100253 priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100254
Johan Hovold456c5be2012-10-25 10:29:03 +0200255 r = ch341_configure(port->serial->dev, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100256 if (r < 0)
257 goto error;
258
Johan Hovold456c5be2012-10-25 10:29:03 +0200259 usb_set_serial_port_data(port, priv);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100260 return 0;
261
262error: kfree(priv);
263 return r;
264}
265
Johan Hovold456c5be2012-10-25 10:29:03 +0200266static int ch341_port_remove(struct usb_serial_port *port)
267{
268 struct ch341_private *priv;
269
270 priv = usb_get_serial_port_data(port);
271 kfree(priv);
272
273 return 0;
274}
275
Alan Cox335f8512009-06-11 12:26:29 +0100276static int ch341_carrier_raised(struct usb_serial_port *port)
277{
278 struct ch341_private *priv = usb_get_serial_port_data(port);
279 if (priv->line_status & CH341_BIT_DCD)
280 return 1;
281 return 0;
282}
283
284static void ch341_dtr_rts(struct usb_serial_port *port, int on)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100285{
286 struct ch341_private *priv = usb_get_serial_port_data(port);
287 unsigned long flags;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100288
Alan Cox335f8512009-06-11 12:26:29 +0100289 /* drop DTR and RTS */
290 spin_lock_irqsave(&priv->lock, flags);
291 if (on)
292 priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
293 else
294 priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
295 spin_unlock_irqrestore(&priv->lock, flags);
296 ch341_set_handshake(port->serial->dev, priv->line_control);
Alan Cox335f8512009-06-11 12:26:29 +0100297}
298
299static void ch341_close(struct usb_serial_port *port)
300{
Johan Hovoldf26788d2010-03-17 23:00:45 +0100301 usb_serial_generic_close(port);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100302 usb_kill_urb(port->interrupt_in_urb);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100303}
304
305
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100306/* open this device, set default parameters */
Alan Coxa509a7e2009-09-19 13:13:26 -0700307static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100308{
309 struct usb_serial *serial = port->serial;
Johan Hovold456c5be2012-10-25 10:29:03 +0200310 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100311 int r;
312
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100313 priv->baud_rate = DEFAULT_BAUD_RATE;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100314
315 r = ch341_configure(serial->dev, priv);
316 if (r)
317 goto out;
318
Werner Cornelius664d5df2009-01-16 21:02:41 +0100319 r = ch341_set_handshake(serial->dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100320 if (r)
321 goto out;
322
323 r = ch341_set_baudrate(serial->dev, priv);
324 if (r)
325 goto out;
326
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700327 dev_dbg(&port->dev, "%s - submitting interrupt urb", __func__);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100328 r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
329 if (r) {
330 dev_err(&port->dev, "%s - failed submitting interrupt urb,"
331 " error %d\n", __func__, r);
Alan Cox335f8512009-06-11 12:26:29 +0100332 ch341_close(port);
Johan Hovold06946a62011-11-10 14:58:28 +0100333 goto out;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100334 }
335
Alan Coxa509a7e2009-09-19 13:13:26 -0700336 r = usb_serial_generic_open(tty, port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100337
338out: return r;
339}
340
341/* Old_termios contains the original termios settings and
342 * tty->termios contains the new setting to be used.
343 */
Alan Cox95da3102008-07-22 11:09:07 +0100344static void ch341_set_termios(struct tty_struct *tty,
345 struct usb_serial_port *port, struct ktermios *old_termios)
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100346{
347 struct ch341_private *priv = usb_get_serial_port_data(port);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100348 unsigned baud_rate;
Werner Cornelius664d5df2009-01-16 21:02:41 +0100349 unsigned long flags;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100350
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100351 baud_rate = tty_get_baud_rate(tty);
352
Werner Cornelius664d5df2009-01-16 21:02:41 +0100353 priv->baud_rate = baud_rate;
354
355 if (baud_rate) {
356 spin_lock_irqsave(&priv->lock, flags);
357 priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
358 spin_unlock_irqrestore(&priv->lock, flags);
359 ch341_set_baudrate(port->serial->dev, priv);
360 } else {
361 spin_lock_irqsave(&priv->lock, flags);
362 priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
363 spin_unlock_irqrestore(&priv->lock, flags);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100364 }
365
Werner Cornelius664d5df2009-01-16 21:02:41 +0100366 ch341_set_handshake(port->serial->dev, priv->line_control);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100367
368 /* Unimplemented:
369 * (cflag & CSIZE) : data bits [5, 8]
370 * (cflag & PARENB) : parity {NONE, EVEN, ODD}
371 * (cflag & CSTOPB) : stop bits [1, 2]
372 */
Werner Cornelius664d5df2009-01-16 21:02:41 +0100373}
Alan Cox73f59302007-10-18 01:24:18 -0700374
Tim Small492896f2009-08-17 13:21:57 +0100375static void ch341_break_ctl(struct tty_struct *tty, int break_state)
376{
377 const uint16_t ch341_break_reg =
378 CH341_REG_BREAK1 | ((uint16_t) CH341_REG_BREAK2 << 8);
379 struct usb_serial_port *port = tty->driver_data;
380 int r;
381 uint16_t reg_contents;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100382 uint8_t *break_reg;
Tim Small492896f2009-08-17 13:21:57 +0100383
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100384 break_reg = kmalloc(2, GFP_KERNEL);
Johan Hovold10c642d2013-12-29 19:22:56 +0100385 if (!break_reg)
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100386 return;
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100387
Tim Small492896f2009-08-17 13:21:57 +0100388 r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100389 ch341_break_reg, 0, break_reg, 2);
Tim Small492896f2009-08-17 13:21:57 +0100390 if (r < 0) {
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100391 dev_err(&port->dev, "%s - USB control read error (%d)\n",
392 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100393 goto out;
Tim Small492896f2009-08-17 13:21:57 +0100394 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700395 dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
396 __func__, break_reg[0], break_reg[1]);
Tim Small492896f2009-08-17 13:21:57 +0100397 if (break_state != 0) {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700398 dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
Tim Small492896f2009-08-17 13:21:57 +0100399 break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
400 break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
401 } else {
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700402 dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
Tim Small492896f2009-08-17 13:21:57 +0100403 break_reg[0] |= CH341_NBREAK_BITS_REG1;
404 break_reg[1] |= CH341_NBREAK_BITS_REG2;
405 }
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700406 dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
407 __func__, break_reg[0], break_reg[1]);
Johan Hovold5be796f2009-12-31 16:47:59 +0100408 reg_contents = get_unaligned_le16(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100409 r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
410 ch341_break_reg, reg_contents);
411 if (r < 0)
Johan Hovold6a9b15f2009-12-28 23:01:45 +0100412 dev_err(&port->dev, "%s - USB control write error (%d)\n",
413 __func__, r);
Johan Hovoldf2b5cc82009-12-28 23:01:46 +0100414out:
415 kfree(break_reg);
Tim Small492896f2009-08-17 13:21:57 +0100416}
417
Alan Cox20b9d172011-02-14 16:26:50 +0000418static int ch341_tiocmset(struct tty_struct *tty,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100419 unsigned int set, unsigned int clear)
420{
421 struct usb_serial_port *port = tty->driver_data;
422 struct ch341_private *priv = usb_get_serial_port_data(port);
423 unsigned long flags;
424 u8 control;
425
426 spin_lock_irqsave(&priv->lock, flags);
427 if (set & TIOCM_RTS)
428 priv->line_control |= CH341_BIT_RTS;
429 if (set & TIOCM_DTR)
430 priv->line_control |= CH341_BIT_DTR;
431 if (clear & TIOCM_RTS)
432 priv->line_control &= ~CH341_BIT_RTS;
433 if (clear & TIOCM_DTR)
434 priv->line_control &= ~CH341_BIT_DTR;
435 control = priv->line_control;
436 spin_unlock_irqrestore(&priv->lock, flags);
437
438 return ch341_set_handshake(port->serial->dev, control);
439}
440
Johan Hovoldac035622014-01-02 22:49:28 +0100441static void ch341_update_line_status(struct usb_serial_port *port,
442 unsigned char *data, size_t len)
443{
444 struct ch341_private *priv = usb_get_serial_port_data(port);
Johan Hovoldb7700812014-01-02 22:49:29 +0100445 struct tty_struct *tty;
Johan Hovoldac035622014-01-02 22:49:28 +0100446 unsigned long flags;
Johan Hovoldb7700812014-01-02 22:49:29 +0100447 u8 status;
448 u8 delta;
Johan Hovoldac035622014-01-02 22:49:28 +0100449
450 if (len < 4)
451 return;
452
Johan Hovoldb7700812014-01-02 22:49:29 +0100453 status = ~data[2] & CH341_BITS_MODEM_STAT;
454
Johan Hovoldac035622014-01-02 22:49:28 +0100455 spin_lock_irqsave(&priv->lock, flags);
Johan Hovoldb7700812014-01-02 22:49:29 +0100456 delta = status ^ priv->line_status;
457 priv->line_status = status;
Johan Hovoldac035622014-01-02 22:49:28 +0100458 spin_unlock_irqrestore(&priv->lock, flags);
459
Johan Hovoldfd74b0b2014-01-02 22:49:30 +0100460 if (data[1] & CH341_MULT_STAT)
461 dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
462
Johan Hovoldd984fe92014-01-02 22:49:31 +0100463 if (!delta)
464 return;
465
Johan Hovold5e409a22014-01-02 22:49:32 +0100466 if (delta & CH341_BIT_CTS)
467 port->icount.cts++;
468 if (delta & CH341_BIT_DSR)
469 port->icount.dsr++;
470 if (delta & CH341_BIT_RI)
471 port->icount.rng++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100472 if (delta & CH341_BIT_DCD) {
Johan Hovold5e409a22014-01-02 22:49:32 +0100473 port->icount.dcd++;
Johan Hovoldb7700812014-01-02 22:49:29 +0100474 tty = tty_port_tty_get(&port->port);
475 if (tty) {
Johan Hovoldac035622014-01-02 22:49:28 +0100476 usb_serial_handle_dcd_change(port, tty,
Johan Hovoldb7700812014-01-02 22:49:29 +0100477 status & CH341_BIT_DCD);
478 tty_kref_put(tty);
479 }
Johan Hovoldac035622014-01-02 22:49:28 +0100480 }
481
482 wake_up_interruptible(&port->port.delta_msr_wait);
483}
484
Werner Cornelius664d5df2009-01-16 21:02:41 +0100485static void ch341_read_int_callback(struct urb *urb)
486{
487 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
488 unsigned char *data = urb->transfer_buffer;
489 unsigned int actual_length = urb->actual_length;
490 int status;
491
Werner Cornelius664d5df2009-01-16 21:02:41 +0100492 switch (urb->status) {
493 case 0:
494 /* success */
495 break;
496 case -ECONNRESET:
497 case -ENOENT:
498 case -ESHUTDOWN:
499 /* this urb is terminated, clean up */
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700500 dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
501 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100502 return;
503 default:
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700504 dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
505 __func__, urb->status);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100506 goto exit;
507 }
508
Greg Kroah-Hartman59d33f22012-09-18 09:58:57 +0100509 usb_serial_debug_data(&port->dev, __func__,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100510 urb->actual_length, urb->transfer_buffer);
Johan Hovoldac035622014-01-02 22:49:28 +0100511 ch341_update_line_status(port, data, actual_length);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100512exit:
513 status = usb_submit_urb(urb, GFP_ATOMIC);
514 if (status)
515 dev_err(&urb->dev->dev,
516 "%s - usb_submit_urb failed with result %d\n",
517 __func__, status);
518}
519
Alan Cox60b33c12011-02-14 16:26:14 +0000520static int ch341_tiocmget(struct tty_struct *tty)
Werner Cornelius664d5df2009-01-16 21:02:41 +0100521{
522 struct usb_serial_port *port = tty->driver_data;
523 struct ch341_private *priv = usb_get_serial_port_data(port);
524 unsigned long flags;
525 u8 mcr;
526 u8 status;
527 unsigned int result;
528
Werner Cornelius664d5df2009-01-16 21:02:41 +0100529 spin_lock_irqsave(&priv->lock, flags);
530 mcr = priv->line_control;
531 status = priv->line_status;
532 spin_unlock_irqrestore(&priv->lock, flags);
533
534 result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
535 | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
536 | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
537 | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
538 | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
539 | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
540
Greg Kroah-Hartman79cbeea2012-09-13 17:18:10 -0700541 dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
Werner Cornelius664d5df2009-01-16 21:02:41 +0100542
543 return result;
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100544}
545
Greg Kroah-Hartman622b80c2012-05-15 15:41:47 -0700546static int ch341_reset_resume(struct usb_serial *serial)
Ming Lei1ded7ea2009-02-20 21:23:09 +0800547{
Ming Lei1ded7ea2009-02-20 21:23:09 +0800548 struct ch341_private *priv;
549
Ming Lei1ded7ea2009-02-20 21:23:09 +0800550 priv = usb_get_serial_port_data(serial->port[0]);
551
Greg Kroah-Hartman2bfd1c92012-05-07 14:10:27 -0700552 /* reconfigure ch341 serial port after bus-reset */
553 ch341_configure(serial->dev, priv);
Ming Lei1ded7ea2009-02-20 21:23:09 +0800554
555 return 0;
556}
557
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100558static struct usb_serial_driver ch341_device = {
559 .driver = {
560 .owner = THIS_MODULE,
561 .name = "ch341-uart",
562 },
Werner Cornelius664d5df2009-01-16 21:02:41 +0100563 .id_table = id_table,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100564 .num_ports = 1,
565 .open = ch341_open,
Alan Cox335f8512009-06-11 12:26:29 +0100566 .dtr_rts = ch341_dtr_rts,
567 .carrier_raised = ch341_carrier_raised,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100568 .close = ch341_close,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100569 .set_termios = ch341_set_termios,
Tim Small492896f2009-08-17 13:21:57 +0100570 .break_ctl = ch341_break_ctl,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100571 .tiocmget = ch341_tiocmget,
572 .tiocmset = ch341_tiocmset,
Johan Hovold5e409a22014-01-02 22:49:32 +0100573 .tiocmiwait = usb_serial_generic_tiocmiwait,
Werner Cornelius664d5df2009-01-16 21:02:41 +0100574 .read_int_callback = ch341_read_int_callback,
Johan Hovold456c5be2012-10-25 10:29:03 +0200575 .port_probe = ch341_port_probe,
576 .port_remove = ch341_port_remove,
Greg Kroah-Hartman1c1eaba2012-05-16 08:36:13 -0700577 .reset_resume = ch341_reset_resume,
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100578};
579
Alan Stern08a4f6b2012-02-23 14:56:17 -0500580static struct usb_serial_driver * const serial_drivers[] = {
581 &ch341_device, NULL
582};
583
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700584module_usb_serial_driver(serial_drivers, id_table);
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100585
Frank A Kingswood6ce76102007-08-22 20:48:58 +0100586MODULE_LICENSE("GPL");