aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-02-28 16:36:35 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-02-28 16:36:35 -0500
commitaac1fc386fa13f9f450909fcfb02e73db55f0c0f (patch)
tree5e696ff4cefd52cc3a2158af43572288e7de4bdb /drivers/usb/serial
parentdba607f9f0aebfa8f29b3eb911250aa19d5d71a8 (diff)
USB: serial: add Fintek F81232 usb to serial driver
This is the first cut at a driver for the Fintek F81232 USB to serial single port converter. This provides the initial framework for the device, and some data can move through it, but no line settings are handled, so it's not that useful yet. It does give people a starting place to work from. Thank to Fintek for providing samples and specifications, without which, this driver would have never been able to be written. Cc: Amanda Ying <Amanda_Ying@fintek.com.tw> Cc: Tom Tsai <Tom_Tsai@fintek.com.tw> Cc: <linux-usb@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/Kconfig9
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/f81232.c405
3 files changed, 415 insertions, 0 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 677f577c0243..434df7f2a4bd 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -238,6 +238,15 @@ config USB_SERIAL_EDGEPORT_TI
238 To compile this driver as a module, choose M here: the 238 To compile this driver as a module, choose M here: the
239 module will be called io_ti. 239 module will be called io_ti.
240 240
241config USB_SERIAL_F81232
242 tristate "USB Fintek F81232 Single Port Serial Driver"
243 help
244 Say Y here if you want to use the Fintek F81232 single
245 port usb to serial adapter.
246
247 To compile this driver as a module, choose M here: the
248 module will be called f81232.
249
241config USB_SERIAL_GARMIN 250config USB_SERIAL_GARMIN
242 tristate "USB Garmin GPS driver" 251 tristate "USB Garmin GPS driver"
243 help 252 help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 9e536eefb32c..5ac438b40270 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o
23obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o 23obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o
24obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o 24obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o
25obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o 25obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o
26obj-$(CONFIG_USB_SERIAL_F81232) += f81232.o
26obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o 27obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
27obj-$(CONFIG_USB_SERIAL_FUNSOFT) += funsoft.o 28obj-$(CONFIG_USB_SERIAL_FUNSOFT) += funsoft.o
28obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o 29obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
new file mode 100644
index 000000000000..88c0b1963920
--- /dev/null
+++ b/drivers/usb/serial/f81232.c
@@ -0,0 +1,405 @@
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 bool debug;
29
30static const struct usb_device_id id_table[] = {
31 { USB_DEVICE(0x1934, 0x0706) },
32 { } /* Terminating entry */
33};
34MODULE_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
50struct f81232_private {
51 spinlock_t lock;
52 wait_queue_head_t delta_msr_wait;
53 u8 line_control;
54 u8 line_status;
55};
56
57static void f81232_update_line_status(struct usb_serial_port *port,
58 unsigned char *data,
59 unsigned int actual_length)
60{
61}
62
63static 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 dbg("%s (%d)", __func__, port->number);
72
73 switch (status) {
74 case 0:
75 /* success */
76 break;
77 case -ECONNRESET:
78 case -ENOENT:
79 case -ESHUTDOWN:
80 /* this urb is terminated, clean up */
81 dbg("%s - urb shutting down with status: %d", __func__,
82 status);
83 return;
84 default:
85 dbg("%s - nonzero urb status received: %d", __func__,
86 status);
87 goto exit;
88 }
89
90 usb_serial_debug_data(debug, &port->dev, __func__,
91 urb->actual_length, urb->transfer_buffer);
92
93 f81232_update_line_status(port, data, actual_length);
94
95exit:
96 retval = usb_submit_urb(urb, GFP_ATOMIC);
97 if (retval)
98 dev_err(&urb->dev->dev,
99 "%s - usb_submit_urb failed with result %d\n",
100 __func__, retval);
101}
102
103static void f81232_process_read_urb(struct urb *urb)
104{
105 struct usb_serial_port *port = urb->context;
106 struct f81232_private *priv = usb_get_serial_port_data(port);
107 struct tty_struct *tty;
108 unsigned char *data = urb->transfer_buffer;
109 char tty_flag = TTY_NORMAL;
110 unsigned long flags;
111 u8 line_status;
112 int i;
113
114 /* update line status */
115 spin_lock_irqsave(&priv->lock, flags);
116 line_status = priv->line_status;
117 priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
118 spin_unlock_irqrestore(&priv->lock, flags);
119 wake_up_interruptible(&priv->delta_msr_wait);
120
121 if (!urb->actual_length)
122 return;
123
124 tty = tty_port_tty_get(&port->port);
125 if (!tty)
126 return;
127
128 /* break takes precedence over parity, */
129 /* which takes precedence over framing errors */
130 if (line_status & UART_BREAK_ERROR)
131 tty_flag = TTY_BREAK;
132 else if (line_status & UART_PARITY_ERROR)
133 tty_flag = TTY_PARITY;
134 else if (line_status & UART_FRAME_ERROR)
135 tty_flag = TTY_FRAME;
136 dbg("%s - tty_flag = %d", __func__, tty_flag);
137
138 /* overrun is special, not associated with a char */
139 if (line_status & UART_OVERRUN_ERROR)
140 tty_insert_flip_char(tty, 0, TTY_OVERRUN);
141
142 if (port->port.console && port->sysrq) {
143 for (i = 0; i < urb->actual_length; ++i)
144 if (!usb_serial_handle_sysrq_char(port, data[i]))
145 tty_insert_flip_char(tty, data[i], tty_flag);
146 } else {
147 tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
148 urb->actual_length);
149 }
150
151 tty_flip_buffer_push(tty);
152 tty_kref_put(tty);
153}
154
155static int set_control_lines(struct usb_device *dev, u8 value)
156{
157 /* FIXME - Stubbed out for now */
158 return 0;
159}
160
161static void f81232_break_ctl(struct tty_struct *tty, int break_state)
162{
163 /* FIXME - Stubbed out for now */
164
165 /*
166 * break_state = -1 to turn on break, and 0 to turn off break
167 * see drivers/char/tty_io.c to see it used.
168 * last_set_data_urb_value NEVER has the break bit set in it.
169 */
170}
171
172static void f81232_set_termios(struct tty_struct *tty,
173 struct usb_serial_port *port, struct ktermios *old_termios)
174{
175 /* FIXME - Stubbed out for now */
176
177 /* Don't change anything if nothing has changed */
178 if (!tty_termios_hw_change(tty->termios, old_termios))
179 return;
180
181 /* Do the real work here... */
182}
183
184static int f81232_tiocmget(struct tty_struct *tty)
185{
186 /* FIXME - Stubbed out for now */
187 return 0;
188}
189
190static int f81232_tiocmset(struct tty_struct *tty,
191 unsigned int set, unsigned int clear)
192{
193 /* FIXME - Stubbed out for now */
194 return 0;
195}
196
197static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
198{
199 struct ktermios tmp_termios;
200 int result;
201
202 /* Setup termios */
203 if (tty)
204 f81232_set_termios(tty, port, &tmp_termios);
205
206 dbg("%s - submitting interrupt urb", __func__);
207 result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
208 if (result) {
209 dev_err(&port->dev, "%s - failed submitting interrupt urb,"
210 " error %d\n", __func__, result);
211 return result;
212 }
213
214 result = usb_serial_generic_open(tty, port);
215 if (result) {
216 usb_kill_urb(port->interrupt_in_urb);
217 return result;
218 }
219
220 port->port.drain_delay = 256;
221 return 0;
222}
223
224static void f81232_close(struct usb_serial_port *port)
225{
226 usb_serial_generic_close(port);
227 usb_kill_urb(port->interrupt_in_urb);
228}
229
230static void f81232_dtr_rts(struct usb_serial_port *port, int on)
231{
232 struct f81232_private *priv = usb_get_serial_port_data(port);
233 unsigned long flags;
234 u8 control;
235
236 spin_lock_irqsave(&priv->lock, flags);
237 /* Change DTR and RTS */
238 if (on)
239 priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
240 else
241 priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
242 control = priv->line_control;
243 spin_unlock_irqrestore(&priv->lock, flags);
244 set_control_lines(port->serial->dev, control);
245}
246
247static int f81232_carrier_raised(struct usb_serial_port *port)
248{
249 struct f81232_private *priv = usb_get_serial_port_data(port);
250 if (priv->line_status & UART_DCD)
251 return 1;
252 return 0;
253}
254
255static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
256{
257 struct f81232_private *priv = usb_get_serial_port_data(port);
258 unsigned long flags;
259 unsigned int prevstatus;
260 unsigned int status;
261 unsigned int changed;
262
263 spin_lock_irqsave(&priv->lock, flags);
264 prevstatus = priv->line_status;
265 spin_unlock_irqrestore(&priv->lock, flags);
266
267 while (1) {
268 interruptible_sleep_on(&priv->delta_msr_wait);
269 /* see if a signal did it */
270 if (signal_pending(current))
271 return -ERESTARTSYS;
272
273 spin_lock_irqsave(&priv->lock, flags);
274 status = priv->line_status;
275 spin_unlock_irqrestore(&priv->lock, flags);
276
277 changed = prevstatus ^ status;
278
279 if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
280 ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
281 ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
282 ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
283 return 0;
284 }
285 prevstatus = status;
286 }
287 /* NOTREACHED */
288 return 0;
289}
290
291static int f81232_ioctl(struct tty_struct *tty,
292 unsigned int cmd, unsigned long arg)
293{
294 struct serial_struct ser;
295 struct usb_serial_port *port = tty->driver_data;
296 dbg("%s (%d) cmd = 0x%04x", __func__, 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 dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
313 return wait_modem_info(port, arg);
314 default:
315 dbg("%s not supported = 0x%04x", __func__, cmd);
316 break;
317 }
318 return -ENOIOCTLCMD;
319}
320
321static int f81232_startup(struct usb_serial *serial)
322{
323 struct f81232_private *priv;
324 int i;
325
326 for (i = 0; i < serial->num_ports; ++i) {
327 priv = kzalloc(sizeof(struct f81232_private), GFP_KERNEL);
328 if (!priv)
329 goto cleanup;
330 spin_lock_init(&priv->lock);
331 init_waitqueue_head(&priv->delta_msr_wait);
332 usb_set_serial_port_data(serial->port[i], priv);
333 }
334 return 0;
335
336cleanup:
337 for (--i; i >= 0; --i) {
338 priv = usb_get_serial_port_data(serial->port[i]);
339 kfree(priv);
340 usb_set_serial_port_data(serial->port[i], NULL);
341 }
342 return -ENOMEM;
343}
344
345static void f81232_release(struct usb_serial *serial)
346{
347 int i;
348 struct f81232_private *priv;
349
350 for (i = 0; i < serial->num_ports; ++i) {
351 priv = usb_get_serial_port_data(serial->port[i]);
352 kfree(priv);
353 }
354}
355
356static struct usb_driver f81232_driver = {
357 .name = "f81232",
358 .probe = usb_serial_probe,
359 .disconnect = usb_serial_disconnect,
360 .id_table = id_table,
361 .suspend = usb_serial_suspend,
362 .resume = usb_serial_resume,
363 .no_dynamic_id = 1,
364 .supports_autosuspend = 1,
365};
366
367static struct usb_serial_driver f81232_device = {
368 .driver = {
369 .owner = THIS_MODULE,
370 .name = "f81232",
371 },
372 .id_table = id_table,
373 .usb_driver = &f81232_driver,
374 .num_ports = 1,
375 .bulk_in_size = 256,
376 .bulk_out_size = 256,
377 .open = f81232_open,
378 .close = f81232_close,
379 .dtr_rts = f81232_dtr_rts,
380 .carrier_raised = f81232_carrier_raised,
381 .ioctl = f81232_ioctl,
382 .break_ctl = f81232_break_ctl,
383 .set_termios = f81232_set_termios,
384 .tiocmget = f81232_tiocmget,
385 .tiocmset = f81232_tiocmset,
386 .process_read_urb = f81232_process_read_urb,
387 .read_int_callback = f81232_read_int_callback,
388 .attach = f81232_startup,
389 .release = f81232_release,
390};
391
392static struct usb_serial_driver * const serial_drivers[] = {
393 &f81232_device,
394 NULL,
395};
396
397module_usb_serial_driver(f81232_driver, serial_drivers);
398
399MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
400MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
401MODULE_LICENSE("GPL v2");
402
403module_param(debug, bool, S_IRUGO | S_IWUSR);
404MODULE_PARM_DESC(debug, "Debug enabled or not");
405