forked from mirrors/linux
		
	cdc-acm: add TIOCMIWAIT
This implements TIOCMIWAIT for TIOCM_DSR, TIOCM_RI and TIOCM_CD Disconnect is handled as TIOCM_CD or an error. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									4e065b8bba
								
							
						
					
					
						commit
						5a6a62bdb9
					
				
					 2 changed files with 77 additions and 12 deletions
				
			
		|  | @ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb) | |||
| 	struct usb_cdc_notification *dr = urb->transfer_buffer; | ||||
| 	unsigned char *data; | ||||
| 	int newctrl; | ||||
| 	int difference; | ||||
| 	int retval; | ||||
| 	int status = urb->status; | ||||
| 
 | ||||
|  | @ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb) | |||
| 			tty_port_tty_hangup(&acm->port, false); | ||||
| 		} | ||||
| 
 | ||||
| 		difference = acm->ctrlin ^ newctrl; | ||||
| 		spin_lock(&acm->read_lock); | ||||
| 		acm->ctrlin = newctrl; | ||||
| 		acm->oldcount = acm->iocount; | ||||
| 
 | ||||
| 		dev_dbg(&acm->control->dev, | ||||
| 			"%s - input control lines: dcd%c dsr%c break%c " | ||||
| 			"ring%c framing%c parity%c overrun%c\n", | ||||
| 			__func__, | ||||
| 			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', | ||||
| 			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); | ||||
| 			break; | ||||
| 		if (difference & ACM_CTRL_DSR) | ||||
| 			acm->iocount.dsr++; | ||||
| 		if (difference & ACM_CTRL_BRK) | ||||
| 			acm->iocount.brk++; | ||||
| 		if (difference & ACM_CTRL_RI) | ||||
| 			acm->iocount.rng++; | ||||
| 		if (difference & ACM_CTRL_DCD) | ||||
| 			acm->iocount.dcd++; | ||||
| 		if (difference & ACM_CTRL_FRAMING) | ||||
| 			acm->iocount.frame++; | ||||
| 		if (difference & ACM_CTRL_PARITY) | ||||
| 			acm->iocount.parity++; | ||||
| 		if (difference & ACM_CTRL_OVERRUN) | ||||
| 			acm->iocount.overrun++; | ||||
| 		spin_unlock(&acm->read_lock); | ||||
| 
 | ||||
| 		if (difference) | ||||
| 			wake_up_all(&acm->wioctl); | ||||
| 
 | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		dev_dbg(&acm->control->dev, | ||||
|  | @ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm, | |||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| static int wait_serial_change(struct acm *acm, unsigned long arg) | ||||
| { | ||||
| 	int rv = 0; | ||||
| 	DECLARE_WAITQUEUE(wait, current); | ||||
| 	struct async_icount old, new; | ||||
| 
 | ||||
| 	if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD )) | ||||
| 		return -EINVAL; | ||||
| 	do { | ||||
| 		spin_lock_irq(&acm->read_lock); | ||||
| 		old = acm->oldcount; | ||||
| 		new = acm->iocount; | ||||
| 		acm->oldcount = new; | ||||
| 		spin_unlock_irq(&acm->read_lock); | ||||
| 
 | ||||
| 		if ((arg & TIOCM_DSR) && | ||||
| 			old.dsr != new.dsr) | ||||
| 			break; | ||||
| 		if ((arg & TIOCM_CD)  && | ||||
| 			old.dcd != new.dcd) | ||||
| 			break; | ||||
| 		if ((arg & TIOCM_RI) && | ||||
| 			old.rng != new.rng) | ||||
| 			break; | ||||
| 
 | ||||
| 		add_wait_queue(&acm->wioctl, &wait); | ||||
| 		set_current_state(TASK_INTERRUPTIBLE); | ||||
| 		schedule(); | ||||
| 		remove_wait_queue(&acm->wioctl, &wait); | ||||
| 		if (acm->disconnected) { | ||||
| 			if (arg & TIOCM_CD) | ||||
| 				break; | ||||
| 			else | ||||
| 				rv = -ENODEV; | ||||
| 		} else { | ||||
| 			if (signal_pending(current)) | ||||
| 				rv = -ERESTARTSYS; | ||||
| 		} | ||||
| 	} while (!rv); | ||||
| 
 | ||||
| 	 | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| 
 | ||||
| static int acm_tty_ioctl(struct tty_struct *tty, | ||||
| 					unsigned int cmd, unsigned long arg) | ||||
| { | ||||
|  | @ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty, | |||
| 	case TIOCSSERIAL: | ||||
| 		rv = set_serial_info(acm, (struct serial_struct __user *) arg); | ||||
| 		break; | ||||
| 	case TIOCMIWAIT: | ||||
| 		rv = wait_serial_change(acm, arg); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return rv; | ||||
|  | @ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf, | |||
| 	acm->readsize = readsize; | ||||
| 	acm->rx_buflimit = num_rx_buf; | ||||
| 	INIT_WORK(&acm->work, acm_softint); | ||||
| 	init_waitqueue_head(&acm->wioctl); | ||||
| 	spin_lock_init(&acm->write_lock); | ||||
| 	spin_lock_init(&acm->read_lock); | ||||
| 	mutex_init(&acm->mutex); | ||||
|  | @ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf) | |||
| 		device_remove_file(&acm->control->dev, | ||||
| 				&dev_attr_iCountryCodeRelDate); | ||||
| 	} | ||||
| 	wake_up_all(&acm->wioctl); | ||||
| 	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); | ||||
| 	usb_set_intfdata(acm->control, NULL); | ||||
| 	usb_set_intfdata(acm->data, NULL); | ||||
|  |  | |||
|  | @ -106,6 +106,9 @@ struct acm { | |||
| 	struct work_struct work;			/* work queue entry for line discipline waking up */ | ||||
| 	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */ | ||||
| 	unsigned int ctrlout;				/* output control lines (DTR, RTS) */ | ||||
| 	struct async_icount iocount;			/* counters for control line changes */ | ||||
| 	struct async_icount oldcount;			/* for comparison of counter */ | ||||
| 	wait_queue_head_t wioctl;			/* for ioctl */ | ||||
| 	unsigned int writesize;				/* max packet size for the output bulk endpoint */ | ||||
| 	unsigned int readsize,ctrlsize;			/* buffer sizes for freeing */ | ||||
| 	unsigned int minor;				/* acm minor number */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Oliver Neukum
						Oliver Neukum