f81232.c revision e5b1e2062e0535e8ffef79bb34d857e21380d101
1/*
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>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/tty.h>
18#include <linux/tty_driver.h>
19#include <linux/tty_flip.h>
20#include <linux/serial.h>
21#include <linux/module.h>
22#include <linux/moduleparam.h>
23#include <linux/spinlock.h>
24#include <linux/uaccess.h>
25#include <linux/usb.h>
26#include <linux/usb/serial.h>
27
28static 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;
50	u8 line_control;
51	u8 line_status;
52};
53
54static void f81232_update_line_status(struct usb_serial_port *port,
55				      unsigned char *data,
56				      unsigned int actual_length)
57{
58}
59
60static void f81232_read_int_callback(struct urb *urb)
61{
62	struct usb_serial_port *port =  urb->context;
63	unsigned char *data = urb->transfer_buffer;
64	unsigned int actual_length = urb->actual_length;
65	int status = urb->status;
66	int retval;
67
68	switch (status) {
69	case 0:
70		/* success */
71		break;
72	case -ECONNRESET:
73	case -ENOENT:
74	case -ESHUTDOWN:
75		/* this urb is terminated, clean up */
76		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
77			__func__, status);
78		return;
79	default:
80		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
81			__func__, status);
82		goto exit;
83	}
84
85	usb_serial_debug_data(&port->dev, __func__,
86			      urb->actual_length, urb->transfer_buffer);
87
88	f81232_update_line_status(port, data, actual_length);
89
90exit:
91	retval = usb_submit_urb(urb, GFP_ATOMIC);
92	if (retval)
93		dev_err(&urb->dev->dev,
94			"%s - usb_submit_urb failed with result %d\n",
95			__func__, retval);
96}
97
98static void f81232_process_read_urb(struct urb *urb)
99{
100	struct usb_serial_port *port = urb->context;
101	struct f81232_private *priv = usb_get_serial_port_data(port);
102	unsigned char *data = urb->transfer_buffer;
103	char tty_flag = TTY_NORMAL;
104	unsigned long flags;
105	u8 line_status;
106	int i;
107
108	/* update line status */
109	spin_lock_irqsave(&priv->lock, flags);
110	line_status = priv->line_status;
111	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
112	spin_unlock_irqrestore(&priv->lock, flags);
113	wake_up_interruptible(&port->port.delta_msr_wait);
114
115	if (!urb->actual_length)
116		return;
117
118	/* break takes precedence over parity, */
119	/* which takes precedence over framing errors */
120	if (line_status & UART_BREAK_ERROR)
121		tty_flag = TTY_BREAK;
122	else if (line_status & UART_PARITY_ERROR)
123		tty_flag = TTY_PARITY;
124	else if (line_status & UART_FRAME_ERROR)
125		tty_flag = TTY_FRAME;
126	dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__, tty_flag);
127
128	/* overrun is special, not associated with a char */
129	if (line_status & UART_OVERRUN_ERROR)
130		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
131
132	if (port->port.console && port->sysrq) {
133		for (i = 0; i < urb->actual_length; ++i)
134			if (!usb_serial_handle_sysrq_char(port, data[i]))
135				tty_insert_flip_char(&port->port, data[i],
136						tty_flag);
137	} else {
138		tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
139							urb->actual_length);
140	}
141
142	tty_flip_buffer_push(&port->port);
143}
144
145static int set_control_lines(struct usb_device *dev, u8 value)
146{
147	/* FIXME - Stubbed out for now */
148	return 0;
149}
150
151static void f81232_break_ctl(struct tty_struct *tty, int break_state)
152{
153	/* FIXME - Stubbed out for now */
154
155	/*
156	 * break_state = -1 to turn on break, and 0 to turn off break
157	 * see drivers/char/tty_io.c to see it used.
158	 * last_set_data_urb_value NEVER has the break bit set in it.
159	 */
160}
161
162static void f81232_set_termios(struct tty_struct *tty,
163		struct usb_serial_port *port, struct ktermios *old_termios)
164{
165	/* FIXME - Stubbed out for now */
166
167	/* Don't change anything if nothing has changed */
168	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
169		return;
170
171	/* Do the real work here... */
172	if (old_termios)
173		tty_termios_copy_hw(&tty->termios, old_termios);
174}
175
176static int f81232_tiocmget(struct tty_struct *tty)
177{
178	/* FIXME - Stubbed out for now */
179	return 0;
180}
181
182static int f81232_tiocmset(struct tty_struct *tty,
183			unsigned int set, unsigned int clear)
184{
185	/* FIXME - Stubbed out for now */
186	return 0;
187}
188
189static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
190{
191	int result;
192
193	/* Setup termios */
194	if (tty)
195		f81232_set_termios(tty, port, NULL);
196
197	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
198	if (result) {
199		dev_err(&port->dev, "%s - failed submitting interrupt urb,"
200			" error %d\n", __func__, result);
201		return result;
202	}
203
204	result = usb_serial_generic_open(tty, port);
205	if (result) {
206		usb_kill_urb(port->interrupt_in_urb);
207		return result;
208	}
209
210	port->port.drain_delay = 256;
211	return 0;
212}
213
214static void f81232_close(struct usb_serial_port *port)
215{
216	usb_serial_generic_close(port);
217	usb_kill_urb(port->interrupt_in_urb);
218}
219
220static void f81232_dtr_rts(struct usb_serial_port *port, int on)
221{
222	struct f81232_private *priv = usb_get_serial_port_data(port);
223	unsigned long flags;
224	u8 control;
225
226	spin_lock_irqsave(&priv->lock, flags);
227	/* Change DTR and RTS */
228	if (on)
229		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
230	else
231		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
232	control = priv->line_control;
233	spin_unlock_irqrestore(&priv->lock, flags);
234	set_control_lines(port->serial->dev, control);
235}
236
237static int f81232_carrier_raised(struct usb_serial_port *port)
238{
239	struct f81232_private *priv = usb_get_serial_port_data(port);
240	if (priv->line_status & UART_DCD)
241		return 1;
242	return 0;
243}
244
245static int f81232_tiocmiwait(struct tty_struct *tty, unsigned long arg)
246{
247	struct usb_serial_port *port = tty->driver_data;
248	struct f81232_private *priv = usb_get_serial_port_data(port);
249	unsigned long flags;
250	unsigned int prevstatus;
251	unsigned int status;
252	unsigned int changed;
253
254	spin_lock_irqsave(&priv->lock, flags);
255	prevstatus = priv->line_status;
256	spin_unlock_irqrestore(&priv->lock, flags);
257
258	while (1) {
259		interruptible_sleep_on(&port->port.delta_msr_wait);
260		/* see if a signal did it */
261		if (signal_pending(current))
262			return -ERESTARTSYS;
263
264		if (port->serial->disconnected)
265			return -EIO;
266
267		spin_lock_irqsave(&priv->lock, flags);
268		status = priv->line_status;
269		spin_unlock_irqrestore(&priv->lock, flags);
270
271		changed = prevstatus ^ status;
272
273		if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
274		    ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
275		    ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
276		    ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
277			return 0;
278		}
279		prevstatus = status;
280	}
281	/* NOTREACHED */
282	return 0;
283}
284
285static int f81232_ioctl(struct tty_struct *tty,
286			unsigned int cmd, unsigned long arg)
287{
288	struct serial_struct ser;
289	struct usb_serial_port *port = tty->driver_data;
290
291	dev_dbg(&port->dev, "%s cmd = 0x%04x\n", __func__, cmd);
292
293	switch (cmd) {
294	case TIOCGSERIAL:
295		memset(&ser, 0, sizeof ser);
296		ser.type = PORT_16654;
297		ser.line = port->minor;
298		ser.port = port->port_number;
299		ser.baud_base = 460800;
300
301		if (copy_to_user((void __user *)arg, &ser, sizeof ser))
302			return -EFAULT;
303
304		return 0;
305	default:
306		dev_dbg(&port->dev, "%s not supported = 0x%04x\n",
307			__func__, cmd);
308		break;
309	}
310	return -ENOIOCTLCMD;
311}
312
313static int f81232_port_probe(struct usb_serial_port *port)
314{
315	struct f81232_private *priv;
316
317	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
318	if (!priv)
319		return -ENOMEM;
320
321	spin_lock_init(&priv->lock);
322
323	usb_set_serial_port_data(port, priv);
324
325	return 0;
326}
327
328static int f81232_port_remove(struct usb_serial_port *port)
329{
330	struct f81232_private *priv;
331
332	priv = usb_get_serial_port_data(port);
333	kfree(priv);
334
335	return 0;
336}
337
338static struct usb_serial_driver f81232_device = {
339	.driver = {
340		.owner =	THIS_MODULE,
341		.name =		"f81232",
342	},
343	.id_table =		id_table,
344	.num_ports =		1,
345	.bulk_in_size =		256,
346	.bulk_out_size =	256,
347	.open =			f81232_open,
348	.close =		f81232_close,
349	.dtr_rts = 		f81232_dtr_rts,
350	.carrier_raised =	f81232_carrier_raised,
351	.ioctl =		f81232_ioctl,
352	.break_ctl =		f81232_break_ctl,
353	.set_termios =		f81232_set_termios,
354	.tiocmget =		f81232_tiocmget,
355	.tiocmset =		f81232_tiocmset,
356	.tiocmiwait =		f81232_tiocmiwait,
357	.process_read_urb =	f81232_process_read_urb,
358	.read_int_callback =	f81232_read_int_callback,
359	.port_probe =		f81232_port_probe,
360	.port_remove =		f81232_port_remove,
361};
362
363static struct usb_serial_driver * const serial_drivers[] = {
364	&f81232_device,
365	NULL,
366};
367
368module_usb_serial_driver(serial_drivers, id_table);
369
370MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
371MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
372MODULE_LICENSE("GPL v2");
373