diff options
Diffstat (limited to 'drivers/usb/serial/spcp8x5.c')
-rw-r--r-- | drivers/usb/serial/spcp8x5.c | 301 |
1 files changed, 73 insertions, 228 deletions
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 549ef68ff5fa..cf3df793c2b7 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * spcp8x5 USB to serial adaptor driver | 2 | * spcp8x5 USB to serial adaptor driver |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) | 4 | * Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com) |
5 | * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn) | 5 | * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn) |
6 | * Copyright (C) 2006 S1 Corp. | 6 | * Copyright (C) 2006 S1 Corp. |
7 | * | 7 | * |
@@ -13,8 +13,6 @@ | |||
13 | * it under the terms of the GNU General Public License as published by | 13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or | 14 | * the Free Software Foundation; either version 2 of the License, or |
15 | * (at your option) any later version. | 15 | * (at your option) any later version. |
16 | * | ||
17 | * | ||
18 | */ | 16 | */ |
19 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
20 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
@@ -28,7 +26,10 @@ | |||
28 | #include <linux/usb.h> | 26 | #include <linux/usb.h> |
29 | #include <linux/usb/serial.h> | 27 | #include <linux/usb/serial.h> |
30 | 28 | ||
31 | #define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" | 29 | #define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" |
30 | |||
31 | #define SPCP825_QUIRK_NO_UART_STATUS 0x01 | ||
32 | #define SPCP825_QUIRK_NO_WORK_MODE 0x02 | ||
32 | 33 | ||
33 | #define SPCP8x5_007_VID 0x04FC | 34 | #define SPCP8x5_007_VID 0x04FC |
34 | #define SPCP8x5_007_PID 0x0201 | 35 | #define SPCP8x5_007_PID 0x0201 |
@@ -46,13 +47,15 @@ static const struct usb_device_id id_table[] = { | |||
46 | { USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)}, | 47 | { USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)}, |
47 | { USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)}, | 48 | { USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)}, |
48 | { USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)}, | 49 | { USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)}, |
49 | { USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID)}, | 50 | { USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID), |
51 | .driver_info = SPCP825_QUIRK_NO_UART_STATUS | | ||
52 | SPCP825_QUIRK_NO_WORK_MODE }, | ||
50 | { } /* Terminating entry */ | 53 | { } /* Terminating entry */ |
51 | }; | 54 | }; |
52 | MODULE_DEVICE_TABLE(usb, id_table); | 55 | MODULE_DEVICE_TABLE(usb, id_table); |
53 | 56 | ||
54 | struct spcp8x5_usb_ctrl_arg { | 57 | struct spcp8x5_usb_ctrl_arg { |
55 | u8 type; | 58 | u8 type; |
56 | u8 cmd; | 59 | u8 cmd; |
57 | u8 cmd_type; | 60 | u8 cmd_type; |
58 | u16 value; | 61 | u16 value; |
@@ -138,49 +141,33 @@ struct spcp8x5_usb_ctrl_arg { | |||
138 | #define UART_OVERRUN_ERROR 0x40 | 141 | #define UART_OVERRUN_ERROR 0x40 |
139 | #define UART_CTS 0x80 | 142 | #define UART_CTS 0x80 |
140 | 143 | ||
141 | enum spcp8x5_type { | ||
142 | SPCP825_007_TYPE, | ||
143 | SPCP825_008_TYPE, | ||
144 | SPCP825_PHILIP_TYPE, | ||
145 | SPCP825_INTERMATIC_TYPE, | ||
146 | SPCP835_TYPE, | ||
147 | }; | ||
148 | |||
149 | struct spcp8x5_private { | 144 | struct spcp8x5_private { |
150 | spinlock_t lock; | 145 | unsigned quirks; |
151 | enum spcp8x5_type type; | 146 | spinlock_t lock; |
152 | u8 line_control; | 147 | u8 line_control; |
153 | u8 line_status; | ||
154 | }; | 148 | }; |
155 | 149 | ||
150 | static int spcp8x5_probe(struct usb_serial *serial, | ||
151 | const struct usb_device_id *id) | ||
152 | { | ||
153 | usb_set_serial_data(serial, (void *)id); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
156 | static int spcp8x5_port_probe(struct usb_serial_port *port) | 158 | static int spcp8x5_port_probe(struct usb_serial_port *port) |
157 | { | 159 | { |
158 | struct usb_serial *serial = port->serial; | 160 | const struct usb_device_id *id = usb_get_serial_data(port->serial); |
159 | struct spcp8x5_private *priv; | 161 | struct spcp8x5_private *priv; |
160 | enum spcp8x5_type type = SPCP825_007_TYPE; | ||
161 | u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); | ||
162 | |||
163 | if (product == 0x0201) | ||
164 | type = SPCP825_007_TYPE; | ||
165 | else if (product == 0x0231) | ||
166 | type = SPCP835_TYPE; | ||
167 | else if (product == 0x0235) | ||
168 | type = SPCP825_008_TYPE; | ||
169 | else if (product == 0x0204) | ||
170 | type = SPCP825_INTERMATIC_TYPE; | ||
171 | else if (product == 0x0471 && | ||
172 | serial->dev->descriptor.idVendor == cpu_to_le16(0x081e)) | ||
173 | type = SPCP825_PHILIP_TYPE; | ||
174 | dev_dbg(&serial->dev->dev, "device type = %d\n", (int)type); | ||
175 | 162 | ||
176 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | 163 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
177 | if (!priv) | 164 | if (!priv) |
178 | return -ENOMEM; | 165 | return -ENOMEM; |
179 | 166 | ||
180 | spin_lock_init(&priv->lock); | 167 | spin_lock_init(&priv->lock); |
181 | priv->type = type; | 168 | priv->quirks = id->driver_info; |
182 | 169 | ||
183 | usb_set_serial_port_data(port , priv); | 170 | usb_set_serial_port_data(port, priv); |
184 | 171 | ||
185 | return 0; | 172 | return 0; |
186 | } | 173 | } |
@@ -195,86 +182,79 @@ static int spcp8x5_port_remove(struct usb_serial_port *port) | |||
195 | return 0; | 182 | return 0; |
196 | } | 183 | } |
197 | 184 | ||
198 | /* set the modem control line of the device. | 185 | static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) |
199 | * NOTE spcp825-007 not supported this */ | ||
200 | static int spcp8x5_set_ctrlLine(struct usb_device *dev, u8 value, | ||
201 | enum spcp8x5_type type) | ||
202 | { | 186 | { |
187 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | ||
188 | struct usb_device *dev = port->serial->dev; | ||
203 | int retval; | 189 | int retval; |
204 | u8 mcr = 0 ; | ||
205 | 190 | ||
206 | if (type == SPCP825_007_TYPE) | 191 | if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) |
207 | return -EPERM; | 192 | return -EPERM; |
208 | 193 | ||
209 | mcr = (unsigned short)value; | ||
210 | retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | 194 | retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
211 | SET_UART_STATUS_TYPE, SET_UART_STATUS, | 195 | SET_UART_STATUS_TYPE, SET_UART_STATUS, |
212 | mcr, 0x04, NULL, 0, 100); | 196 | mcr, 0x04, NULL, 0, 100); |
213 | if (retval != 0) | 197 | if (retval != 0) { |
214 | dev_dbg(&dev->dev, "usb_control_msg return %#x\n", retval); | 198 | dev_err(&port->dev, "failed to set control lines: %d\n", |
199 | retval); | ||
200 | } | ||
215 | return retval; | 201 | return retval; |
216 | } | 202 | } |
217 | 203 | ||
218 | /* get the modem status register of the device | 204 | static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) |
219 | * NOTE spcp825-007 not supported this */ | ||
220 | static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, | ||
221 | enum spcp8x5_type type) | ||
222 | { | 205 | { |
223 | u8 *status_buffer; | 206 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
207 | struct usb_device *dev = port->serial->dev; | ||
208 | u8 *buf; | ||
224 | int ret; | 209 | int ret; |
225 | 210 | ||
226 | /* I return Permited not support here but seem inval device | 211 | if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) |
227 | * is more fix */ | ||
228 | if (type == SPCP825_007_TYPE) | ||
229 | return -EPERM; | 212 | return -EPERM; |
230 | if (status == NULL) | ||
231 | return -EINVAL; | ||
232 | 213 | ||
233 | status_buffer = kmalloc(1, GFP_KERNEL); | 214 | buf = kzalloc(1, GFP_KERNEL); |
234 | if (!status_buffer) | 215 | if (!buf) |
235 | return -ENOMEM; | 216 | return -ENOMEM; |
236 | status_buffer[0] = status[0]; | ||
237 | 217 | ||
238 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | 218 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
239 | GET_UART_STATUS, GET_UART_STATUS_TYPE, | 219 | GET_UART_STATUS, GET_UART_STATUS_TYPE, |
240 | 0, GET_UART_STATUS_MSR, status_buffer, 1, 100); | 220 | 0, GET_UART_STATUS_MSR, buf, 1, 100); |
241 | if (ret < 0) | 221 | if (ret < 0) |
242 | dev_dbg(&dev->dev, "Get MSR = 0x%p failed (error = %d)", | 222 | dev_err(&port->dev, "failed to get modem status: %d", ret); |
243 | status_buffer, ret); | ||
244 | 223 | ||
245 | dev_dbg(&dev->dev, "0xc0:0x22:0:6 %d - 0x%p ", ret, status_buffer); | 224 | dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *buf); |
246 | status[0] = status_buffer[0]; | 225 | *status = *buf; |
247 | kfree(status_buffer); | 226 | kfree(buf); |
248 | 227 | ||
249 | return ret; | 228 | return ret; |
250 | } | 229 | } |
251 | 230 | ||
252 | /* select the work mode. | 231 | static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, |
253 | * NOTE this function not supported by spcp825-007 */ | 232 | u16 index) |
254 | static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, | ||
255 | u16 index, enum spcp8x5_type type) | ||
256 | { | 233 | { |
234 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | ||
235 | struct usb_device *dev = port->serial->dev; | ||
257 | int ret; | 236 | int ret; |
258 | 237 | ||
259 | /* I return Permited not support here but seem inval device | 238 | if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE) |
260 | * is more fix */ | ||
261 | if (type == SPCP825_007_TYPE) | ||
262 | return; | 239 | return; |
263 | 240 | ||
264 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | 241 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
265 | SET_WORKING_MODE_TYPE, SET_WORKING_MODE, | 242 | SET_WORKING_MODE_TYPE, SET_WORKING_MODE, |
266 | value, index, NULL, 0, 100); | 243 | value, index, NULL, 0, 100); |
267 | dev_dbg(&dev->dev, "value = %#x , index = %#x\n", value, index); | 244 | dev_dbg(&port->dev, "value = %#x , index = %#x\n", value, index); |
268 | if (ret < 0) | 245 | if (ret < 0) |
269 | dev_dbg(&dev->dev, | 246 | dev_err(&port->dev, "failed to set work mode: %d\n", ret); |
270 | "RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret); | ||
271 | } | 247 | } |
272 | 248 | ||
273 | static int spcp8x5_carrier_raised(struct usb_serial_port *port) | 249 | static int spcp8x5_carrier_raised(struct usb_serial_port *port) |
274 | { | 250 | { |
275 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | 251 | u8 msr; |
276 | if (priv->line_status & MSR_STATUS_LINE_DCD) | 252 | int ret; |
253 | |||
254 | ret = spcp8x5_get_msr(port, &msr); | ||
255 | if (ret || msr & MSR_STATUS_LINE_DCD) | ||
277 | return 1; | 256 | return 1; |
257 | |||
278 | return 0; | 258 | return 0; |
279 | } | 259 | } |
280 | 260 | ||
@@ -293,20 +273,17 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) | |||
293 | | MCR_CONTROL_LINE_RTS); | 273 | | MCR_CONTROL_LINE_RTS); |
294 | control = priv->line_control; | 274 | control = priv->line_control; |
295 | spin_unlock_irqrestore(&priv->lock, flags); | 275 | spin_unlock_irqrestore(&priv->lock, flags); |
296 | spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); | 276 | spcp8x5_set_ctrl_line(port, control); |
297 | } | 277 | } |
298 | 278 | ||
299 | static void spcp8x5_init_termios(struct tty_struct *tty) | 279 | static void spcp8x5_init_termios(struct tty_struct *tty) |
300 | { | 280 | { |
301 | /* for the 1st time call this function */ | ||
302 | tty->termios = tty_std_termios; | 281 | tty->termios = tty_std_termios; |
303 | tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; | 282 | tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; |
304 | tty->termios.c_ispeed = 115200; | 283 | tty->termios.c_ispeed = 115200; |
305 | tty->termios.c_ospeed = 115200; | 284 | tty->termios.c_ospeed = 115200; |
306 | } | 285 | } |
307 | 286 | ||
308 | /* set the serial param for transfer. we should check if we really need to | ||
309 | * transfer. if we set flow control we should do this too. */ | ||
310 | static void spcp8x5_set_termios(struct tty_struct *tty, | 287 | static void spcp8x5_set_termios(struct tty_struct *tty, |
311 | struct usb_serial_port *port, struct ktermios *old_termios) | 288 | struct usb_serial_port *port, struct ktermios *old_termios) |
312 | { | 289 | { |
@@ -321,7 +298,6 @@ static void spcp8x5_set_termios(struct tty_struct *tty, | |||
321 | int i; | 298 | int i; |
322 | u8 control; | 299 | u8 control; |
323 | 300 | ||
324 | |||
325 | /* check that they really want us to change something */ | 301 | /* check that they really want us to change something */ |
326 | if (!tty_termios_hw_change(&tty->termios, old_termios)) | 302 | if (!tty_termios_hw_change(&tty->termios, old_termios)) |
327 | return; | 303 | return; |
@@ -337,7 +313,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, | |||
337 | if (control != priv->line_control) { | 313 | if (control != priv->line_control) { |
338 | control = priv->line_control; | 314 | control = priv->line_control; |
339 | spin_unlock_irqrestore(&priv->lock, flags); | 315 | spin_unlock_irqrestore(&priv->lock, flags); |
340 | spcp8x5_set_ctrlLine(serial->dev, control , priv->type); | 316 | spcp8x5_set_ctrl_line(port, control); |
341 | } else { | 317 | } else { |
342 | spin_unlock_irqrestore(&priv->lock, flags); | 318 | spin_unlock_irqrestore(&priv->lock, flags); |
343 | } | 319 | } |
@@ -397,9 +373,9 @@ static void spcp8x5_set_termios(struct tty_struct *tty, | |||
397 | if (cflag & PARENB) { | 373 | if (cflag & PARENB) { |
398 | buf[1] |= (cflag & PARODD) ? | 374 | buf[1] |= (cflag & PARODD) ? |
399 | SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ; | 375 | SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ; |
400 | } else | 376 | } else { |
401 | buf[1] |= SET_UART_FORMAT_PAR_NONE; | 377 | buf[1] |= SET_UART_FORMAT_PAR_NONE; |
402 | 378 | } | |
403 | uartdata = buf[0] | buf[1]<<8; | 379 | uartdata = buf[0] | buf[1]<<8; |
404 | 380 | ||
405 | i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | 381 | i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
@@ -412,22 +388,16 @@ static void spcp8x5_set_termios(struct tty_struct *tty, | |||
412 | 388 | ||
413 | if (cflag & CRTSCTS) { | 389 | if (cflag & CRTSCTS) { |
414 | /* enable hardware flow control */ | 390 | /* enable hardware flow control */ |
415 | spcp8x5_set_workMode(serial->dev, 0x000a, | 391 | spcp8x5_set_work_mode(port, 0x000a, SET_WORKING_MODE_U2C); |
416 | SET_WORKING_MODE_U2C, priv->type); | ||
417 | } | 392 | } |
418 | } | 393 | } |
419 | 394 | ||
420 | /* open the serial port. do some usb system call. set termios and get the line | ||
421 | * status of the device. */ | ||
422 | static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) | 395 | static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) |
423 | { | 396 | { |
424 | struct ktermios tmp_termios; | 397 | struct ktermios tmp_termios; |
425 | struct usb_serial *serial = port->serial; | 398 | struct usb_serial *serial = port->serial; |
426 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | 399 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
427 | int ret; | 400 | int ret; |
428 | unsigned long flags; | ||
429 | u8 status = 0x30; | ||
430 | /* status 0x30 means DSR and CTS = 1 other CDC RI and delta = 0 */ | ||
431 | 401 | ||
432 | usb_clear_halt(serial->dev, port->write_urb->pipe); | 402 | usb_clear_halt(serial->dev, port->write_urb->pipe); |
433 | usb_clear_halt(serial->dev, port->read_urb->pipe); | 403 | usb_clear_halt(serial->dev, port->read_urb->pipe); |
@@ -438,142 +408,16 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
438 | if (ret) | 408 | if (ret) |
439 | return ret; | 409 | return ret; |
440 | 410 | ||
441 | spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); | 411 | spcp8x5_set_ctrl_line(port, priv->line_control); |
442 | 412 | ||
443 | /* Setup termios */ | ||
444 | if (tty) | 413 | if (tty) |
445 | spcp8x5_set_termios(tty, port, &tmp_termios); | 414 | spcp8x5_set_termios(tty, port, &tmp_termios); |
446 | 415 | ||
447 | spcp8x5_get_msr(serial->dev, &status, priv->type); | ||
448 | |||
449 | /* may be we should update uart status here but now we did not do */ | ||
450 | spin_lock_irqsave(&priv->lock, flags); | ||
451 | priv->line_status = status & 0xf0 ; | ||
452 | spin_unlock_irqrestore(&priv->lock, flags); | ||
453 | |||
454 | port->port.drain_delay = 256; | 416 | port->port.drain_delay = 256; |
455 | 417 | ||
456 | return usb_serial_generic_open(tty, port); | 418 | return usb_serial_generic_open(tty, port); |
457 | } | 419 | } |
458 | 420 | ||
459 | static void spcp8x5_process_read_urb(struct urb *urb) | ||
460 | { | ||
461 | struct usb_serial_port *port = urb->context; | ||
462 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | ||
463 | unsigned char *data = urb->transfer_buffer; | ||
464 | unsigned long flags; | ||
465 | u8 status; | ||
466 | char tty_flag; | ||
467 | |||
468 | /* get tty_flag from status */ | ||
469 | tty_flag = TTY_NORMAL; | ||
470 | |||
471 | spin_lock_irqsave(&priv->lock, flags); | ||
472 | status = priv->line_status; | ||
473 | priv->line_status &= ~UART_STATE_TRANSIENT_MASK; | ||
474 | spin_unlock_irqrestore(&priv->lock, flags); | ||
475 | /* wake up the wait for termios */ | ||
476 | wake_up_interruptible(&port->delta_msr_wait); | ||
477 | |||
478 | if (!urb->actual_length) | ||
479 | return; | ||
480 | |||
481 | |||
482 | if (status & UART_STATE_TRANSIENT_MASK) { | ||
483 | /* break takes precedence over parity, which takes precedence | ||
484 | * over framing errors */ | ||
485 | if (status & UART_BREAK_ERROR) | ||
486 | tty_flag = TTY_BREAK; | ||
487 | else if (status & UART_PARITY_ERROR) | ||
488 | tty_flag = TTY_PARITY; | ||
489 | else if (status & UART_FRAME_ERROR) | ||
490 | tty_flag = TTY_FRAME; | ||
491 | dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); | ||
492 | |||
493 | /* overrun is special, not associated with a char */ | ||
494 | if (status & UART_OVERRUN_ERROR) | ||
495 | tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); | ||
496 | |||
497 | if (status & UART_DCD) { | ||
498 | struct tty_struct *tty = tty_port_tty_get(&port->port); | ||
499 | if (tty) { | ||
500 | usb_serial_handle_dcd_change(port, tty, | ||
501 | priv->line_status & MSR_STATUS_LINE_DCD); | ||
502 | tty_kref_put(tty); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag, | ||
508 | urb->actual_length); | ||
509 | tty_flip_buffer_push(&port->port); | ||
510 | } | ||
511 | |||
512 | static int spcp8x5_wait_modem_info(struct usb_serial_port *port, | ||
513 | unsigned int arg) | ||
514 | { | ||
515 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | ||
516 | unsigned long flags; | ||
517 | unsigned int prevstatus; | ||
518 | unsigned int status; | ||
519 | unsigned int changed; | ||
520 | |||
521 | spin_lock_irqsave(&priv->lock, flags); | ||
522 | prevstatus = priv->line_status; | ||
523 | spin_unlock_irqrestore(&priv->lock, flags); | ||
524 | |||
525 | while (1) { | ||
526 | /* wake up in bulk read */ | ||
527 | interruptible_sleep_on(&port->delta_msr_wait); | ||
528 | |||
529 | /* see if a signal did it */ | ||
530 | if (signal_pending(current)) | ||
531 | return -ERESTARTSYS; | ||
532 | |||
533 | if (port->serial->disconnected) | ||
534 | return -EIO; | ||
535 | |||
536 | spin_lock_irqsave(&priv->lock, flags); | ||
537 | status = priv->line_status; | ||
538 | spin_unlock_irqrestore(&priv->lock, flags); | ||
539 | |||
540 | changed = prevstatus^status; | ||
541 | |||
542 | if (((arg & TIOCM_RNG) && (changed & MSR_STATUS_LINE_RI)) || | ||
543 | ((arg & TIOCM_DSR) && (changed & MSR_STATUS_LINE_DSR)) || | ||
544 | ((arg & TIOCM_CD) && (changed & MSR_STATUS_LINE_DCD)) || | ||
545 | ((arg & TIOCM_CTS) && (changed & MSR_STATUS_LINE_CTS))) | ||
546 | return 0; | ||
547 | |||
548 | prevstatus = status; | ||
549 | } | ||
550 | /* NOTREACHED */ | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | static int spcp8x5_ioctl(struct tty_struct *tty, | ||
555 | unsigned int cmd, unsigned long arg) | ||
556 | { | ||
557 | struct usb_serial_port *port = tty->driver_data; | ||
558 | |||
559 | dev_dbg(&port->dev, "%s (%d) cmd = 0x%04x\n", __func__, | ||
560 | port->number, cmd); | ||
561 | |||
562 | switch (cmd) { | ||
563 | case TIOCMIWAIT: | ||
564 | dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, | ||
565 | port->number); | ||
566 | return spcp8x5_wait_modem_info(port, arg); | ||
567 | |||
568 | default: | ||
569 | dev_dbg(&port->dev, "%s not supported = 0x%04x", __func__, | ||
570 | cmd); | ||
571 | break; | ||
572 | } | ||
573 | |||
574 | return -ENOIOCTLCMD; | ||
575 | } | ||
576 | |||
577 | static int spcp8x5_tiocmset(struct tty_struct *tty, | 421 | static int spcp8x5_tiocmset(struct tty_struct *tty, |
578 | unsigned int set, unsigned int clear) | 422 | unsigned int set, unsigned int clear) |
579 | { | 423 | { |
@@ -594,7 +438,7 @@ static int spcp8x5_tiocmset(struct tty_struct *tty, | |||
594 | control = priv->line_control; | 438 | control = priv->line_control; |
595 | spin_unlock_irqrestore(&priv->lock, flags); | 439 | spin_unlock_irqrestore(&priv->lock, flags); |
596 | 440 | ||
597 | return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); | 441 | return spcp8x5_set_ctrl_line(port, control); |
598 | } | 442 | } |
599 | 443 | ||
600 | static int spcp8x5_tiocmget(struct tty_struct *tty) | 444 | static int spcp8x5_tiocmget(struct tty_struct *tty) |
@@ -603,12 +447,15 @@ static int spcp8x5_tiocmget(struct tty_struct *tty) | |||
603 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); | 447 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
604 | unsigned long flags; | 448 | unsigned long flags; |
605 | unsigned int mcr; | 449 | unsigned int mcr; |
606 | unsigned int status; | 450 | u8 status; |
607 | unsigned int result; | 451 | unsigned int result; |
608 | 452 | ||
453 | result = spcp8x5_get_msr(port, &status); | ||
454 | if (result) | ||
455 | return result; | ||
456 | |||
609 | spin_lock_irqsave(&priv->lock, flags); | 457 | spin_lock_irqsave(&priv->lock, flags); |
610 | mcr = priv->line_control; | 458 | mcr = priv->line_control; |
611 | status = priv->line_status; | ||
612 | spin_unlock_irqrestore(&priv->lock, flags); | 459 | spin_unlock_irqrestore(&priv->lock, flags); |
613 | 460 | ||
614 | result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | 461 | result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) |
@@ -621,7 +468,6 @@ static int spcp8x5_tiocmget(struct tty_struct *tty) | |||
621 | return result; | 468 | return result; |
622 | } | 469 | } |
623 | 470 | ||
624 | /* All of the device info needed for the spcp8x5 SIO serial converter */ | ||
625 | static struct usb_serial_driver spcp8x5_device = { | 471 | static struct usb_serial_driver spcp8x5_device = { |
626 | .driver = { | 472 | .driver = { |
627 | .owner = THIS_MODULE, | 473 | .owner = THIS_MODULE, |
@@ -629,17 +475,16 @@ static struct usb_serial_driver spcp8x5_device = { | |||
629 | }, | 475 | }, |
630 | .id_table = id_table, | 476 | .id_table = id_table, |
631 | .num_ports = 1, | 477 | .num_ports = 1, |
632 | .open = spcp8x5_open, | 478 | .open = spcp8x5_open, |
633 | .dtr_rts = spcp8x5_dtr_rts, | 479 | .dtr_rts = spcp8x5_dtr_rts, |
634 | .carrier_raised = spcp8x5_carrier_raised, | 480 | .carrier_raised = spcp8x5_carrier_raised, |
635 | .set_termios = spcp8x5_set_termios, | 481 | .set_termios = spcp8x5_set_termios, |
636 | .init_termios = spcp8x5_init_termios, | 482 | .init_termios = spcp8x5_init_termios, |
637 | .ioctl = spcp8x5_ioctl, | 483 | .tiocmget = spcp8x5_tiocmget, |
638 | .tiocmget = spcp8x5_tiocmget, | 484 | .tiocmset = spcp8x5_tiocmset, |
639 | .tiocmset = spcp8x5_tiocmset, | 485 | .probe = spcp8x5_probe, |
640 | .port_probe = spcp8x5_port_probe, | 486 | .port_probe = spcp8x5_port_probe, |
641 | .port_remove = spcp8x5_port_remove, | 487 | .port_remove = spcp8x5_port_remove, |
642 | .process_read_urb = spcp8x5_process_read_urb, | ||
643 | }; | 488 | }; |
644 | 489 | ||
645 | static struct usb_serial_driver * const serial_drivers[] = { | 490 | static struct usb_serial_driver * const serial_drivers[] = { |