Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver | |
3 | * | |
4 | * Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is largely derived from the Belkin USB Serial Adapter Driver | |
12 | * (see belkin_sa.[ch]). All of the information about the device was acquired | |
13 | * by using SniffUSB on Windows98. For technical details see mct_u232.h. | |
14 | * | |
15 | * William G. Greathouse and Greg Kroah-Hartman provided great help on how to | |
16 | * do the reverse engineering and how to write a USB serial device driver. | |
17 | * | |
18 | * TO BE DONE, TO BE CHECKED: | |
19 | * DTR/RTS signal handling may be incomplete or incorrect. I have mainly | |
20 | * implemented what I have seen with SniffUSB or found in belkin_sa.c. | |
21 | * For further TODOs check also belkin_sa.c. | |
1da177e4 LT |
22 | */ |
23 | ||
1da177e4 LT |
24 | #include <linux/kernel.h> |
25 | #include <linux/errno.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/slab.h> | |
28 | #include <linux/tty.h> | |
29 | #include <linux/tty_driver.h> | |
30 | #include <linux/tty_flip.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/spinlock.h> | |
e19b2560 | 33 | #include <linux/uaccess.h> |
af2ac1a0 | 34 | #include <asm/unaligned.h> |
1da177e4 | 35 | #include <linux/usb.h> |
a969888c | 36 | #include <linux/usb/serial.h> |
7af75af2 | 37 | #include <linux/serial.h> |
1da177e4 LT |
38 | #include "mct_u232.h" |
39 | ||
1da177e4 LT |
40 | #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" |
41 | #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" | |
42 | ||
1da177e4 LT |
43 | /* |
44 | * Function prototypes | |
45 | */ | |
a8f2ae7a JH |
46 | static int mct_u232_port_probe(struct usb_serial_port *port); |
47 | static int mct_u232_port_remove(struct usb_serial_port *remove); | |
a509a7e4 | 48 | static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); |
335f8514 AC |
49 | static void mct_u232_close(struct usb_serial_port *port); |
50 | static void mct_u232_dtr_rts(struct usb_serial_port *port, int on); | |
e19b2560 AC |
51 | static void mct_u232_read_int_callback(struct urb *urb); |
52 | static void mct_u232_set_termios(struct tty_struct *tty, | |
53 | struct usb_serial_port *port, struct ktermios *old); | |
54 | static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); | |
60b33c13 | 55 | static int mct_u232_tiocmget(struct tty_struct *tty); |
20b9d177 | 56 | static int mct_u232_tiocmset(struct tty_struct *tty, |
e19b2560 AC |
57 | unsigned int set, unsigned int clear); |
58 | static void mct_u232_throttle(struct tty_struct *tty); | |
59 | static void mct_u232_unthrottle(struct tty_struct *tty); | |
45b844df DP |
60 | |
61 | ||
1da177e4 LT |
62 | /* |
63 | * All of the device info needed for the MCT USB-RS232 converter. | |
64 | */ | |
68e24113 | 65 | static const struct usb_device_id id_table[] = { |
1da177e4 LT |
66 | { USB_DEVICE(MCT_U232_VID, MCT_U232_PID) }, |
67 | { USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) }, | |
68 | { USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) }, | |
69 | { USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) }, | |
70 | { } /* Terminating entry */ | |
71 | }; | |
68e24113 | 72 | MODULE_DEVICE_TABLE(usb, id_table); |
1da177e4 | 73 | |
ea65370d | 74 | static struct usb_serial_driver mct_u232_device = { |
18fcac35 GKH |
75 | .driver = { |
76 | .owner = THIS_MODULE, | |
269bda1c | 77 | .name = "mct_u232", |
18fcac35 | 78 | }, |
269bda1c | 79 | .description = "MCT U232", |
68e24113 | 80 | .id_table = id_table, |
1da177e4 LT |
81 | .num_ports = 1, |
82 | .open = mct_u232_open, | |
83 | .close = mct_u232_close, | |
335f8514 | 84 | .dtr_rts = mct_u232_dtr_rts, |
45b844df DP |
85 | .throttle = mct_u232_throttle, |
86 | .unthrottle = mct_u232_unthrottle, | |
1da177e4 | 87 | .read_int_callback = mct_u232_read_int_callback, |
1da177e4 LT |
88 | .set_termios = mct_u232_set_termios, |
89 | .break_ctl = mct_u232_break_ctl, | |
90 | .tiocmget = mct_u232_tiocmget, | |
91 | .tiocmset = mct_u232_tiocmset, | |
128434ab | 92 | .tiocmiwait = usb_serial_generic_tiocmiwait, |
a8f2ae7a JH |
93 | .port_probe = mct_u232_port_probe, |
94 | .port_remove = mct_u232_port_remove, | |
468c740e | 95 | .get_icount = usb_serial_generic_get_icount, |
1da177e4 LT |
96 | }; |
97 | ||
4d2a7aff AS |
98 | static struct usb_serial_driver * const serial_drivers[] = { |
99 | &mct_u232_device, NULL | |
100 | }; | |
101 | ||
1da177e4 | 102 | struct mct_u232_private { |
35807187 | 103 | struct urb *read_urb; |
1da177e4 LT |
104 | spinlock_t lock; |
105 | unsigned int control_state; /* Modem Line Setting (TIOCM) */ | |
106 | unsigned char last_lcr; /* Line Control Register */ | |
107 | unsigned char last_lsr; /* Line Status Register */ | |
108 | unsigned char last_msr; /* Modem Status Register */ | |
45b844df | 109 | unsigned int rx_flags; /* Throttling flags */ |
1da177e4 LT |
110 | }; |
111 | ||
45b844df DP |
112 | #define THROTTLED 0x01 |
113 | ||
1da177e4 LT |
114 | /* |
115 | * Handle vendor specific USB requests | |
116 | */ | |
117 | ||
118 | #define WDR_TIMEOUT 5000 /* default urb timeout */ | |
119 | ||
120 | /* | |
121 | * Later day 2.6.0-test kernels have new baud rates like B230400 which | |
122 | * we do not know how to support. We ignore them for the moment. | |
1da177e4 | 123 | */ |
e19b2560 AC |
124 | static int mct_u232_calculate_baud_rate(struct usb_serial *serial, |
125 | speed_t value, speed_t *result) | |
1da177e4 | 126 | { |
d0fab0dd AC |
127 | *result = value; |
128 | ||
1da177e4 | 129 | if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID |
e19b2560 | 130 | || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) { |
1da177e4 | 131 | switch (value) { |
e19b2560 AC |
132 | case 300: |
133 | return 0x01; | |
134 | case 600: | |
135 | return 0x02; /* this one not tested */ | |
136 | case 1200: | |
137 | return 0x03; | |
138 | case 2400: | |
139 | return 0x04; | |
140 | case 4800: | |
141 | return 0x06; | |
142 | case 9600: | |
143 | return 0x08; | |
144 | case 19200: | |
145 | return 0x09; | |
146 | case 38400: | |
147 | return 0x0a; | |
148 | case 57600: | |
149 | return 0x0b; | |
150 | case 115200: | |
151 | return 0x0c; | |
1da177e4 | 152 | default: |
d0fab0dd | 153 | *result = 9600; |
1da177e4 LT |
154 | return 0x08; |
155 | } | |
156 | } else { | |
d0fab0dd AC |
157 | /* FIXME: Can we use any divider - should we do |
158 | divider = 115200/value; | |
159 | real baud = 115200/divider */ | |
1da177e4 | 160 | switch (value) { |
b3aceb2b AM |
161 | case 300: break; |
162 | case 600: break; | |
163 | case 1200: break; | |
164 | case 2400: break; | |
165 | case 4800: break; | |
166 | case 9600: break; | |
167 | case 19200: break; | |
168 | case 38400: break; | |
169 | case 57600: break; | |
170 | case 115200: break; | |
171 | default: | |
b3aceb2b | 172 | value = 9600; |
d0fab0dd | 173 | *result = 9600; |
1da177e4 LT |
174 | } |
175 | return 115200/value; | |
176 | } | |
177 | } | |
178 | ||
95da310e AC |
179 | static int mct_u232_set_baud_rate(struct tty_struct *tty, |
180 | struct usb_serial *serial, struct usb_serial_port *port, speed_t value) | |
1da177e4 | 181 | { |
af2ac1a0 | 182 | unsigned int divisor; |
e19b2560 | 183 | int rc; |
af2ac1a0 | 184 | unsigned char *buf; |
e19b2560 AC |
185 | unsigned char cts_enable_byte = 0; |
186 | speed_t speed; | |
187 | ||
af2ac1a0 PZ |
188 | buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); |
189 | if (buf == NULL) | |
190 | return -ENOMEM; | |
e19b2560 | 191 | |
af2ac1a0 PZ |
192 | divisor = mct_u232_calculate_baud_rate(serial, value, &speed); |
193 | put_unaligned_le32(cpu_to_le32(divisor), buf); | |
e19b2560 AC |
194 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
195 | MCT_U232_SET_BAUD_RATE_REQUEST, | |
196 | MCT_U232_SET_REQUEST_TYPE, | |
af2ac1a0 | 197 | 0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE, |
e19b2560 | 198 | WDR_TIMEOUT); |
d0fab0dd | 199 | if (rc < 0) /*FIXME: What value speed results */ |
194343d9 GKH |
200 | dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n", |
201 | value, rc); | |
d0fab0dd | 202 | else |
95da310e | 203 | tty_encode_baud_rate(tty, speed, speed); |
4a770cca | 204 | dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor); |
1da177e4 LT |
205 | |
206 | /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which | |
207 | always sends two extra USB 'device request' messages after the | |
208 | 'baud rate change' message. The actual functionality of the | |
209 | request codes in these messages is not fully understood but these | |
210 | particular codes are never seen in any operation besides a baud | |
45b844df DP |
211 | rate change. Both of these messages send a single byte of data. |
212 | In the first message, the value of this byte is always zero. | |
213 | ||
214 | The second message has been determined experimentally to control | |
215 | whether data will be transmitted to a device which is not asserting | |
216 | the 'CTS' signal. If the second message's data byte is zero, data | |
217 | will be transmitted even if 'CTS' is not asserted (i.e. no hardware | |
e19b2560 AC |
218 | flow control). if the second message's data byte is nonzero (a |
219 | value of 1 is used by this driver), data will not be transmitted to | |
220 | a device which is not asserting 'CTS'. | |
45b844df | 221 | */ |
1da177e4 | 222 | |
af2ac1a0 | 223 | buf[0] = 0; |
1da177e4 | 224 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
e19b2560 AC |
225 | MCT_U232_SET_UNKNOWN1_REQUEST, |
226 | MCT_U232_SET_REQUEST_TYPE, | |
af2ac1a0 | 227 | 0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE, |
e19b2560 | 228 | WDR_TIMEOUT); |
1da177e4 | 229 | if (rc < 0) |
194343d9 GKH |
230 | dev_err(&port->dev, "Sending USB device request code %d " |
231 | "failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST, | |
232 | rc); | |
1da177e4 | 233 | |
e19b2560 | 234 | if (port && C_CRTSCTS(tty)) |
45b844df | 235 | cts_enable_byte = 1; |
45b844df | 236 | |
4a770cca GKH |
237 | dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n", |
238 | cts_enable_byte); | |
af2ac1a0 | 239 | buf[0] = cts_enable_byte; |
1da177e4 | 240 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
e19b2560 AC |
241 | MCT_U232_SET_CTS_REQUEST, |
242 | MCT_U232_SET_REQUEST_TYPE, | |
af2ac1a0 | 243 | 0, 0, buf, MCT_U232_SET_CTS_SIZE, |
e19b2560 | 244 | WDR_TIMEOUT); |
1da177e4 | 245 | if (rc < 0) |
194343d9 GKH |
246 | dev_err(&port->dev, "Sending USB device request code %d " |
247 | "failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc); | |
1da177e4 | 248 | |
af2ac1a0 | 249 | kfree(buf); |
e19b2560 | 250 | return rc; |
1da177e4 LT |
251 | } /* mct_u232_set_baud_rate */ |
252 | ||
4a770cca GKH |
253 | static int mct_u232_set_line_ctrl(struct usb_serial_port *port, |
254 | unsigned char lcr) | |
1da177e4 | 255 | { |
e19b2560 | 256 | int rc; |
af2ac1a0 PZ |
257 | unsigned char *buf; |
258 | ||
259 | buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); | |
260 | if (buf == NULL) | |
261 | return -ENOMEM; | |
262 | ||
263 | buf[0] = lcr; | |
4a770cca | 264 | rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), |
e19b2560 AC |
265 | MCT_U232_SET_LINE_CTRL_REQUEST, |
266 | MCT_U232_SET_REQUEST_TYPE, | |
af2ac1a0 | 267 | 0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE, |
e19b2560 | 268 | WDR_TIMEOUT); |
1da177e4 | 269 | if (rc < 0) |
4a770cca GKH |
270 | dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc); |
271 | dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr); | |
af2ac1a0 | 272 | kfree(buf); |
e19b2560 | 273 | return rc; |
1da177e4 LT |
274 | } /* mct_u232_set_line_ctrl */ |
275 | ||
4a770cca | 276 | static int mct_u232_set_modem_ctrl(struct usb_serial_port *port, |
1da177e4 LT |
277 | unsigned int control_state) |
278 | { | |
e19b2560 | 279 | int rc; |
af2ac1a0 PZ |
280 | unsigned char mcr; |
281 | unsigned char *buf; | |
282 | ||
283 | buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); | |
284 | if (buf == NULL) | |
285 | return -ENOMEM; | |
1da177e4 | 286 | |
af2ac1a0 | 287 | mcr = MCT_U232_MCR_NONE; |
1da177e4 LT |
288 | if (control_state & TIOCM_DTR) |
289 | mcr |= MCT_U232_MCR_DTR; | |
290 | if (control_state & TIOCM_RTS) | |
291 | mcr |= MCT_U232_MCR_RTS; | |
292 | ||
af2ac1a0 | 293 | buf[0] = mcr; |
4a770cca | 294 | rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), |
e19b2560 AC |
295 | MCT_U232_SET_MODEM_CTRL_REQUEST, |
296 | MCT_U232_SET_REQUEST_TYPE, | |
af2ac1a0 | 297 | 0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE, |
e19b2560 | 298 | WDR_TIMEOUT); |
1aa3c63c AC |
299 | kfree(buf); |
300 | ||
4a770cca | 301 | dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr); |
1da177e4 | 302 | |
1aa3c63c | 303 | if (rc < 0) { |
4a770cca | 304 | dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc); |
1aa3c63c AC |
305 | return rc; |
306 | } | |
307 | return 0; | |
1da177e4 LT |
308 | } /* mct_u232_set_modem_ctrl */ |
309 | ||
4a770cca GKH |
310 | static int mct_u232_get_modem_stat(struct usb_serial_port *port, |
311 | unsigned char *msr) | |
1da177e4 | 312 | { |
e19b2560 | 313 | int rc; |
af2ac1a0 PZ |
314 | unsigned char *buf; |
315 | ||
316 | buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); | |
317 | if (buf == NULL) { | |
318 | *msr = 0; | |
319 | return -ENOMEM; | |
320 | } | |
4a770cca | 321 | rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), |
e19b2560 AC |
322 | MCT_U232_GET_MODEM_STAT_REQUEST, |
323 | MCT_U232_GET_REQUEST_TYPE, | |
af2ac1a0 | 324 | 0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE, |
e19b2560 | 325 | WDR_TIMEOUT); |
1da177e4 | 326 | if (rc < 0) { |
4a770cca | 327 | dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc); |
1da177e4 | 328 | *msr = 0; |
af2ac1a0 PZ |
329 | } else { |
330 | *msr = buf[0]; | |
1da177e4 | 331 | } |
4a770cca | 332 | dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr); |
af2ac1a0 | 333 | kfree(buf); |
e19b2560 | 334 | return rc; |
1da177e4 LT |
335 | } /* mct_u232_get_modem_stat */ |
336 | ||
7af75af2 VT |
337 | static void mct_u232_msr_to_icount(struct async_icount *icount, |
338 | unsigned char msr) | |
339 | { | |
340 | /* Translate Control Line states */ | |
341 | if (msr & MCT_U232_MSR_DDSR) | |
342 | icount->dsr++; | |
343 | if (msr & MCT_U232_MSR_DCTS) | |
344 | icount->cts++; | |
345 | if (msr & MCT_U232_MSR_DRI) | |
346 | icount->rng++; | |
347 | if (msr & MCT_U232_MSR_DCD) | |
348 | icount->dcd++; | |
349 | } /* mct_u232_msr_to_icount */ | |
350 | ||
4a770cca GKH |
351 | static void mct_u232_msr_to_state(struct usb_serial_port *port, |
352 | unsigned int *control_state, unsigned char msr) | |
1da177e4 | 353 | { |
e19b2560 | 354 | /* Translate Control Line states */ |
1da177e4 LT |
355 | if (msr & MCT_U232_MSR_DSR) |
356 | *control_state |= TIOCM_DSR; | |
357 | else | |
358 | *control_state &= ~TIOCM_DSR; | |
359 | if (msr & MCT_U232_MSR_CTS) | |
360 | *control_state |= TIOCM_CTS; | |
361 | else | |
362 | *control_state &= ~TIOCM_CTS; | |
363 | if (msr & MCT_U232_MSR_RI) | |
364 | *control_state |= TIOCM_RI; | |
365 | else | |
366 | *control_state &= ~TIOCM_RI; | |
367 | if (msr & MCT_U232_MSR_CD) | |
368 | *control_state |= TIOCM_CD; | |
369 | else | |
370 | *control_state &= ~TIOCM_CD; | |
4a770cca | 371 | dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state); |
1da177e4 LT |
372 | } /* mct_u232_msr_to_state */ |
373 | ||
374 | /* | |
375 | * Driver's tty interface functions | |
376 | */ | |
377 | ||
a8f2ae7a JH |
378 | static int mct_u232_port_probe(struct usb_serial_port *port) |
379 | { | |
380 | struct mct_u232_private *priv; | |
381 | ||
382 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | |
383 | if (!priv) | |
384 | return -ENOMEM; | |
1da177e4 | 385 | |
35807187 JH |
386 | /* Use second interrupt-in endpoint for reading. */ |
387 | priv->read_urb = port->serial->port[1]->interrupt_in_urb; | |
388 | priv->read_urb->context = port; | |
389 | ||
a8f2ae7a | 390 | spin_lock_init(&priv->lock); |
a8f2ae7a JH |
391 | |
392 | usb_set_serial_port_data(port, priv); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | static int mct_u232_port_remove(struct usb_serial_port *port) | |
1da177e4 LT |
398 | { |
399 | struct mct_u232_private *priv; | |
e19b2560 | 400 | |
a8f2ae7a JH |
401 | priv = usb_get_serial_port_data(port); |
402 | kfree(priv); | |
403 | ||
404 | return 0; | |
405 | } | |
1da177e4 | 406 | |
a509a7e4 | 407 | static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) |
1da177e4 LT |
408 | { |
409 | struct usb_serial *serial = port->serial; | |
410 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | |
411 | int retval = 0; | |
412 | unsigned int control_state; | |
413 | unsigned long flags; | |
414 | unsigned char last_lcr; | |
415 | unsigned char last_msr; | |
416 | ||
1da177e4 LT |
417 | /* Compensate for a hardware bug: although the Sitecom U232-P25 |
418 | * device reports a maximum output packet size of 32 bytes, | |
419 | * it seems to be able to accept only 16 bytes (and that's what | |
420 | * SniffUSB says too...) | |
421 | */ | |
e19b2560 AC |
422 | if (le16_to_cpu(serial->dev->descriptor.idProduct) |
423 | == MCT_U232_SITECOM_PID) | |
1da177e4 LT |
424 | port->bulk_out_size = 16; |
425 | ||
e19b2560 | 426 | /* Do a defined restart: the normal serial device seems to |
1da177e4 LT |
427 | * always turn on DTR and RTS here, so do the same. I'm not |
428 | * sure if this is really necessary. But it should not harm | |
429 | * either. | |
430 | */ | |
431 | spin_lock_irqsave(&priv->lock, flags); | |
adc8d746 | 432 | if (tty && (tty->termios.c_cflag & CBAUD)) |
1da177e4 LT |
433 | priv->control_state = TIOCM_DTR | TIOCM_RTS; |
434 | else | |
435 | priv->control_state = 0; | |
e19b2560 AC |
436 | |
437 | priv->last_lcr = (MCT_U232_DATA_BITS_8 | | |
1da177e4 LT |
438 | MCT_U232_PARITY_NONE | |
439 | MCT_U232_STOP_BITS_1); | |
440 | control_state = priv->control_state; | |
441 | last_lcr = priv->last_lcr; | |
442 | spin_unlock_irqrestore(&priv->lock, flags); | |
4a770cca GKH |
443 | mct_u232_set_modem_ctrl(port, control_state); |
444 | mct_u232_set_line_ctrl(port, last_lcr); | |
1da177e4 LT |
445 | |
446 | /* Read modem status and update control state */ | |
4a770cca | 447 | mct_u232_get_modem_stat(port, &last_msr); |
1da177e4 LT |
448 | spin_lock_irqsave(&priv->lock, flags); |
449 | priv->last_msr = last_msr; | |
4a770cca | 450 | mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); |
1da177e4 LT |
451 | spin_unlock_irqrestore(&priv->lock, flags); |
452 | ||
35807187 | 453 | retval = usb_submit_urb(priv->read_urb, GFP_KERNEL); |
1da177e4 | 454 | if (retval) { |
194343d9 | 455 | dev_err(&port->dev, |
35807187 | 456 | "usb_submit_urb(read) failed pipe 0x%x err %d\n", |
194343d9 | 457 | port->read_urb->pipe, retval); |
2f007de2 | 458 | goto error; |
1da177e4 LT |
459 | } |
460 | ||
1da177e4 | 461 | retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); |
2f007de2 | 462 | if (retval) { |
35807187 | 463 | usb_kill_urb(priv->read_urb); |
194343d9 GKH |
464 | dev_err(&port->dev, |
465 | "usb_submit_urb(read int) failed pipe 0x%x err %d", | |
466 | port->interrupt_in_urb->pipe, retval); | |
2f007de2 ON |
467 | goto error; |
468 | } | |
1da177e4 | 469 | return 0; |
2f007de2 ON |
470 | |
471 | error: | |
472 | return retval; | |
1da177e4 LT |
473 | } /* mct_u232_open */ |
474 | ||
335f8514 | 475 | static void mct_u232_dtr_rts(struct usb_serial_port *port, int on) |
1da177e4 | 476 | { |
45b844df DP |
477 | unsigned int control_state; |
478 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | |
1da177e4 | 479 | |
b2ca6990 JH |
480 | spin_lock_irq(&priv->lock); |
481 | if (on) | |
482 | priv->control_state |= TIOCM_DTR | TIOCM_RTS; | |
483 | else | |
484 | priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); | |
485 | control_state = priv->control_state; | |
486 | spin_unlock_irq(&priv->lock); | |
487 | ||
488 | mct_u232_set_modem_ctrl(port, control_state); | |
335f8514 | 489 | } |
45b844df | 490 | |
335f8514 AC |
491 | static void mct_u232_close(struct usb_serial_port *port) |
492 | { | |
35807187 JH |
493 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
494 | ||
495 | usb_kill_urb(priv->read_urb); | |
5260e458 JH |
496 | usb_kill_urb(port->interrupt_in_urb); |
497 | ||
498 | usb_serial_generic_close(port); | |
1da177e4 LT |
499 | } /* mct_u232_close */ |
500 | ||
501 | ||
e19b2560 | 502 | static void mct_u232_read_int_callback(struct urb *urb) |
1da177e4 | 503 | { |
cdc97792 | 504 | struct usb_serial_port *port = urb->context; |
1da177e4 | 505 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
1da177e4 | 506 | unsigned char *data = urb->transfer_buffer; |
e96da398 GKH |
507 | int retval; |
508 | int status = urb->status; | |
1da177e4 LT |
509 | unsigned long flags; |
510 | ||
e96da398 | 511 | switch (status) { |
1da177e4 LT |
512 | case 0: |
513 | /* success */ | |
514 | break; | |
515 | case -ECONNRESET: | |
516 | case -ENOENT: | |
517 | case -ESHUTDOWN: | |
518 | /* this urb is terminated, clean up */ | |
4a770cca GKH |
519 | dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", |
520 | __func__, status); | |
1da177e4 LT |
521 | return; |
522 | default: | |
4a770cca GKH |
523 | dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", |
524 | __func__, status); | |
1da177e4 LT |
525 | goto exit; |
526 | } | |
527 | ||
59d33f2f | 528 | usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); |
1da177e4 LT |
529 | |
530 | /* | |
531 | * Work-a-round: handle the 'usual' bulk-in pipe here | |
532 | */ | |
533 | if (urb->transfer_buffer_length > 2) { | |
1da177e4 | 534 | if (urb->actual_length) { |
2e124b4a JS |
535 | tty_insert_flip_string(&port->port, data, |
536 | urb->actual_length); | |
537 | tty_flip_buffer_push(&port->port); | |
1da177e4 LT |
538 | } |
539 | goto exit; | |
540 | } | |
e19b2560 | 541 | |
1da177e4 LT |
542 | /* |
543 | * The interrupt-in pipe signals exceptional conditions (modem line | |
544 | * signal changes and errors). data[0] holds MSR, data[1] holds LSR. | |
545 | */ | |
546 | spin_lock_irqsave(&priv->lock, flags); | |
547 | priv->last_msr = data[MCT_U232_MSR_INDEX]; | |
e19b2560 | 548 | |
1da177e4 | 549 | /* Record Control Line states */ |
4a770cca | 550 | mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); |
1da177e4 | 551 | |
468c740e | 552 | mct_u232_msr_to_icount(&port->icount, priv->last_msr); |
7af75af2 | 553 | |
1da177e4 | 554 | #if 0 |
e19b2560 | 555 | /* Not yet handled. See belkin_sa.c for further information */ |
1da177e4 LT |
556 | /* Now to report any errors */ |
557 | priv->last_lsr = data[MCT_U232_LSR_INDEX]; | |
558 | /* | |
559 | * fill in the flip buffer here, but I do not know the relation | |
560 | * to the current/next receive buffer or characters. I need | |
561 | * to look in to this before committing any code. | |
562 | */ | |
563 | if (priv->last_lsr & MCT_U232_LSR_ERR) { | |
4a90f09b | 564 | tty = tty_port_tty_get(&port->port); |
1da177e4 LT |
565 | /* Overrun Error */ |
566 | if (priv->last_lsr & MCT_U232_LSR_OE) { | |
567 | } | |
568 | /* Parity Error */ | |
569 | if (priv->last_lsr & MCT_U232_LSR_PE) { | |
570 | } | |
571 | /* Framing Error */ | |
572 | if (priv->last_lsr & MCT_U232_LSR_FE) { | |
573 | } | |
574 | /* Break Indicator */ | |
575 | if (priv->last_lsr & MCT_U232_LSR_BI) { | |
576 | } | |
4a90f09b | 577 | tty_kref_put(tty); |
1da177e4 LT |
578 | } |
579 | #endif | |
128434ab | 580 | wake_up_interruptible(&port->port.delta_msr_wait); |
1da177e4 LT |
581 | spin_unlock_irqrestore(&priv->lock, flags); |
582 | exit: | |
e19b2560 | 583 | retval = usb_submit_urb(urb, GFP_ATOMIC); |
e96da398 | 584 | if (retval) |
194343d9 GKH |
585 | dev_err(&port->dev, |
586 | "%s - usb_submit_urb failed with result %d\n", | |
587 | __func__, retval); | |
1da177e4 LT |
588 | } /* mct_u232_read_int_callback */ |
589 | ||
e19b2560 AC |
590 | static void mct_u232_set_termios(struct tty_struct *tty, |
591 | struct usb_serial_port *port, | |
592 | struct ktermios *old_termios) | |
1da177e4 LT |
593 | { |
594 | struct usb_serial *serial = port->serial; | |
595 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | |
adc8d746 | 596 | struct ktermios *termios = &tty->termios; |
d0fab0dd | 597 | unsigned int cflag = termios->c_cflag; |
1da177e4 LT |
598 | unsigned int old_cflag = old_termios->c_cflag; |
599 | unsigned long flags; | |
45b844df | 600 | unsigned int control_state; |
1da177e4 LT |
601 | unsigned char last_lcr; |
602 | ||
603 | /* get a local copy of the current port settings */ | |
604 | spin_lock_irqsave(&priv->lock, flags); | |
605 | control_state = priv->control_state; | |
606 | spin_unlock_irqrestore(&priv->lock, flags); | |
607 | last_lcr = 0; | |
608 | ||
609 | /* | |
610 | * Update baud rate. | |
611 | * Do not attempt to cache old rates and skip settings, | |
612 | * disconnects screw such tricks up completely. | |
613 | * Premature optimization is the root of all evil. | |
614 | */ | |
615 | ||
e19b2560 | 616 | /* reassert DTR and RTS on transition from B0 */ |
1da177e4 | 617 | if ((old_cflag & CBAUD) == B0) { |
4a770cca | 618 | dev_dbg(&port->dev, "%s: baud was B0\n", __func__); |
45b844df | 619 | control_state |= TIOCM_DTR | TIOCM_RTS; |
4a770cca | 620 | mct_u232_set_modem_ctrl(port, control_state); |
1da177e4 LT |
621 | } |
622 | ||
95da310e | 623 | mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty)); |
1da177e4 | 624 | |
e19b2560 | 625 | if ((cflag & CBAUD) == B0) { |
4a770cca | 626 | dev_dbg(&port->dev, "%s: baud is B0\n", __func__); |
1da177e4 LT |
627 | /* Drop RTS and DTR */ |
628 | control_state &= ~(TIOCM_DTR | TIOCM_RTS); | |
4a770cca | 629 | mct_u232_set_modem_ctrl(port, control_state); |
1da177e4 LT |
630 | } |
631 | ||
632 | /* | |
633 | * Update line control register (LCR) | |
634 | */ | |
635 | ||
636 | /* set the parity */ | |
637 | if (cflag & PARENB) | |
638 | last_lcr |= (cflag & PARODD) ? | |
639 | MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; | |
640 | else | |
641 | last_lcr |= MCT_U232_PARITY_NONE; | |
642 | ||
643 | /* set the number of data bits */ | |
644 | switch (cflag & CSIZE) { | |
645 | case CS5: | |
646 | last_lcr |= MCT_U232_DATA_BITS_5; break; | |
647 | case CS6: | |
648 | last_lcr |= MCT_U232_DATA_BITS_6; break; | |
649 | case CS7: | |
650 | last_lcr |= MCT_U232_DATA_BITS_7; break; | |
651 | case CS8: | |
652 | last_lcr |= MCT_U232_DATA_BITS_8; break; | |
653 | default: | |
194343d9 GKH |
654 | dev_err(&port->dev, |
655 | "CSIZE was not CS5-CS8, using default of 8\n"); | |
1da177e4 LT |
656 | last_lcr |= MCT_U232_DATA_BITS_8; |
657 | break; | |
658 | } | |
659 | ||
d0fab0dd AC |
660 | termios->c_cflag &= ~CMSPAR; |
661 | ||
1da177e4 LT |
662 | /* set the number of stop bits */ |
663 | last_lcr |= (cflag & CSTOPB) ? | |
664 | MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; | |
665 | ||
4a770cca | 666 | mct_u232_set_line_ctrl(port, last_lcr); |
1da177e4 | 667 | |
1da177e4 LT |
668 | /* save off the modified port settings */ |
669 | spin_lock_irqsave(&priv->lock, flags); | |
670 | priv->control_state = control_state; | |
671 | priv->last_lcr = last_lcr; | |
672 | spin_unlock_irqrestore(&priv->lock, flags); | |
673 | } /* mct_u232_set_termios */ | |
674 | ||
95da310e | 675 | static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) |
1da177e4 | 676 | { |
95da310e | 677 | struct usb_serial_port *port = tty->driver_data; |
1da177e4 LT |
678 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
679 | unsigned char lcr; | |
680 | unsigned long flags; | |
681 | ||
1da177e4 LT |
682 | spin_lock_irqsave(&priv->lock, flags); |
683 | lcr = priv->last_lcr; | |
1da177e4 LT |
684 | |
685 | if (break_state) | |
686 | lcr |= MCT_U232_SET_BREAK; | |
6b447f04 | 687 | spin_unlock_irqrestore(&priv->lock, flags); |
1da177e4 | 688 | |
4a770cca | 689 | mct_u232_set_line_ctrl(port, lcr); |
1da177e4 LT |
690 | } /* mct_u232_break_ctl */ |
691 | ||
692 | ||
60b33c13 | 693 | static int mct_u232_tiocmget(struct tty_struct *tty) |
1da177e4 | 694 | { |
95da310e | 695 | struct usb_serial_port *port = tty->driver_data; |
1da177e4 LT |
696 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
697 | unsigned int control_state; | |
698 | unsigned long flags; | |
e19b2560 | 699 | |
1da177e4 LT |
700 | spin_lock_irqsave(&priv->lock, flags); |
701 | control_state = priv->control_state; | |
702 | spin_unlock_irqrestore(&priv->lock, flags); | |
703 | ||
704 | return control_state; | |
705 | } | |
706 | ||
20b9d177 | 707 | static int mct_u232_tiocmset(struct tty_struct *tty, |
1da177e4 LT |
708 | unsigned int set, unsigned int clear) |
709 | { | |
95da310e | 710 | struct usb_serial_port *port = tty->driver_data; |
1da177e4 LT |
711 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
712 | unsigned int control_state; | |
713 | unsigned long flags; | |
e19b2560 | 714 | |
1da177e4 LT |
715 | spin_lock_irqsave(&priv->lock, flags); |
716 | control_state = priv->control_state; | |
717 | ||
718 | if (set & TIOCM_RTS) | |
719 | control_state |= TIOCM_RTS; | |
720 | if (set & TIOCM_DTR) | |
721 | control_state |= TIOCM_DTR; | |
722 | if (clear & TIOCM_RTS) | |
723 | control_state &= ~TIOCM_RTS; | |
724 | if (clear & TIOCM_DTR) | |
725 | control_state &= ~TIOCM_DTR; | |
726 | ||
727 | priv->control_state = control_state; | |
728 | spin_unlock_irqrestore(&priv->lock, flags); | |
4a770cca | 729 | return mct_u232_set_modem_ctrl(port, control_state); |
1da177e4 LT |
730 | } |
731 | ||
95da310e | 732 | static void mct_u232_throttle(struct tty_struct *tty) |
45b844df | 733 | { |
95da310e | 734 | struct usb_serial_port *port = tty->driver_data; |
45b844df | 735 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
45b844df | 736 | unsigned int control_state; |
45b844df | 737 | |
63832515 | 738 | spin_lock_irq(&priv->lock); |
45b844df DP |
739 | priv->rx_flags |= THROTTLED; |
740 | if (C_CRTSCTS(tty)) { | |
95da310e AC |
741 | priv->control_state &= ~TIOCM_RTS; |
742 | control_state = priv->control_state; | |
63832515 | 743 | spin_unlock_irq(&priv->lock); |
4a770cca | 744 | mct_u232_set_modem_ctrl(port, control_state); |
45b844df | 745 | } else { |
63832515 | 746 | spin_unlock_irq(&priv->lock); |
45b844df DP |
747 | } |
748 | } | |
749 | ||
95da310e | 750 | static void mct_u232_unthrottle(struct tty_struct *tty) |
45b844df | 751 | { |
95da310e | 752 | struct usb_serial_port *port = tty->driver_data; |
45b844df | 753 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
45b844df | 754 | unsigned int control_state; |
45b844df | 755 | |
63832515 | 756 | spin_lock_irq(&priv->lock); |
45b844df | 757 | if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { |
95da310e AC |
758 | priv->rx_flags &= ~THROTTLED; |
759 | priv->control_state |= TIOCM_RTS; | |
760 | control_state = priv->control_state; | |
63832515 | 761 | spin_unlock_irq(&priv->lock); |
4a770cca | 762 | mct_u232_set_modem_ctrl(port, control_state); |
45b844df | 763 | } else { |
63832515 | 764 | spin_unlock_irq(&priv->lock); |
45b844df DP |
765 | } |
766 | } | |
1da177e4 | 767 | |
68e24113 | 768 | module_usb_serial_driver(serial_drivers, id_table); |
1da177e4 | 769 | |
e19b2560 AC |
770 | MODULE_AUTHOR(DRIVER_AUTHOR); |
771 | MODULE_DESCRIPTION(DRIVER_DESC); | |
1da177e4 | 772 | MODULE_LICENSE("GPL"); |