diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/serial/f81232.c | 405 |
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 | ||
241 | config 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 | |||
241 | config USB_SERIAL_GARMIN | 250 | config 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 | |||
23 | obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o | 23 | obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o |
24 | obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o | 24 | obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o |
25 | obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o | 25 | obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o |
26 | obj-$(CONFIG_USB_SERIAL_F81232) += f81232.o | ||
26 | obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o | 27 | obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o |
27 | obj-$(CONFIG_USB_SERIAL_FUNSOFT) += funsoft.o | 28 | obj-$(CONFIG_USB_SERIAL_FUNSOFT) += funsoft.o |
28 | obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o | 29 | obj-$(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 | |||
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 | 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 | |||
95 | exit: | ||
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 | |||
103 | static 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 | |||
155 | static int set_control_lines(struct usb_device *dev, u8 value) | ||
156 | { | ||
157 | /* FIXME - Stubbed out for now */ | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static 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 | |||
172 | static 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 | |||
184 | static int f81232_tiocmget(struct tty_struct *tty) | ||
185 | { | ||
186 | /* FIXME - Stubbed out for now */ | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static 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 | |||
197 | static 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 | |||
224 | static 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 | |||
230 | static 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 | |||
247 | static 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 | |||
255 | static 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 | |||
291 | static 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 | |||
321 | static 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 | |||
336 | cleanup: | ||
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 | |||
345 | static 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 | |||
356 | static 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 | |||
367 | static 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 | |||
392 | static struct usb_serial_driver * const serial_drivers[] = { | ||
393 | &f81232_device, | ||
394 | NULL, | ||
395 | }; | ||
396 | |||
397 | module_usb_serial_driver(f81232_driver, serial_drivers); | ||
398 | |||
399 | MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver"); | ||
400 | MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org"); | ||
401 | MODULE_LICENSE("GPL v2"); | ||
402 | |||
403 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
404 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||
405 | |||