diff options
Diffstat (limited to 'drivers/usb/serial/ftdi_sio.c')
| -rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 36 | 
1 files changed, 20 insertions, 16 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ff8605b4b4b..02e7f2d32d5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -75,7 +75,8 @@ struct ftdi_private {  	unsigned long last_dtr_rts;	/* saved modem control outputs */  	struct async_icount	icount;  	wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ -	char prev_status, diff_status;        /* Used for TIOCMIWAIT */ +	char prev_status;        /* Used for TIOCMIWAIT */ +	bool dev_gone;        /* Used to abort TIOCMIWAIT */  	char transmit_empty;	/* If transmitter is empty or not */  	struct usb_serial_port *port;  	__u16 interface;	/* FT2232C, FT2232H or FT4232H port interface @@ -1681,6 +1682,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)  	init_waitqueue_head(&priv->delta_msr_wait);  	priv->flags = ASYNC_LOW_LATENCY; +	priv->dev_gone = false;  	if (quirk && quirk->port_probe)  		quirk->port_probe(priv); @@ -1839,6 +1841,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)  	dbg("%s", __func__); +	priv->dev_gone = true; +	wake_up_interruptible_all(&priv->delta_msr_wait); +  	remove_sysfs_attrs(port);  	kref_put(&priv->kref, ftdi_sio_priv_release); @@ -1982,17 +1987,19 @@ static int ftdi_process_packet(struct tty_struct *tty,  	   N.B. packet may be processed more than once, but differences  	   are only processed once.  */  	status = packet[0] & FTDI_STATUS_B0_MASK; -	if (status & FTDI_RS0_CTS) -		priv->icount.cts++; -	if (status & FTDI_RS0_DSR) -		priv->icount.dsr++; -	if (status & FTDI_RS0_RI) -		priv->icount.rng++; -	if (status & FTDI_RS0_RLSD) -		priv->icount.dcd++;  	if (status != priv->prev_status) { -		priv->diff_status |= status ^ priv->prev_status; -		wake_up_interruptible(&priv->delta_msr_wait); +		char diff_status = status ^ priv->prev_status; + +		if (diff_status & FTDI_RS0_CTS) +			priv->icount.cts++; +		if (diff_status & FTDI_RS0_DSR) +			priv->icount.dsr++; +		if (diff_status & FTDI_RS0_RI) +			priv->icount.rng++; +		if (diff_status & FTDI_RS0_RLSD) +			priv->icount.dcd++; + +		wake_up_interruptible_all(&priv->delta_msr_wait);  		priv->prev_status = status;  	} @@ -2395,15 +2402,12 @@ static int ftdi_ioctl(struct tty_struct *tty,  	 */  	case TIOCMIWAIT:  		cprev = priv->icount; -		while (1) { +		while (!priv->dev_gone) {  			interruptible_sleep_on(&priv->delta_msr_wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS;  			cnow = priv->icount; -			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && -			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) -				return -EIO; /* no change => error */  			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||  			    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||  			    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) || @@ -2412,7 +2416,7 @@ static int ftdi_ioctl(struct tty_struct *tty,  			}  			cprev = cnow;  		} -		/* not reached */ +		return -EIO;  		break;  	case TIOCSERGETLSR:  		return get_lsr_info(port, (struct serial_struct __user *)arg);  |