Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a6a62bd authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

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: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4e065b8b
Loading
Loading
Loading
Loading
+74 −12
Original line number Original line Diff line number Diff line
@@ -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,19 +303,30 @@ 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;

		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);


		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;
		break;


	default:
	default:
@@ -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 @@ made_compressed_probe:
	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);
+3 −0
Original line number Original line Diff line number Diff line
@@ -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 */