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; | 	struct usb_cdc_notification *dr = urb->transfer_buffer; | ||||||
| 	unsigned char *data; | 	unsigned char *data; | ||||||
| 	int newctrl; | 	int newctrl; | ||||||
|  | 	int difference; | ||||||
| 	int retval; | 	int retval; | ||||||
| 	int status = urb->status; | 	int status = urb->status; | ||||||
| 
 | 
 | ||||||
|  | @ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb) | ||||||
| 			tty_port_tty_hangup(&acm->port, false); | 			tty_port_tty_hangup(&acm->port, false); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		difference = acm->ctrlin ^ newctrl; | ||||||
|  | 		spin_lock(&acm->read_lock); | ||||||
| 		acm->ctrlin = newctrl; | 		acm->ctrlin = newctrl; | ||||||
|  | 		acm->oldcount = acm->iocount; | ||||||
| 
 | 
 | ||||||
| 		dev_dbg(&acm->control->dev, | 		if (difference & ACM_CTRL_DSR) | ||||||
| 			"%s - input control lines: dcd%c dsr%c break%c " | 			acm->iocount.dsr++; | ||||||
| 			"ring%c framing%c parity%c overrun%c\n", | 		if (difference & ACM_CTRL_BRK) | ||||||
| 			__func__, | 			acm->iocount.brk++; | ||||||
| 			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', | 		if (difference & ACM_CTRL_RI) | ||||||
| 			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', | 			acm->iocount.rng++; | ||||||
| 			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', | 		if (difference & ACM_CTRL_DCD) | ||||||
| 			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-', | 			acm->iocount.dcd++; | ||||||
| 			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', | 		if (difference & ACM_CTRL_FRAMING) | ||||||
| 			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', | 			acm->iocount.frame++; | ||||||
| 			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); | 		if (difference & ACM_CTRL_PARITY) | ||||||
| 			break; | 			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: | 	default: | ||||||
| 		dev_dbg(&acm->control->dev, | 		dev_dbg(&acm->control->dev, | ||||||
|  | @ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm, | ||||||
| 	return retval; | 	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, | static int acm_tty_ioctl(struct tty_struct *tty, | ||||||
| 					unsigned int cmd, unsigned long arg) | 					unsigned int cmd, unsigned long arg) | ||||||
| { | { | ||||||
|  | @ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty, | ||||||
| 	case TIOCSSERIAL: | 	case TIOCSSERIAL: | ||||||
| 		rv = set_serial_info(acm, (struct serial_struct __user *) arg); | 		rv = set_serial_info(acm, (struct serial_struct __user *) arg); | ||||||
| 		break; | 		break; | ||||||
|  | 	case TIOCMIWAIT: | ||||||
|  | 		rv = wait_serial_change(acm, arg); | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return rv; | 	return rv; | ||||||
|  | @ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf, | ||||||
| 	acm->readsize = readsize; | 	acm->readsize = readsize; | ||||||
| 	acm->rx_buflimit = num_rx_buf; | 	acm->rx_buflimit = num_rx_buf; | ||||||
| 	INIT_WORK(&acm->work, acm_softint); | 	INIT_WORK(&acm->work, acm_softint); | ||||||
|  | 	init_waitqueue_head(&acm->wioctl); | ||||||
| 	spin_lock_init(&acm->write_lock); | 	spin_lock_init(&acm->write_lock); | ||||||
| 	spin_lock_init(&acm->read_lock); | 	spin_lock_init(&acm->read_lock); | ||||||
| 	mutex_init(&acm->mutex); | 	mutex_init(&acm->mutex); | ||||||
|  | @ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf) | ||||||
| 		device_remove_file(&acm->control->dev, | 		device_remove_file(&acm->control->dev, | ||||||
| 				&dev_attr_iCountryCodeRelDate); | 				&dev_attr_iCountryCodeRelDate); | ||||||
| 	} | 	} | ||||||
|  | 	wake_up_all(&acm->wioctl); | ||||||
| 	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); | 	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); | ||||||
| 	usb_set_intfdata(acm->control, NULL); | 	usb_set_intfdata(acm->control, NULL); | ||||||
| 	usb_set_intfdata(acm->data, 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 */ | 	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 ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */ | ||||||
| 	unsigned int ctrlout;				/* output control lines (DTR, RTS) */ | 	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 writesize;				/* max packet size for the output bulk endpoint */ | ||||||
| 	unsigned int readsize,ctrlsize;			/* buffer sizes for freeing */ | 	unsigned int readsize,ctrlsize;			/* buffer sizes for freeing */ | ||||||
| 	unsigned int minor;				/* acm minor number */ | 	unsigned int minor;				/* acm minor number */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Oliver Neukum
						Oliver Neukum