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