|
@@ -151,3 +151,108 @@ void tty_port_raise_dtr_rts(struct tty_port *port)
|
|
|
port->ops->raise_dtr_rts(port);
|
|
|
}
|
|
|
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
|
|
+
|
|
|
+/**
|
|
|
+ * tty_port_block_til_ready - Waiting logic for tty open
|
|
|
+ * @port: the tty port being opened
|
|
|
+ * @tty: the tty device being bound
|
|
|
+ * @filp: the file pointer of the opener
|
|
|
+ *
|
|
|
+ * Implement the core POSIX/SuS tty behaviour when opening a tty device.
|
|
|
+ * Handles:
|
|
|
+ * - hangup (both before and during)
|
|
|
+ * - non blocking open
|
|
|
+ * - rts/dtr/dcd
|
|
|
+ * - signals
|
|
|
+ * - port flags and counts
|
|
|
+ *
|
|
|
+ * The passed tty_port must implement the carrier_raised method if it can
|
|
|
+ * do carrier detect and the raise_dtr_rts method if it supports software
|
|
|
+ * management of these lines. Note that the dtr/rts raise is done each
|
|
|
+ * iteration as a hangup may have previously dropped them while we wait.
|
|
|
+ */
|
|
|
+
|
|
|
+int tty_port_block_til_ready(struct tty_port *port,
|
|
|
+ struct tty_struct *tty, struct file *filp)
|
|
|
+{
|
|
|
+ int do_clocal = 0, retval;
|
|
|
+ unsigned long flags;
|
|
|
+ DECLARE_WAITQUEUE(wait, current);
|
|
|
+ int cd;
|
|
|
+
|
|
|
+ /* block if port is in the process of being closed */
|
|
|
+ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
|
|
|
+ interruptible_sleep_on(&port->close_wait);
|
|
|
+ if (port->flags & ASYNC_HUP_NOTIFY)
|
|
|
+ return -EAGAIN;
|
|
|
+ else
|
|
|
+ return -ERESTARTSYS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if non-blocking mode is set we can pass directly to open unless
|
|
|
+ the port has just hung up or is in another error state */
|
|
|
+ if ((filp->f_flags & O_NONBLOCK) ||
|
|
|
+ (tty->flags & (1 << TTY_IO_ERROR))) {
|
|
|
+ port->flags |= ASYNC_NORMAL_ACTIVE;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (C_CLOCAL(tty))
|
|
|
+ do_clocal = 1;
|
|
|
+
|
|
|
+ /* Block waiting until we can proceed. We may need to wait for the
|
|
|
+ carrier, but we must also wait for any close that is in progress
|
|
|
+ before the next open may complete */
|
|
|
+
|
|
|
+ retval = 0;
|
|
|
+ add_wait_queue(&port->open_wait, &wait);
|
|
|
+
|
|
|
+ /* The port lock protects the port counts */
|
|
|
+ spin_lock_irqsave(&port->lock, flags);
|
|
|
+ if (!tty_hung_up_p(filp))
|
|
|
+ port->count--;
|
|
|
+ port->blocked_open++;
|
|
|
+ spin_unlock_irqrestore(&port->lock, flags);
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ /* Indicate we are open */
|
|
|
+ tty_port_raise_dtr_rts(port);
|
|
|
+
|
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
|
+ /* Check for a hangup or uninitialised port. Return accordingly */
|
|
|
+ if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
|
|
|
+ if (port->flags & ASYNC_HUP_NOTIFY)
|
|
|
+ retval = -EAGAIN;
|
|
|
+ else
|
|
|
+ retval = -ERESTARTSYS;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* Probe the carrier. For devices with no carrier detect this
|
|
|
+ will always return true */
|
|
|
+ cd = tty_port_carrier_raised(port);
|
|
|
+ if (!(port->flags & ASYNC_CLOSING) &&
|
|
|
+ (do_clocal || cd))
|
|
|
+ break;
|
|
|
+ if (signal_pending(current)) {
|
|
|
+ retval = -ERESTARTSYS;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ schedule();
|
|
|
+ }
|
|
|
+ set_current_state(TASK_RUNNING);
|
|
|
+ remove_wait_queue(&port->open_wait, &wait);
|
|
|
+
|
|
|
+ /* Update counts. A parallel hangup will have set count to zero and
|
|
|
+ we must not mess that up further */
|
|
|
+ spin_lock_irqsave(&port->lock, flags);
|
|
|
+ if (!tty_hung_up_p(filp))
|
|
|
+ port->count++;
|
|
|
+ port->blocked_open--;
|
|
|
+ if (retval == 0)
|
|
|
+ port->flags |= ASYNC_NORMAL_ACTIVE;
|
|
|
+ spin_unlock_irqrestore(&port->lock, flags);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(tty_port_block_til_ready);
|
|
|
+
|