diff options
author | Bill Pemberton <wfp5p@virginia.edu> | 2010-08-05 17:01:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-23 23:50:16 -0400 |
commit | f81c83db563334d8377b26ad45585261f604605a (patch) | |
tree | c6566eb22756dc8da59da413d3287443dbae5ba0 /drivers/usb/serial/ssu100.c | |
parent | 556f1a0e9c178193e584209b47cf1cb9f669bd51 (diff) |
USB: ssu100: rework logic for TIOCMIWAIT
Rework the logic for TIOCMIWAIT to use wait_event_interruptible.
This also adds support for TIOCGICOUNT.
Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/ssu100.c')
-rw-r--r-- | drivers/usb/serial/ssu100.c | 146 |
1 files changed, 111 insertions, 35 deletions
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index ad5f9ae40687..e244491b1a0d 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c | |||
@@ -80,6 +80,7 @@ struct ssu100_port_private { | |||
80 | u8 shadowMSR; | 80 | u8 shadowMSR; |
81 | wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ | 81 | wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ |
82 | unsigned short max_packet_size; | 82 | unsigned short max_packet_size; |
83 | struct async_icount icount; | ||
83 | }; | 84 | }; |
84 | 85 | ||
85 | static void ssu100_release(struct usb_serial *serial) | 86 | static void ssu100_release(struct usb_serial *serial) |
@@ -330,11 +331,8 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
330 | } | 331 | } |
331 | 332 | ||
332 | spin_lock_irqsave(&priv->status_lock, flags); | 333 | spin_lock_irqsave(&priv->status_lock, flags); |
333 | priv->shadowLSR = data[0] & (UART_LSR_OE | UART_LSR_PE | | 334 | priv->shadowLSR = data[0]; |
334 | UART_LSR_FE | UART_LSR_BI); | 335 | priv->shadowMSR = data[1]; |
335 | |||
336 | priv->shadowMSR = data[1] & (UART_MSR_CTS | UART_MSR_DSR | | ||
337 | UART_MSR_RI | UART_MSR_DCD); | ||
338 | spin_unlock_irqrestore(&priv->status_lock, flags); | 336 | spin_unlock_irqrestore(&priv->status_lock, flags); |
339 | 337 | ||
340 | kfree(data); | 338 | kfree(data); |
@@ -379,11 +377,51 @@ static int get_serial_info(struct usb_serial_port *port, | |||
379 | return 0; | 377 | return 0; |
380 | } | 378 | } |
381 | 379 | ||
380 | static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) | ||
381 | { | ||
382 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
383 | struct async_icount prev, cur; | ||
384 | unsigned long flags; | ||
385 | |||
386 | spin_lock_irqsave(&priv->status_lock, flags); | ||
387 | prev = priv->icount; | ||
388 | spin_unlock_irqrestore(&priv->status_lock, flags); | ||
389 | |||
390 | while (1) { | ||
391 | wait_event_interruptible(priv->delta_msr_wait, | ||
392 | ((priv->icount.rng != prev.rng) || | ||
393 | (priv->icount.dsr != prev.dsr) || | ||
394 | (priv->icount.dcd != prev.dcd) || | ||
395 | (priv->icount.cts != prev.cts))); | ||
396 | |||
397 | if (signal_pending(current)) | ||
398 | return -ERESTARTSYS; | ||
399 | |||
400 | spin_lock_irqsave(&priv->status_lock, flags); | ||
401 | cur = priv->icount; | ||
402 | spin_unlock_irqrestore(&priv->status_lock, flags); | ||
403 | |||
404 | if ((prev.rng == cur.rng) && | ||
405 | (prev.dsr == cur.dsr) && | ||
406 | (prev.dcd == cur.dcd) && | ||
407 | (prev.cts == cur.cts)) | ||
408 | return -EIO; | ||
409 | |||
410 | if ((arg & TIOCM_RNG && (prev.rng != cur.rng)) || | ||
411 | (arg & TIOCM_DSR && (prev.dsr != cur.dsr)) || | ||
412 | (arg & TIOCM_CD && (prev.dcd != cur.dcd)) || | ||
413 | (arg & TIOCM_CTS && (prev.cts != cur.cts))) | ||
414 | return 0; | ||
415 | } | ||
416 | return 0; | ||
417 | } | ||
418 | |||
382 | static int ssu100_ioctl(struct tty_struct *tty, struct file *file, | 419 | static int ssu100_ioctl(struct tty_struct *tty, struct file *file, |
383 | unsigned int cmd, unsigned long arg) | 420 | unsigned int cmd, unsigned long arg) |
384 | { | 421 | { |
385 | struct usb_serial_port *port = tty->driver_data; | 422 | struct usb_serial_port *port = tty->driver_data; |
386 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | 423 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); |
424 | void __user *user_arg = (void __user *)arg; | ||
387 | 425 | ||
388 | dbg("%s cmd 0x%04x", __func__, cmd); | 426 | dbg("%s cmd 0x%04x", __func__, cmd); |
389 | 427 | ||
@@ -393,28 +431,28 @@ static int ssu100_ioctl(struct tty_struct *tty, struct file *file, | |||
393 | (struct serial_struct __user *) arg); | 431 | (struct serial_struct __user *) arg); |
394 | 432 | ||
395 | case TIOCMIWAIT: | 433 | case TIOCMIWAIT: |
396 | while (priv != NULL) { | 434 | return wait_modem_info(port, arg); |
397 | u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK; | 435 | |
398 | interruptible_sleep_on(&priv->delta_msr_wait); | 436 | case TIOCGICOUNT: |
399 | /* see if a signal did it */ | 437 | { |
400 | if (signal_pending(current)) | 438 | struct serial_icounter_struct icount; |
401 | return -ERESTARTSYS; | 439 | struct async_icount cnow = priv->icount; |
402 | else { | 440 | memset(&icount, 0, sizeof(icount)); |
403 | u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR; | 441 | icount.cts = cnow.cts; |
404 | if (!diff) | 442 | icount.dsr = cnow.dsr; |
405 | return -EIO; /* no change => error */ | 443 | icount.rng = cnow.rng; |
406 | 444 | icount.dcd = cnow.dcd; | |
407 | /* Return 0 if caller wanted to know about | 445 | icount.rx = cnow.rx; |
408 | these bits */ | 446 | icount.tx = cnow.tx; |
409 | 447 | icount.frame = cnow.frame; | |
410 | if (((arg & TIOCM_RNG) && (diff & UART_MSR_RI)) || | 448 | icount.overrun = cnow.overrun; |
411 | ((arg & TIOCM_DSR) && (diff & UART_MSR_DSR)) || | 449 | icount.parity = cnow.parity; |
412 | ((arg & TIOCM_CD) && (diff & UART_MSR_DCD)) || | 450 | icount.brk = cnow.brk; |
413 | ((arg & TIOCM_CTS) && (diff & UART_MSR_CTS))) | 451 | icount.buf_overrun = cnow.buf_overrun; |
414 | return 0; | 452 | if (copy_to_user(user_arg, &icount, sizeof(icount))) |
415 | } | 453 | return -EFAULT; |
416 | } | ||
417 | return 0; | 454 | return 0; |
455 | } | ||
418 | 456 | ||
419 | default: | 457 | default: |
420 | break; | 458 | break; |
@@ -541,6 +579,50 @@ static void ssu100_dtr_rts(struct usb_serial_port *port, int on) | |||
541 | mutex_unlock(&port->serial->disc_mutex); | 579 | mutex_unlock(&port->serial->disc_mutex); |
542 | } | 580 | } |
543 | 581 | ||
582 | static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) | ||
583 | { | ||
584 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
585 | unsigned long flags; | ||
586 | |||
587 | spin_lock_irqsave(&priv->status_lock, flags); | ||
588 | priv->shadowMSR = msr; | ||
589 | spin_unlock_irqrestore(&priv->status_lock, flags); | ||
590 | |||
591 | if (msr & UART_MSR_ANY_DELTA) { | ||
592 | /* update input line counters */ | ||
593 | if (msr & UART_MSR_DCTS) | ||
594 | priv->icount.cts++; | ||
595 | if (msr & UART_MSR_DDSR) | ||
596 | priv->icount.dsr++; | ||
597 | if (msr & UART_MSR_DDCD) | ||
598 | priv->icount.dcd++; | ||
599 | if (msr & UART_MSR_TERI) | ||
600 | priv->icount.rng++; | ||
601 | wake_up_interruptible(&priv->delta_msr_wait); | ||
602 | } | ||
603 | } | ||
604 | |||
605 | static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr) | ||
606 | { | ||
607 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
608 | unsigned long flags; | ||
609 | |||
610 | spin_lock_irqsave(&priv->status_lock, flags); | ||
611 | priv->shadowLSR = lsr; | ||
612 | spin_unlock_irqrestore(&priv->status_lock, flags); | ||
613 | |||
614 | if (lsr & UART_LSR_BRK_ERROR_BITS) { | ||
615 | if (lsr & UART_LSR_BI) | ||
616 | priv->icount.brk++; | ||
617 | if (lsr & UART_LSR_FE) | ||
618 | priv->icount.frame++; | ||
619 | if (lsr & UART_LSR_PE) | ||
620 | priv->icount.parity++; | ||
621 | if (lsr & UART_LSR_OE) | ||
622 | priv->icount.overrun++; | ||
623 | } | ||
624 | } | ||
625 | |||
544 | static int ssu100_process_packet(struct tty_struct *tty, | 626 | static int ssu100_process_packet(struct tty_struct *tty, |
545 | struct usb_serial_port *port, | 627 | struct usb_serial_port *port, |
546 | struct ssu100_port_private *priv, | 628 | struct ssu100_port_private *priv, |
@@ -556,15 +638,9 @@ static int ssu100_process_packet(struct tty_struct *tty, | |||
556 | (packet[0] == 0x1b) && (packet[1] == 0x1b) && | 638 | (packet[0] == 0x1b) && (packet[1] == 0x1b) && |
557 | ((packet[2] == 0x00) || (packet[2] == 0x01))) { | 639 | ((packet[2] == 0x00) || (packet[2] == 0x01))) { |
558 | if (packet[2] == 0x00) | 640 | if (packet[2] == 0x00) |
559 | priv->shadowLSR = packet[3] & (UART_LSR_OE | | 641 | ssu100_update_lsr(port, packet[3]); |
560 | UART_LSR_PE | | 642 | if (packet[2] == 0x01) |
561 | UART_LSR_FE | | 643 | ssu100_update_msr(port, packet[3]); |
562 | UART_LSR_BI); | ||
563 | |||
564 | if (packet[2] == 0x01) { | ||
565 | priv->shadowMSR = packet[3]; | ||
566 | wake_up_interruptible(&priv->delta_msr_wait); | ||
567 | } | ||
568 | 644 | ||
569 | len -= 4; | 645 | len -= 4; |
570 | ch = packet + 4; | 646 | ch = packet + 4; |