blob: 4664cf4cc7a8fdf81f752d8cf62d37cbfddf7334 [file] [log] [blame]
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -08001/*
2 * Fintek F81232 USB to serial adaptor driver
3 *
4 * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
5 * Copyright (C) 2012 Linux Foundation
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 */
12
13#include <linux/kernel.h>
14#include <linux/errno.h>
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080015#include <linux/slab.h>
16#include <linux/tty.h>
17#include <linux/tty_driver.h>
18#include <linux/tty_flip.h>
19#include <linux/serial.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/spinlock.h>
23#include <linux/uaccess.h>
24#include <linux/usb.h>
25#include <linux/usb/serial.h>
Peter Hung88850782015-03-17 17:48:20 +080026#include <linux/serial_reg.h>
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080027
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080028static const struct usb_device_id id_table[] = {
29 { USB_DEVICE(0x1934, 0x0706) },
30 { } /* Terminating entry */
31};
32MODULE_DEVICE_TABLE(usb, id_table);
33
34#define CONTROL_DTR 0x01
35#define CONTROL_RTS 0x02
36
37#define UART_STATE 0x08
38#define UART_STATE_TRANSIENT_MASK 0x74
39#define UART_DCD 0x01
40#define UART_DSR 0x02
41#define UART_BREAK_ERROR 0x04
42#define UART_RING 0x08
43#define UART_FRAME_ERROR 0x10
44#define UART_PARITY_ERROR 0x20
45#define UART_OVERRUN_ERROR 0x40
46#define UART_CTS 0x80
47
48struct f81232_private {
49 spinlock_t lock;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080050 u8 line_control;
Peter Hungb830d072015-03-17 17:48:19 +080051 u8 modem_status;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080052};
53
54static void f81232_update_line_status(struct usb_serial_port *port,
55 unsigned char *data,
56 unsigned int actual_length)
57{
Johan Hovold49fabf22013-12-29 19:22:57 +010058 /*
Johan Hovoldc50db822013-12-29 19:22:58 +010059 * FIXME: Update port->icount, and call
Johan Hovold49fabf22013-12-29 19:22:57 +010060 *
61 * wake_up_interruptible(&port->port.delta_msr_wait);
62 *
63 * on MSR changes.
64 */
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080065}
66
67static void f81232_read_int_callback(struct urb *urb)
68{
69 struct usb_serial_port *port = urb->context;
70 unsigned char *data = urb->transfer_buffer;
71 unsigned int actual_length = urb->actual_length;
72 int status = urb->status;
73 int retval;
74
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080075 switch (status) {
76 case 0:
77 /* success */
78 break;
79 case -ECONNRESET:
80 case -ENOENT:
81 case -ESHUTDOWN:
82 /* this urb is terminated, clean up */
Greg Kroah-Hartmana94e9b92012-05-15 16:27:17 -070083 dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
84 __func__, status);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080085 return;
86 default:
Greg Kroah-Hartmana94e9b92012-05-15 16:27:17 -070087 dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
88 __func__, status);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080089 goto exit;
90 }
91
Greg Kroah-Hartman59d33f22012-09-18 09:58:57 +010092 usb_serial_debug_data(&port->dev, __func__,
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -080093 urb->actual_length, urb->transfer_buffer);
94
95 f81232_update_line_status(port, data, actual_length);
96
97exit:
98 retval = usb_submit_urb(urb, GFP_ATOMIC);
99 if (retval)
100 dev_err(&urb->dev->dev,
101 "%s - usb_submit_urb failed with result %d\n",
102 __func__, retval);
103}
104
105static void f81232_process_read_urb(struct urb *urb)
106{
107 struct usb_serial_port *port = urb->context;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800108 unsigned char *data = urb->transfer_buffer;
Peter Hung88850782015-03-17 17:48:20 +0800109 char tty_flag;
110 unsigned int i;
111 u8 lsr;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800112
Peter Hung88850782015-03-17 17:48:20 +0800113 /*
114 * When opening the port we get a 1-byte packet with the current LSR,
115 * which we discard.
116 */
117 if ((urb->actual_length < 2) || (urb->actual_length % 2))
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800118 return;
119
Peter Hung88850782015-03-17 17:48:20 +0800120 /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800121
Peter Hung88850782015-03-17 17:48:20 +0800122 for (i = 0; i < urb->actual_length; i += 2) {
123 tty_flag = TTY_NORMAL;
124 lsr = data[i];
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800125
Peter Hung88850782015-03-17 17:48:20 +0800126 if (lsr & UART_LSR_BRK_ERROR_BITS) {
127 if (lsr & UART_LSR_BI) {
128 tty_flag = TTY_BREAK;
129 port->icount.brk++;
130 usb_serial_handle_break(port);
131 } else if (lsr & UART_LSR_PE) {
132 tty_flag = TTY_PARITY;
133 port->icount.parity++;
134 } else if (lsr & UART_LSR_FE) {
135 tty_flag = TTY_FRAME;
136 port->icount.frame++;
137 }
138
139 if (lsr & UART_LSR_OE) {
140 port->icount.overrun++;
141 tty_insert_flip_char(&port->port, 0,
142 TTY_OVERRUN);
143 }
144 }
145
146 if (port->port.console && port->sysrq) {
147 if (usb_serial_handle_sysrq_char(port, data[i + 1]))
148 continue;
149 }
150
151 tty_insert_flip_char(&port->port, data[i + 1], tty_flag);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800152 }
153
Jiri Slaby2e124b42013-01-03 15:53:06 +0100154 tty_flip_buffer_push(&port->port);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800155}
156
157static int set_control_lines(struct usb_device *dev, u8 value)
158{
159 /* FIXME - Stubbed out for now */
160 return 0;
161}
162
163static void f81232_break_ctl(struct tty_struct *tty, int break_state)
164{
165 /* FIXME - Stubbed out for now */
166
167 /*
168 * break_state = -1 to turn on break, and 0 to turn off break
169 * see drivers/char/tty_io.c to see it used.
170 * last_set_data_urb_value NEVER has the break bit set in it.
171 */
172}
173
174static void f81232_set_termios(struct tty_struct *tty,
175 struct usb_serial_port *port, struct ktermios *old_termios)
176{
177 /* FIXME - Stubbed out for now */
178
179 /* Don't change anything if nothing has changed */
Johan Hovold21886722013-06-10 18:29:37 +0200180 if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800181 return;
182
183 /* Do the real work here... */
Johan Hovold21886722013-06-10 18:29:37 +0200184 if (old_termios)
185 tty_termios_copy_hw(&tty->termios, old_termios);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800186}
187
188static int f81232_tiocmget(struct tty_struct *tty)
189{
190 /* FIXME - Stubbed out for now */
191 return 0;
192}
193
194static int f81232_tiocmset(struct tty_struct *tty,
195 unsigned int set, unsigned int clear)
196{
197 /* FIXME - Stubbed out for now */
198 return 0;
199}
200
201static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
202{
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800203 int result;
204
205 /* Setup termios */
206 if (tty)
Johan Hovold21886722013-06-10 18:29:37 +0200207 f81232_set_termios(tty, port, NULL);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800208
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800209 result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
210 if (result) {
211 dev_err(&port->dev, "%s - failed submitting interrupt urb,"
212 " error %d\n", __func__, result);
213 return result;
214 }
215
216 result = usb_serial_generic_open(tty, port);
217 if (result) {
218 usb_kill_urb(port->interrupt_in_urb);
219 return result;
220 }
221
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800222 return 0;
223}
224
225static void f81232_close(struct usb_serial_port *port)
226{
227 usb_serial_generic_close(port);
228 usb_kill_urb(port->interrupt_in_urb);
229}
230
231static void f81232_dtr_rts(struct usb_serial_port *port, int on)
232{
233 struct f81232_private *priv = usb_get_serial_port_data(port);
234 unsigned long flags;
235 u8 control;
236
237 spin_lock_irqsave(&priv->lock, flags);
238 /* Change DTR and RTS */
239 if (on)
240 priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
241 else
242 priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
243 control = priv->line_control;
244 spin_unlock_irqrestore(&priv->lock, flags);
245 set_control_lines(port->serial->dev, control);
246}
247
248static int f81232_carrier_raised(struct usb_serial_port *port)
249{
250 struct f81232_private *priv = usb_get_serial_port_data(port);
Peter Hungb830d072015-03-17 17:48:19 +0800251 if (priv->modem_status & UART_DCD)
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800252 return 1;
253 return 0;
254}
255
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800256static int f81232_ioctl(struct tty_struct *tty,
257 unsigned int cmd, unsigned long arg)
258{
259 struct serial_struct ser;
260 struct usb_serial_port *port = tty->driver_data;
Greg Kroah-Hartmana94e9b92012-05-15 16:27:17 -0700261
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800262 switch (cmd) {
263 case TIOCGSERIAL:
264 memset(&ser, 0, sizeof ser);
265 ser.type = PORT_16654;
Greg Kroah-Hartmane5b1e202013-06-07 11:04:28 -0700266 ser.line = port->minor;
Greg Kroah-Hartman11438322013-06-06 10:32:00 -0700267 ser.port = port->port_number;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800268 ser.baud_base = 460800;
269
270 if (copy_to_user((void __user *)arg, &ser, sizeof ser))
271 return -EFAULT;
272
273 return 0;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800274 default:
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800275 break;
276 }
277 return -ENOIOCTLCMD;
278}
279
Johan Hovold3124d1d2012-10-17 13:34:56 +0200280static int f81232_port_probe(struct usb_serial_port *port)
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800281{
282 struct f81232_private *priv;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800283
Johan Hovold3124d1d2012-10-17 13:34:56 +0200284 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
285 if (!priv)
286 return -ENOMEM;
287
288 spin_lock_init(&priv->lock);
Johan Hovold3124d1d2012-10-17 13:34:56 +0200289
290 usb_set_serial_port_data(port, priv);
291
Johan Hovoldd7be6222013-06-26 16:47:23 +0200292 port->port.drain_delay = 256;
293
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800294 return 0;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800295}
296
Johan Hovold3124d1d2012-10-17 13:34:56 +0200297static int f81232_port_remove(struct usb_serial_port *port)
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800298{
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800299 struct f81232_private *priv;
300
Johan Hovold3124d1d2012-10-17 13:34:56 +0200301 priv = usb_get_serial_port_data(port);
302 kfree(priv);
303
304 return 0;
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800305}
306
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800307static struct usb_serial_driver f81232_device = {
308 .driver = {
309 .owner = THIS_MODULE,
310 .name = "f81232",
311 },
312 .id_table = id_table,
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800313 .num_ports = 1,
314 .bulk_in_size = 256,
315 .bulk_out_size = 256,
316 .open = f81232_open,
317 .close = f81232_close,
318 .dtr_rts = f81232_dtr_rts,
319 .carrier_raised = f81232_carrier_raised,
320 .ioctl = f81232_ioctl,
321 .break_ctl = f81232_break_ctl,
322 .set_termios = f81232_set_termios,
323 .tiocmget = f81232_tiocmget,
324 .tiocmset = f81232_tiocmset,
Johan Hovoldc50db822013-12-29 19:22:58 +0100325 .tiocmiwait = usb_serial_generic_tiocmiwait,
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800326 .process_read_urb = f81232_process_read_urb,
327 .read_int_callback = f81232_read_int_callback,
Johan Hovold3124d1d2012-10-17 13:34:56 +0200328 .port_probe = f81232_port_probe,
329 .port_remove = f81232_port_remove,
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800330};
331
332static struct usb_serial_driver * const serial_drivers[] = {
333 &f81232_device,
334 NULL,
335};
336
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700337module_usb_serial_driver(serial_drivers, id_table);
Greg Kroah-Hartmanaac1fc32012-02-28 13:36:35 -0800338
339MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
340MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
341MODULE_LICENSE("GPL v2");