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