1*96fd7ce5SGreg Kroah-Hartman /* 2*96fd7ce5SGreg Kroah-Hartman * Tty port functions 3*96fd7ce5SGreg Kroah-Hartman */ 4*96fd7ce5SGreg Kroah-Hartman 5*96fd7ce5SGreg Kroah-Hartman #include <linux/types.h> 6*96fd7ce5SGreg Kroah-Hartman #include <linux/errno.h> 7*96fd7ce5SGreg Kroah-Hartman #include <linux/tty.h> 8*96fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h> 9*96fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h> 10*96fd7ce5SGreg Kroah-Hartman #include <linux/serial.h> 11*96fd7ce5SGreg Kroah-Hartman #include <linux/timer.h> 12*96fd7ce5SGreg Kroah-Hartman #include <linux/string.h> 13*96fd7ce5SGreg Kroah-Hartman #include <linux/slab.h> 14*96fd7ce5SGreg Kroah-Hartman #include <linux/sched.h> 15*96fd7ce5SGreg Kroah-Hartman #include <linux/init.h> 16*96fd7ce5SGreg Kroah-Hartman #include <linux/wait.h> 17*96fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h> 18*96fd7ce5SGreg Kroah-Hartman #include <linux/delay.h> 19*96fd7ce5SGreg Kroah-Hartman #include <linux/module.h> 20*96fd7ce5SGreg Kroah-Hartman 21*96fd7ce5SGreg Kroah-Hartman void tty_port_init(struct tty_port *port) 22*96fd7ce5SGreg Kroah-Hartman { 23*96fd7ce5SGreg Kroah-Hartman memset(port, 0, sizeof(*port)); 24*96fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->open_wait); 25*96fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->close_wait); 26*96fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->delta_msr_wait); 27*96fd7ce5SGreg Kroah-Hartman mutex_init(&port->mutex); 28*96fd7ce5SGreg Kroah-Hartman mutex_init(&port->buf_mutex); 29*96fd7ce5SGreg Kroah-Hartman spin_lock_init(&port->lock); 30*96fd7ce5SGreg Kroah-Hartman port->close_delay = (50 * HZ) / 100; 31*96fd7ce5SGreg Kroah-Hartman port->closing_wait = (3000 * HZ) / 100; 32*96fd7ce5SGreg Kroah-Hartman kref_init(&port->kref); 33*96fd7ce5SGreg Kroah-Hartman } 34*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_init); 35*96fd7ce5SGreg Kroah-Hartman 36*96fd7ce5SGreg Kroah-Hartman int tty_port_alloc_xmit_buf(struct tty_port *port) 37*96fd7ce5SGreg Kroah-Hartman { 38*96fd7ce5SGreg Kroah-Hartman /* We may sleep in get_zeroed_page() */ 39*96fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 40*96fd7ce5SGreg Kroah-Hartman if (port->xmit_buf == NULL) 41*96fd7ce5SGreg Kroah-Hartman port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); 42*96fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 43*96fd7ce5SGreg Kroah-Hartman if (port->xmit_buf == NULL) 44*96fd7ce5SGreg Kroah-Hartman return -ENOMEM; 45*96fd7ce5SGreg Kroah-Hartman return 0; 46*96fd7ce5SGreg Kroah-Hartman } 47*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_alloc_xmit_buf); 48*96fd7ce5SGreg Kroah-Hartman 49*96fd7ce5SGreg Kroah-Hartman void tty_port_free_xmit_buf(struct tty_port *port) 50*96fd7ce5SGreg Kroah-Hartman { 51*96fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 52*96fd7ce5SGreg Kroah-Hartman if (port->xmit_buf != NULL) { 53*96fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 54*96fd7ce5SGreg Kroah-Hartman port->xmit_buf = NULL; 55*96fd7ce5SGreg Kroah-Hartman } 56*96fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 57*96fd7ce5SGreg Kroah-Hartman } 58*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_free_xmit_buf); 59*96fd7ce5SGreg Kroah-Hartman 60*96fd7ce5SGreg Kroah-Hartman static void tty_port_destructor(struct kref *kref) 61*96fd7ce5SGreg Kroah-Hartman { 62*96fd7ce5SGreg Kroah-Hartman struct tty_port *port = container_of(kref, struct tty_port, kref); 63*96fd7ce5SGreg Kroah-Hartman if (port->xmit_buf) 64*96fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 65*96fd7ce5SGreg Kroah-Hartman if (port->ops->destruct) 66*96fd7ce5SGreg Kroah-Hartman port->ops->destruct(port); 67*96fd7ce5SGreg Kroah-Hartman else 68*96fd7ce5SGreg Kroah-Hartman kfree(port); 69*96fd7ce5SGreg Kroah-Hartman } 70*96fd7ce5SGreg Kroah-Hartman 71*96fd7ce5SGreg Kroah-Hartman void tty_port_put(struct tty_port *port) 72*96fd7ce5SGreg Kroah-Hartman { 73*96fd7ce5SGreg Kroah-Hartman if (port) 74*96fd7ce5SGreg Kroah-Hartman kref_put(&port->kref, tty_port_destructor); 75*96fd7ce5SGreg Kroah-Hartman } 76*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_put); 77*96fd7ce5SGreg Kroah-Hartman 78*96fd7ce5SGreg Kroah-Hartman /** 79*96fd7ce5SGreg Kroah-Hartman * tty_port_tty_get - get a tty reference 80*96fd7ce5SGreg Kroah-Hartman * @port: tty port 81*96fd7ce5SGreg Kroah-Hartman * 82*96fd7ce5SGreg Kroah-Hartman * Return a refcount protected tty instance or NULL if the port is not 83*96fd7ce5SGreg Kroah-Hartman * associated with a tty (eg due to close or hangup) 84*96fd7ce5SGreg Kroah-Hartman */ 85*96fd7ce5SGreg Kroah-Hartman 86*96fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_port_tty_get(struct tty_port *port) 87*96fd7ce5SGreg Kroah-Hartman { 88*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 89*96fd7ce5SGreg Kroah-Hartman struct tty_struct *tty; 90*96fd7ce5SGreg Kroah-Hartman 91*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 92*96fd7ce5SGreg Kroah-Hartman tty = tty_kref_get(port->tty); 93*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 94*96fd7ce5SGreg Kroah-Hartman return tty; 95*96fd7ce5SGreg Kroah-Hartman } 96*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_get); 97*96fd7ce5SGreg Kroah-Hartman 98*96fd7ce5SGreg Kroah-Hartman /** 99*96fd7ce5SGreg Kroah-Hartman * tty_port_tty_set - set the tty of a port 100*96fd7ce5SGreg Kroah-Hartman * @port: tty port 101*96fd7ce5SGreg Kroah-Hartman * @tty: the tty 102*96fd7ce5SGreg Kroah-Hartman * 103*96fd7ce5SGreg Kroah-Hartman * Associate the port and tty pair. Manages any internal refcounts. 104*96fd7ce5SGreg Kroah-Hartman * Pass NULL to deassociate a port 105*96fd7ce5SGreg Kroah-Hartman */ 106*96fd7ce5SGreg Kroah-Hartman 107*96fd7ce5SGreg Kroah-Hartman void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) 108*96fd7ce5SGreg Kroah-Hartman { 109*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 110*96fd7ce5SGreg Kroah-Hartman 111*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 112*96fd7ce5SGreg Kroah-Hartman if (port->tty) 113*96fd7ce5SGreg Kroah-Hartman tty_kref_put(port->tty); 114*96fd7ce5SGreg Kroah-Hartman port->tty = tty_kref_get(tty); 115*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 116*96fd7ce5SGreg Kroah-Hartman } 117*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_set); 118*96fd7ce5SGreg Kroah-Hartman 119*96fd7ce5SGreg Kroah-Hartman static void tty_port_shutdown(struct tty_port *port) 120*96fd7ce5SGreg Kroah-Hartman { 121*96fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 122*96fd7ce5SGreg Kroah-Hartman if (port->ops->shutdown && !port->console && 123*96fd7ce5SGreg Kroah-Hartman test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) 124*96fd7ce5SGreg Kroah-Hartman port->ops->shutdown(port); 125*96fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 126*96fd7ce5SGreg Kroah-Hartman } 127*96fd7ce5SGreg Kroah-Hartman 128*96fd7ce5SGreg Kroah-Hartman /** 129*96fd7ce5SGreg Kroah-Hartman * tty_port_hangup - hangup helper 130*96fd7ce5SGreg Kroah-Hartman * @port: tty port 131*96fd7ce5SGreg Kroah-Hartman * 132*96fd7ce5SGreg Kroah-Hartman * Perform port level tty hangup flag and count changes. Drop the tty 133*96fd7ce5SGreg Kroah-Hartman * reference. 134*96fd7ce5SGreg Kroah-Hartman */ 135*96fd7ce5SGreg Kroah-Hartman 136*96fd7ce5SGreg Kroah-Hartman void tty_port_hangup(struct tty_port *port) 137*96fd7ce5SGreg Kroah-Hartman { 138*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 139*96fd7ce5SGreg Kroah-Hartman 140*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 141*96fd7ce5SGreg Kroah-Hartman port->count = 0; 142*96fd7ce5SGreg Kroah-Hartman port->flags &= ~ASYNC_NORMAL_ACTIVE; 143*96fd7ce5SGreg Kroah-Hartman if (port->tty) { 144*96fd7ce5SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &port->tty->flags); 145*96fd7ce5SGreg Kroah-Hartman tty_kref_put(port->tty); 146*96fd7ce5SGreg Kroah-Hartman } 147*96fd7ce5SGreg Kroah-Hartman port->tty = NULL; 148*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 149*96fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 150*96fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->delta_msr_wait); 151*96fd7ce5SGreg Kroah-Hartman tty_port_shutdown(port); 152*96fd7ce5SGreg Kroah-Hartman } 153*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_hangup); 154*96fd7ce5SGreg Kroah-Hartman 155*96fd7ce5SGreg Kroah-Hartman /** 156*96fd7ce5SGreg Kroah-Hartman * tty_port_carrier_raised - carrier raised check 157*96fd7ce5SGreg Kroah-Hartman * @port: tty port 158*96fd7ce5SGreg Kroah-Hartman * 159*96fd7ce5SGreg Kroah-Hartman * Wrapper for the carrier detect logic. For the moment this is used 160*96fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 161*96fd7ce5SGreg Kroah-Hartman * internal to the tty port. 162*96fd7ce5SGreg Kroah-Hartman */ 163*96fd7ce5SGreg Kroah-Hartman 164*96fd7ce5SGreg Kroah-Hartman int tty_port_carrier_raised(struct tty_port *port) 165*96fd7ce5SGreg Kroah-Hartman { 166*96fd7ce5SGreg Kroah-Hartman if (port->ops->carrier_raised == NULL) 167*96fd7ce5SGreg Kroah-Hartman return 1; 168*96fd7ce5SGreg Kroah-Hartman return port->ops->carrier_raised(port); 169*96fd7ce5SGreg Kroah-Hartman } 170*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_carrier_raised); 171*96fd7ce5SGreg Kroah-Hartman 172*96fd7ce5SGreg Kroah-Hartman /** 173*96fd7ce5SGreg Kroah-Hartman * tty_port_raise_dtr_rts - Raise DTR/RTS 174*96fd7ce5SGreg Kroah-Hartman * @port: tty port 175*96fd7ce5SGreg Kroah-Hartman * 176*96fd7ce5SGreg Kroah-Hartman * Wrapper for the DTR/RTS raise logic. For the moment this is used 177*96fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 178*96fd7ce5SGreg Kroah-Hartman * internal to the tty port. 179*96fd7ce5SGreg Kroah-Hartman */ 180*96fd7ce5SGreg Kroah-Hartman 181*96fd7ce5SGreg Kroah-Hartman void tty_port_raise_dtr_rts(struct tty_port *port) 182*96fd7ce5SGreg Kroah-Hartman { 183*96fd7ce5SGreg Kroah-Hartman if (port->ops->dtr_rts) 184*96fd7ce5SGreg Kroah-Hartman port->ops->dtr_rts(port, 1); 185*96fd7ce5SGreg Kroah-Hartman } 186*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_raise_dtr_rts); 187*96fd7ce5SGreg Kroah-Hartman 188*96fd7ce5SGreg Kroah-Hartman /** 189*96fd7ce5SGreg Kroah-Hartman * tty_port_lower_dtr_rts - Lower DTR/RTS 190*96fd7ce5SGreg Kroah-Hartman * @port: tty port 191*96fd7ce5SGreg Kroah-Hartman * 192*96fd7ce5SGreg Kroah-Hartman * Wrapper for the DTR/RTS raise logic. For the moment this is used 193*96fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 194*96fd7ce5SGreg Kroah-Hartman * internal to the tty port. 195*96fd7ce5SGreg Kroah-Hartman */ 196*96fd7ce5SGreg Kroah-Hartman 197*96fd7ce5SGreg Kroah-Hartman void tty_port_lower_dtr_rts(struct tty_port *port) 198*96fd7ce5SGreg Kroah-Hartman { 199*96fd7ce5SGreg Kroah-Hartman if (port->ops->dtr_rts) 200*96fd7ce5SGreg Kroah-Hartman port->ops->dtr_rts(port, 0); 201*96fd7ce5SGreg Kroah-Hartman } 202*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_lower_dtr_rts); 203*96fd7ce5SGreg Kroah-Hartman 204*96fd7ce5SGreg Kroah-Hartman /** 205*96fd7ce5SGreg Kroah-Hartman * tty_port_block_til_ready - Waiting logic for tty open 206*96fd7ce5SGreg Kroah-Hartman * @port: the tty port being opened 207*96fd7ce5SGreg Kroah-Hartman * @tty: the tty device being bound 208*96fd7ce5SGreg Kroah-Hartman * @filp: the file pointer of the opener 209*96fd7ce5SGreg Kroah-Hartman * 210*96fd7ce5SGreg Kroah-Hartman * Implement the core POSIX/SuS tty behaviour when opening a tty device. 211*96fd7ce5SGreg Kroah-Hartman * Handles: 212*96fd7ce5SGreg Kroah-Hartman * - hangup (both before and during) 213*96fd7ce5SGreg Kroah-Hartman * - non blocking open 214*96fd7ce5SGreg Kroah-Hartman * - rts/dtr/dcd 215*96fd7ce5SGreg Kroah-Hartman * - signals 216*96fd7ce5SGreg Kroah-Hartman * - port flags and counts 217*96fd7ce5SGreg Kroah-Hartman * 218*96fd7ce5SGreg Kroah-Hartman * The passed tty_port must implement the carrier_raised method if it can 219*96fd7ce5SGreg Kroah-Hartman * do carrier detect and the dtr_rts method if it supports software 220*96fd7ce5SGreg Kroah-Hartman * management of these lines. Note that the dtr/rts raise is done each 221*96fd7ce5SGreg Kroah-Hartman * iteration as a hangup may have previously dropped them while we wait. 222*96fd7ce5SGreg Kroah-Hartman */ 223*96fd7ce5SGreg Kroah-Hartman 224*96fd7ce5SGreg Kroah-Hartman int tty_port_block_til_ready(struct tty_port *port, 225*96fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 226*96fd7ce5SGreg Kroah-Hartman { 227*96fd7ce5SGreg Kroah-Hartman int do_clocal = 0, retval; 228*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 229*96fd7ce5SGreg Kroah-Hartman DEFINE_WAIT(wait); 230*96fd7ce5SGreg Kroah-Hartman int cd; 231*96fd7ce5SGreg Kroah-Hartman 232*96fd7ce5SGreg Kroah-Hartman /* block if port is in the process of being closed */ 233*96fd7ce5SGreg Kroah-Hartman if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { 234*96fd7ce5SGreg Kroah-Hartman wait_event_interruptible_tty(port->close_wait, 235*96fd7ce5SGreg Kroah-Hartman !(port->flags & ASYNC_CLOSING)); 236*96fd7ce5SGreg Kroah-Hartman if (port->flags & ASYNC_HUP_NOTIFY) 237*96fd7ce5SGreg Kroah-Hartman return -EAGAIN; 238*96fd7ce5SGreg Kroah-Hartman else 239*96fd7ce5SGreg Kroah-Hartman return -ERESTARTSYS; 240*96fd7ce5SGreg Kroah-Hartman } 241*96fd7ce5SGreg Kroah-Hartman 242*96fd7ce5SGreg Kroah-Hartman /* if non-blocking mode is set we can pass directly to open unless 243*96fd7ce5SGreg Kroah-Hartman the port has just hung up or is in another error state */ 244*96fd7ce5SGreg Kroah-Hartman if (tty->flags & (1 << TTY_IO_ERROR)) { 245*96fd7ce5SGreg Kroah-Hartman port->flags |= ASYNC_NORMAL_ACTIVE; 246*96fd7ce5SGreg Kroah-Hartman return 0; 247*96fd7ce5SGreg Kroah-Hartman } 248*96fd7ce5SGreg Kroah-Hartman if (filp->f_flags & O_NONBLOCK) { 249*96fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 250*96fd7ce5SGreg Kroah-Hartman if (tty->termios->c_cflag & CBAUD) 251*96fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 252*96fd7ce5SGreg Kroah-Hartman port->flags |= ASYNC_NORMAL_ACTIVE; 253*96fd7ce5SGreg Kroah-Hartman return 0; 254*96fd7ce5SGreg Kroah-Hartman } 255*96fd7ce5SGreg Kroah-Hartman 256*96fd7ce5SGreg Kroah-Hartman if (C_CLOCAL(tty)) 257*96fd7ce5SGreg Kroah-Hartman do_clocal = 1; 258*96fd7ce5SGreg Kroah-Hartman 259*96fd7ce5SGreg Kroah-Hartman /* Block waiting until we can proceed. We may need to wait for the 260*96fd7ce5SGreg Kroah-Hartman carrier, but we must also wait for any close that is in progress 261*96fd7ce5SGreg Kroah-Hartman before the next open may complete */ 262*96fd7ce5SGreg Kroah-Hartman 263*96fd7ce5SGreg Kroah-Hartman retval = 0; 264*96fd7ce5SGreg Kroah-Hartman 265*96fd7ce5SGreg Kroah-Hartman /* The port lock protects the port counts */ 266*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 267*96fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 268*96fd7ce5SGreg Kroah-Hartman port->count--; 269*96fd7ce5SGreg Kroah-Hartman port->blocked_open++; 270*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 271*96fd7ce5SGreg Kroah-Hartman 272*96fd7ce5SGreg Kroah-Hartman while (1) { 273*96fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 274*96fd7ce5SGreg Kroah-Hartman if (tty->termios->c_cflag & CBAUD) 275*96fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 276*96fd7ce5SGreg Kroah-Hartman 277*96fd7ce5SGreg Kroah-Hartman prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); 278*96fd7ce5SGreg Kroah-Hartman /* Check for a hangup or uninitialised port. 279*96fd7ce5SGreg Kroah-Hartman Return accordingly */ 280*96fd7ce5SGreg Kroah-Hartman if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { 281*96fd7ce5SGreg Kroah-Hartman if (port->flags & ASYNC_HUP_NOTIFY) 282*96fd7ce5SGreg Kroah-Hartman retval = -EAGAIN; 283*96fd7ce5SGreg Kroah-Hartman else 284*96fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 285*96fd7ce5SGreg Kroah-Hartman break; 286*96fd7ce5SGreg Kroah-Hartman } 287*96fd7ce5SGreg Kroah-Hartman /* Probe the carrier. For devices with no carrier detect this 288*96fd7ce5SGreg Kroah-Hartman will always return true */ 289*96fd7ce5SGreg Kroah-Hartman cd = tty_port_carrier_raised(port); 290*96fd7ce5SGreg Kroah-Hartman if (!(port->flags & ASYNC_CLOSING) && 291*96fd7ce5SGreg Kroah-Hartman (do_clocal || cd)) 292*96fd7ce5SGreg Kroah-Hartman break; 293*96fd7ce5SGreg Kroah-Hartman if (signal_pending(current)) { 294*96fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 295*96fd7ce5SGreg Kroah-Hartman break; 296*96fd7ce5SGreg Kroah-Hartman } 297*96fd7ce5SGreg Kroah-Hartman tty_unlock(); 298*96fd7ce5SGreg Kroah-Hartman schedule(); 299*96fd7ce5SGreg Kroah-Hartman tty_lock(); 300*96fd7ce5SGreg Kroah-Hartman } 301*96fd7ce5SGreg Kroah-Hartman finish_wait(&port->open_wait, &wait); 302*96fd7ce5SGreg Kroah-Hartman 303*96fd7ce5SGreg Kroah-Hartman /* Update counts. A parallel hangup will have set count to zero and 304*96fd7ce5SGreg Kroah-Hartman we must not mess that up further */ 305*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 306*96fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 307*96fd7ce5SGreg Kroah-Hartman port->count++; 308*96fd7ce5SGreg Kroah-Hartman port->blocked_open--; 309*96fd7ce5SGreg Kroah-Hartman if (retval == 0) 310*96fd7ce5SGreg Kroah-Hartman port->flags |= ASYNC_NORMAL_ACTIVE; 311*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 312*96fd7ce5SGreg Kroah-Hartman return retval; 313*96fd7ce5SGreg Kroah-Hartman } 314*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_block_til_ready); 315*96fd7ce5SGreg Kroah-Hartman 316*96fd7ce5SGreg Kroah-Hartman int tty_port_close_start(struct tty_port *port, 317*96fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 318*96fd7ce5SGreg Kroah-Hartman { 319*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 320*96fd7ce5SGreg Kroah-Hartman 321*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 322*96fd7ce5SGreg Kroah-Hartman if (tty_hung_up_p(filp)) { 323*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 324*96fd7ce5SGreg Kroah-Hartman return 0; 325*96fd7ce5SGreg Kroah-Hartman } 326*96fd7ce5SGreg Kroah-Hartman 327*96fd7ce5SGreg Kroah-Hartman if (tty->count == 1 && port->count != 1) { 328*96fd7ce5SGreg Kroah-Hartman printk(KERN_WARNING 329*96fd7ce5SGreg Kroah-Hartman "tty_port_close_start: tty->count = 1 port count = %d.\n", 330*96fd7ce5SGreg Kroah-Hartman port->count); 331*96fd7ce5SGreg Kroah-Hartman port->count = 1; 332*96fd7ce5SGreg Kroah-Hartman } 333*96fd7ce5SGreg Kroah-Hartman if (--port->count < 0) { 334*96fd7ce5SGreg Kroah-Hartman printk(KERN_WARNING "tty_port_close_start: count = %d\n", 335*96fd7ce5SGreg Kroah-Hartman port->count); 336*96fd7ce5SGreg Kroah-Hartman port->count = 0; 337*96fd7ce5SGreg Kroah-Hartman } 338*96fd7ce5SGreg Kroah-Hartman 339*96fd7ce5SGreg Kroah-Hartman if (port->count) { 340*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 341*96fd7ce5SGreg Kroah-Hartman if (port->ops->drop) 342*96fd7ce5SGreg Kroah-Hartman port->ops->drop(port); 343*96fd7ce5SGreg Kroah-Hartman return 0; 344*96fd7ce5SGreg Kroah-Hartman } 345*96fd7ce5SGreg Kroah-Hartman set_bit(ASYNCB_CLOSING, &port->flags); 346*96fd7ce5SGreg Kroah-Hartman tty->closing = 1; 347*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 348*96fd7ce5SGreg Kroah-Hartman /* Don't block on a stalled port, just pull the chain */ 349*96fd7ce5SGreg Kroah-Hartman if (tty->flow_stopped) 350*96fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty); 351*96fd7ce5SGreg Kroah-Hartman if (test_bit(ASYNCB_INITIALIZED, &port->flags) && 352*96fd7ce5SGreg Kroah-Hartman port->closing_wait != ASYNC_CLOSING_WAIT_NONE) 353*96fd7ce5SGreg Kroah-Hartman tty_wait_until_sent(tty, port->closing_wait); 354*96fd7ce5SGreg Kroah-Hartman if (port->drain_delay) { 355*96fd7ce5SGreg Kroah-Hartman unsigned int bps = tty_get_baud_rate(tty); 356*96fd7ce5SGreg Kroah-Hartman long timeout; 357*96fd7ce5SGreg Kroah-Hartman 358*96fd7ce5SGreg Kroah-Hartman if (bps > 1200) 359*96fd7ce5SGreg Kroah-Hartman timeout = max_t(long, 360*96fd7ce5SGreg Kroah-Hartman (HZ * 10 * port->drain_delay) / bps, HZ / 10); 361*96fd7ce5SGreg Kroah-Hartman else 362*96fd7ce5SGreg Kroah-Hartman timeout = 2 * HZ; 363*96fd7ce5SGreg Kroah-Hartman schedule_timeout_interruptible(timeout); 364*96fd7ce5SGreg Kroah-Hartman } 365*96fd7ce5SGreg Kroah-Hartman /* Flush the ldisc buffering */ 366*96fd7ce5SGreg Kroah-Hartman tty_ldisc_flush(tty); 367*96fd7ce5SGreg Kroah-Hartman 368*96fd7ce5SGreg Kroah-Hartman /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to 369*96fd7ce5SGreg Kroah-Hartman hang up the line */ 370*96fd7ce5SGreg Kroah-Hartman if (tty->termios->c_cflag & HUPCL) 371*96fd7ce5SGreg Kroah-Hartman tty_port_lower_dtr_rts(port); 372*96fd7ce5SGreg Kroah-Hartman 373*96fd7ce5SGreg Kroah-Hartman /* Don't call port->drop for the last reference. Callers will want 374*96fd7ce5SGreg Kroah-Hartman to drop the last active reference in ->shutdown() or the tty 375*96fd7ce5SGreg Kroah-Hartman shutdown path */ 376*96fd7ce5SGreg Kroah-Hartman return 1; 377*96fd7ce5SGreg Kroah-Hartman } 378*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_start); 379*96fd7ce5SGreg Kroah-Hartman 380*96fd7ce5SGreg Kroah-Hartman void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) 381*96fd7ce5SGreg Kroah-Hartman { 382*96fd7ce5SGreg Kroah-Hartman unsigned long flags; 383*96fd7ce5SGreg Kroah-Hartman 384*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 385*96fd7ce5SGreg Kroah-Hartman tty->closing = 0; 386*96fd7ce5SGreg Kroah-Hartman 387*96fd7ce5SGreg Kroah-Hartman if (port->blocked_open) { 388*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 389*96fd7ce5SGreg Kroah-Hartman if (port->close_delay) { 390*96fd7ce5SGreg Kroah-Hartman msleep_interruptible( 391*96fd7ce5SGreg Kroah-Hartman jiffies_to_msecs(port->close_delay)); 392*96fd7ce5SGreg Kroah-Hartman } 393*96fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 394*96fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 395*96fd7ce5SGreg Kroah-Hartman } 396*96fd7ce5SGreg Kroah-Hartman port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); 397*96fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->close_wait); 398*96fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 399*96fd7ce5SGreg Kroah-Hartman } 400*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_end); 401*96fd7ce5SGreg Kroah-Hartman 402*96fd7ce5SGreg Kroah-Hartman void tty_port_close(struct tty_port *port, struct tty_struct *tty, 403*96fd7ce5SGreg Kroah-Hartman struct file *filp) 404*96fd7ce5SGreg Kroah-Hartman { 405*96fd7ce5SGreg Kroah-Hartman if (tty_port_close_start(port, tty, filp) == 0) 406*96fd7ce5SGreg Kroah-Hartman return; 407*96fd7ce5SGreg Kroah-Hartman tty_port_shutdown(port); 408*96fd7ce5SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 409*96fd7ce5SGreg Kroah-Hartman tty_port_close_end(port, tty); 410*96fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, NULL); 411*96fd7ce5SGreg Kroah-Hartman } 412*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close); 413*96fd7ce5SGreg Kroah-Hartman 414*96fd7ce5SGreg Kroah-Hartman int tty_port_open(struct tty_port *port, struct tty_struct *tty, 415*96fd7ce5SGreg Kroah-Hartman struct file *filp) 416*96fd7ce5SGreg Kroah-Hartman { 417*96fd7ce5SGreg Kroah-Hartman spin_lock_irq(&port->lock); 418*96fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 419*96fd7ce5SGreg Kroah-Hartman ++port->count; 420*96fd7ce5SGreg Kroah-Hartman spin_unlock_irq(&port->lock); 421*96fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, tty); 422*96fd7ce5SGreg Kroah-Hartman 423*96fd7ce5SGreg Kroah-Hartman /* 424*96fd7ce5SGreg Kroah-Hartman * Do the device-specific open only if the hardware isn't 425*96fd7ce5SGreg Kroah-Hartman * already initialized. Serialize open and shutdown using the 426*96fd7ce5SGreg Kroah-Hartman * port mutex. 427*96fd7ce5SGreg Kroah-Hartman */ 428*96fd7ce5SGreg Kroah-Hartman 429*96fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 430*96fd7ce5SGreg Kroah-Hartman 431*96fd7ce5SGreg Kroah-Hartman if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { 432*96fd7ce5SGreg Kroah-Hartman clear_bit(TTY_IO_ERROR, &tty->flags); 433*96fd7ce5SGreg Kroah-Hartman if (port->ops->activate) { 434*96fd7ce5SGreg Kroah-Hartman int retval = port->ops->activate(port, tty); 435*96fd7ce5SGreg Kroah-Hartman if (retval) { 436*96fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 437*96fd7ce5SGreg Kroah-Hartman return retval; 438*96fd7ce5SGreg Kroah-Hartman } 439*96fd7ce5SGreg Kroah-Hartman } 440*96fd7ce5SGreg Kroah-Hartman set_bit(ASYNCB_INITIALIZED, &port->flags); 441*96fd7ce5SGreg Kroah-Hartman } 442*96fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 443*96fd7ce5SGreg Kroah-Hartman return tty_port_block_til_ready(port, tty, filp); 444*96fd7ce5SGreg Kroah-Hartman } 445*96fd7ce5SGreg Kroah-Hartman 446*96fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_open); 447