提交 f81c83db 编写于 作者: B Bill Pemberton 提交者: Greg Kroah-Hartman

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: NBill Pemberton <wfp5p@virginia.edu>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 556f1a0e
...@@ -80,6 +80,7 @@ struct ssu100_port_private { ...@@ -80,6 +80,7 @@ struct ssu100_port_private {
u8 shadowMSR; u8 shadowMSR;
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
unsigned short max_packet_size; unsigned short max_packet_size;
struct async_icount icount;
}; };
static void ssu100_release(struct usb_serial *serial) 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,11 +331,8 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
} }
spin_lock_irqsave(&priv->status_lock, flags); spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowLSR = data[0] & (UART_LSR_OE | UART_LSR_PE | priv->shadowLSR = data[0];
UART_LSR_FE | UART_LSR_BI); priv->shadowMSR = data[1];
priv->shadowMSR = data[1] & (UART_MSR_CTS | UART_MSR_DSR |
UART_MSR_RI | UART_MSR_DCD);
spin_unlock_irqrestore(&priv->status_lock, flags); spin_unlock_irqrestore(&priv->status_lock, flags);
kfree(data); kfree(data);
...@@ -379,11 +377,51 @@ static int get_serial_info(struct usb_serial_port *port, ...@@ -379,11 +377,51 @@ static int get_serial_info(struct usb_serial_port *port,
return 0; return 0;
} }
static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
struct async_icount prev, cur;
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
prev = priv->icount;
spin_unlock_irqrestore(&priv->status_lock, flags);
while (1) {
wait_event_interruptible(priv->delta_msr_wait,
((priv->icount.rng != prev.rng) ||
(priv->icount.dsr != prev.dsr) ||
(priv->icount.dcd != prev.dcd) ||
(priv->icount.cts != prev.cts)));
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irqsave(&priv->status_lock, flags);
cur = priv->icount;
spin_unlock_irqrestore(&priv->status_lock, flags);
if ((prev.rng == cur.rng) &&
(prev.dsr == cur.dsr) &&
(prev.dcd == cur.dcd) &&
(prev.cts == cur.cts))
return -EIO;
if ((arg & TIOCM_RNG && (prev.rng != cur.rng)) ||
(arg & TIOCM_DSR && (prev.dsr != cur.dsr)) ||
(arg & TIOCM_CD && (prev.dcd != cur.dcd)) ||
(arg & TIOCM_CTS && (prev.cts != cur.cts)))
return 0;
}
return 0;
}
static int ssu100_ioctl(struct tty_struct *tty, struct file *file, static int ssu100_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct usb_serial_port *port = tty->driver_data; struct usb_serial_port *port = tty->driver_data;
struct ssu100_port_private *priv = usb_get_serial_port_data(port); struct ssu100_port_private *priv = usb_get_serial_port_data(port);
void __user *user_arg = (void __user *)arg;
dbg("%s cmd 0x%04x", __func__, cmd); dbg("%s cmd 0x%04x", __func__, cmd);
...@@ -393,28 +431,28 @@ static int ssu100_ioctl(struct tty_struct *tty, struct file *file, ...@@ -393,28 +431,28 @@ static int ssu100_ioctl(struct tty_struct *tty, struct file *file,
(struct serial_struct __user *) arg); (struct serial_struct __user *) arg);
case TIOCMIWAIT: case TIOCMIWAIT:
while (priv != NULL) { return wait_modem_info(port, arg);
u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK;
interruptible_sleep_on(&priv->delta_msr_wait); case TIOCGICOUNT:
/* see if a signal did it */ {
if (signal_pending(current)) struct serial_icounter_struct icount;
return -ERESTARTSYS; struct async_icount cnow = priv->icount;
else { memset(&icount, 0, sizeof(icount));
u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR; icount.cts = cnow.cts;
if (!diff) icount.dsr = cnow.dsr;
return -EIO; /* no change => error */ icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
/* Return 0 if caller wanted to know about icount.rx = cnow.rx;
these bits */ icount.tx = cnow.tx;
icount.frame = cnow.frame;
if (((arg & TIOCM_RNG) && (diff & UART_MSR_RI)) || icount.overrun = cnow.overrun;
((arg & TIOCM_DSR) && (diff & UART_MSR_DSR)) || icount.parity = cnow.parity;
((arg & TIOCM_CD) && (diff & UART_MSR_DCD)) || icount.brk = cnow.brk;
((arg & TIOCM_CTS) && (diff & UART_MSR_CTS))) icount.buf_overrun = cnow.buf_overrun;
if (copy_to_user(user_arg, &icount, sizeof(icount)))
return -EFAULT;
return 0; return 0;
} }
}
return 0;
default: default:
break; break;
...@@ -541,6 +579,50 @@ static void ssu100_dtr_rts(struct usb_serial_port *port, int on) ...@@ -541,6 +579,50 @@ static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
mutex_unlock(&port->serial->disc_mutex); mutex_unlock(&port->serial->disc_mutex);
} }
static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
{
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowMSR = msr;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (msr & UART_MSR_ANY_DELTA) {
/* update input line counters */
if (msr & UART_MSR_DCTS)
priv->icount.cts++;
if (msr & UART_MSR_DDSR)
priv->icount.dsr++;
if (msr & UART_MSR_DDCD)
priv->icount.dcd++;
if (msr & UART_MSR_TERI)
priv->icount.rng++;
wake_up_interruptible(&priv->delta_msr_wait);
}
}
static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr)
{
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowLSR = lsr;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (lsr & UART_LSR_BRK_ERROR_BITS) {
if (lsr & UART_LSR_BI)
priv->icount.brk++;
if (lsr & UART_LSR_FE)
priv->icount.frame++;
if (lsr & UART_LSR_PE)
priv->icount.parity++;
if (lsr & UART_LSR_OE)
priv->icount.overrun++;
}
}
static int ssu100_process_packet(struct tty_struct *tty, static int ssu100_process_packet(struct tty_struct *tty,
struct usb_serial_port *port, struct usb_serial_port *port,
struct ssu100_port_private *priv, struct ssu100_port_private *priv,
...@@ -556,15 +638,9 @@ static int ssu100_process_packet(struct tty_struct *tty, ...@@ -556,15 +638,9 @@ static int ssu100_process_packet(struct tty_struct *tty,
(packet[0] == 0x1b) && (packet[1] == 0x1b) && (packet[0] == 0x1b) && (packet[1] == 0x1b) &&
((packet[2] == 0x00) || (packet[2] == 0x01))) { ((packet[2] == 0x00) || (packet[2] == 0x01))) {
if (packet[2] == 0x00) if (packet[2] == 0x00)
priv->shadowLSR = packet[3] & (UART_LSR_OE | ssu100_update_lsr(port, packet[3]);
UART_LSR_PE | if (packet[2] == 0x01)
UART_LSR_FE | ssu100_update_msr(port, packet[3]);
UART_LSR_BI);
if (packet[2] == 0x01) {
priv->shadowMSR = packet[3];
wake_up_interruptible(&priv->delta_msr_wait);
}
len -= 4; len -= 4;
ch = packet + 4; ch = packet + 4;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册