linux-old/drivers/char/tty_ioctl.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/char/tty_ioctl.c
   3 *
   4 *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
   5 *
   6 * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
   7 * which can be dynamically activated and de-activated by the line
   8 * discipline handling modules (like SLIP).
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/termios.h>
  13#include <linux/errno.h>
  14#include <linux/sched.h>
  15#include <linux/kernel.h>
  16#include <linux/major.h>
  17#include <linux/tty.h>
  18#include <linux/fcntl.h>
  19#include <linux/string.h>
  20#include <linux/mm.h>
  21#include <linux/module.h>
  22
  23#include <asm/io.h>
  24#include <asm/bitops.h>
  25#include <asm/uaccess.h>
  26#include <asm/system.h>
  27
  28#undef TTY_DEBUG_WAIT_UNTIL_SENT
  29
  30#undef  DEBUG
  31
  32/*
  33 * Internal flag options for termios setting behavior
  34 */
  35#define TERMIOS_FLUSH   1
  36#define TERMIOS_WAIT    2
  37#define TERMIOS_TERMIO  4
  38
  39void tty_wait_until_sent(struct tty_struct * tty, long timeout)
  40{
  41        DECLARE_WAITQUEUE(wait, current);
  42
  43#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
  44        char buf[64];
  45        
  46        printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf));
  47#endif
  48        if (!tty->driver.chars_in_buffer)
  49                return;
  50        add_wait_queue(&tty->write_wait, &wait);
  51        if (!timeout)
  52                timeout = MAX_SCHEDULE_TIMEOUT;
  53        do {
  54#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
  55                printk(KERN_DEBUG "waiting %s...(%d)\n", tty_name(tty, buf),
  56                       tty->driver.chars_in_buffer(tty));
  57#endif
  58                set_current_state(TASK_INTERRUPTIBLE);
  59                if (signal_pending(current))
  60                        goto stop_waiting;
  61                if (!tty->driver.chars_in_buffer(tty))
  62                        break;
  63                timeout = schedule_timeout(timeout);
  64        } while (timeout);
  65        if (tty->driver.wait_until_sent)
  66                tty->driver.wait_until_sent(tty, timeout);
  67stop_waiting:
  68        current->state = TASK_RUNNING;
  69        remove_wait_queue(&tty->write_wait, &wait);
  70}
  71
  72static void unset_locked_termios(struct termios *termios,
  73                                 struct termios *old,
  74                                 struct termios *locked)
  75{
  76        int     i;
  77        
  78#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
  79
  80        if (!locked) {
  81                printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
  82                return;
  83        }
  84
  85        NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
  86        NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
  87        NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
  88        NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
  89        termios->c_line = locked->c_line ? old->c_line : termios->c_line;
  90        for (i=0; i < NCCS; i++)
  91                termios->c_cc[i] = locked->c_cc[i] ?
  92                        old->c_cc[i] : termios->c_cc[i];
  93}
  94
  95static void change_termios(struct tty_struct * tty, struct termios * new_termios)
  96{
  97        int canon_change;
  98        struct termios old_termios = *tty->termios;
  99        struct tty_ldisc *ld;
 100
 101        /*
 102         *      Perform the actual termios internal changes under lock.
 103         */
 104
 105        /* FIXME: we need to decide on some locking/ordering semantics
 106           for the set_termios notification eventually */
 107        down(&tty->termios_sem);
 108
 109        *tty->termios = *new_termios;
 110        unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
 111        canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
 112        if (canon_change) {
 113                memset(&tty->read_flags, 0, sizeof tty->read_flags);
 114                tty->canon_head = tty->read_tail;
 115                tty->canon_data = 0;
 116                tty->erasing = 0;
 117        }
 118        if (canon_change && !L_ICANON(tty) && tty->read_cnt)
 119                /* Get characters left over from canonical mode. */
 120                wake_up_interruptible(&tty->read_wait);
 121
 122        /* see if packet mode change of state */
 123
 124        if (tty->link && tty->link->packet) {
 125                int old_flow = ((old_termios.c_iflag & IXON) &&
 126                                (old_termios.c_cc[VSTOP] == '\023') &&
 127                                (old_termios.c_cc[VSTART] == '\021'));
 128                int new_flow = (I_IXON(tty) &&
 129                                STOP_CHAR(tty) == '\023' &&
 130                                START_CHAR(tty) == '\021');
 131                if (old_flow != new_flow) {
 132                        tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
 133                        if (new_flow)
 134                                tty->ctrl_status |= TIOCPKT_DOSTOP;
 135                        else
 136                                tty->ctrl_status |= TIOCPKT_NOSTOP;
 137                        wake_up_interruptible(&tty->link->read_wait);
 138                }
 139        }
 140
 141        if (tty->driver.set_termios)
 142                (*tty->driver.set_termios)(tty, &old_termios);
 143
 144        ld = tty_ldisc_ref(tty);
 145        if (ld != NULL) {
 146                if (ld->set_termios)
 147                        (ld->set_termios)(tty, &old_termios);
 148                tty_ldisc_deref(ld);
 149        }
 150        up(&tty->termios_sem);
 151}
 152
 153static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
 154{
 155        struct termios tmp_termios;
 156        struct tty_ldisc *ld;
 157        int retval = tty_check_change(tty);
 158
 159        if (retval)
 160                return retval;
 161
 162        if (opt & TERMIOS_TERMIO) {
 163                memcpy(&tmp_termios, tty->termios, sizeof(struct termios));
 164                if (user_termio_to_kernel_termios(&tmp_termios,
 165                                                  (struct termio *) arg))
 166                        return -EFAULT;
 167        } else {
 168                if (user_termios_to_kernel_termios(&tmp_termios,
 169                                                   (struct termios *) arg))
 170                        return -EFAULT;
 171        }
 172
 173        ld = tty_ldisc_ref(tty);
 174
 175        if (ld != NULL) {
 176                if ((opt & TERMIOS_FLUSH) && ld->flush_buffer)
 177                        ld->flush_buffer(tty);
 178                tty_ldisc_deref(ld);
 179        }
 180
 181        if (opt & TERMIOS_WAIT) {
 182                tty_wait_until_sent(tty, 0);
 183                if (signal_pending(current))
 184                        return -EINTR;
 185        }
 186
 187        change_termios(tty, &tmp_termios);
 188        return 0;
 189}
 190
 191static int get_termio(struct tty_struct * tty, struct termio * termio)
 192{
 193        if (kernel_termios_to_user_termio(termio, tty->termios))
 194                return -EFAULT;
 195        return 0;
 196}
 197
 198static unsigned long inq_canon(struct tty_struct * tty)
 199{
 200        int nr, head, tail;
 201
 202        if (!tty->canon_data || !tty->read_buf)
 203                return 0;
 204        head = tty->canon_head;
 205        tail = tty->read_tail;
 206        nr = (head - tail) & (N_TTY_BUF_SIZE-1);
 207        /* Skip EOF-chars.. */
 208        while (head != tail) {
 209                if (test_bit(tail, &tty->read_flags) &&
 210                    tty->read_buf[tail] == __DISABLED_CHAR)
 211                        nr--;
 212                tail = (tail+1) & (N_TTY_BUF_SIZE-1);
 213        }
 214        return nr;
 215}
 216
 217#ifdef TIOCGETP
 218/*
 219 * These are deprecated, but there is limited support..
 220 *
 221 * The "sg_flags" translation is a joke..
 222 */
 223static int get_sgflags(struct tty_struct * tty)
 224{
 225        int flags = 0;
 226
 227        if (!(tty->termios->c_lflag & ICANON)) {
 228                if (tty->termios->c_lflag & ISIG)
 229                        flags |= 0x02;          /* cbreak */
 230                else
 231                        flags |= 0x20;          /* raw */
 232        }
 233        if (tty->termios->c_lflag & ECHO)
 234                flags |= 0x08;                  /* echo */
 235        if (tty->termios->c_oflag & OPOST)
 236                if (tty->termios->c_oflag & ONLCR)
 237                        flags |= 0x10;          /* crmod */
 238        return flags;
 239}
 240
 241static int get_sgttyb(struct tty_struct * tty, struct sgttyb * sgttyb)
 242{
 243        struct sgttyb tmp;
 244        unsigned long flags;
 245
 246        down(&tty->termios_sem);
 247        tmp.sg_ispeed = 0;
 248        tmp.sg_ospeed = 0;
 249        tmp.sg_erase = tty->termios->c_cc[VERASE];
 250        tmp.sg_kill = tty->termios->c_cc[VKILL];
 251        tmp.sg_flags = get_sgflags(tty);
 252        up(&tty->termios_sem);
 253
 254        return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 255}
 256
 257static void set_sgflags(struct termios * termios, int flags)
 258{
 259        termios->c_iflag = ICRNL | IXON;
 260        termios->c_oflag = 0;
 261        termios->c_lflag = ISIG | ICANON;
 262        if (flags & 0x02) {     /* cbreak */
 263                termios->c_iflag = 0;
 264                termios->c_lflag &= ~ICANON;
 265        }
 266        if (flags & 0x08) {             /* echo */
 267                termios->c_lflag |= ECHO | ECHOE | ECHOK |
 268                                    ECHOCTL | ECHOKE | IEXTEN;
 269        }
 270        if (flags & 0x10) {             /* crmod */
 271                termios->c_oflag |= OPOST | ONLCR;
 272        }
 273        if (flags & 0x20) {     /* raw */
 274                termios->c_iflag = 0;
 275                termios->c_lflag &= ~(ISIG | ICANON);
 276        }
 277        if (!(termios->c_lflag & ICANON)) {
 278                termios->c_cc[VMIN] = 1;
 279                termios->c_cc[VTIME] = 0;
 280        }
 281}
 282
 283static int set_sgttyb(struct tty_struct * tty, struct sgttyb * sgttyb)
 284{
 285        int retval;
 286        struct sgttyb tmp;
 287        struct termios termios;
 288
 289        retval = tty_check_change(tty);
 290        if (retval)
 291                return retval;
 292        if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
 293                return -EFAULT;
 294        down(&tty->termios_sem);
 295        termios =  *tty->termios;
 296        termios.c_cc[VERASE] = tmp.sg_erase;
 297        termios.c_cc[VKILL] = tmp.sg_kill;
 298        set_sgflags(&termios, tmp.sg_flags);
 299        up(&tty->termios_sem);
 300        change_termios(tty, &termios);
 301        return 0;
 302}
 303#endif
 304
 305#ifdef TIOCGETC
 306static int get_tchars(struct tty_struct * tty, struct tchars * tchars)
 307{
 308        struct tchars tmp;
 309
 310        tmp.t_intrc = tty->termios->c_cc[VINTR];
 311        tmp.t_quitc = tty->termios->c_cc[VQUIT];
 312        tmp.t_startc = tty->termios->c_cc[VSTART];
 313        tmp.t_stopc = tty->termios->c_cc[VSTOP];
 314        tmp.t_eofc = tty->termios->c_cc[VEOF];
 315        tmp.t_brkc = tty->termios->c_cc[VEOL2]; /* what is brkc anyway? */
 316        return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 317}
 318
 319static int set_tchars(struct tty_struct * tty, struct tchars * tchars)
 320{
 321        struct tchars tmp;
 322
 323        if (copy_from_user(&tmp, tchars, sizeof(tmp)))
 324                return -EFAULT;
 325        tty->termios->c_cc[VINTR] = tmp.t_intrc;
 326        tty->termios->c_cc[VQUIT] = tmp.t_quitc;
 327        tty->termios->c_cc[VSTART] = tmp.t_startc;
 328        tty->termios->c_cc[VSTOP] = tmp.t_stopc;
 329        tty->termios->c_cc[VEOF] = tmp.t_eofc;
 330        tty->termios->c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */
 331        return 0;
 332}
 333#endif
 334
 335#ifdef TIOCGLTC
 336static int get_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
 337{
 338        struct ltchars tmp;
 339
 340        tmp.t_suspc = tty->termios->c_cc[VSUSP];
 341        tmp.t_dsuspc = tty->termios->c_cc[VSUSP];       /* what is dsuspc anyway? */
 342        tmp.t_rprntc = tty->termios->c_cc[VREPRINT];
 343        tmp.t_flushc = tty->termios->c_cc[VEOL2];       /* what is flushc anyway? */
 344        tmp.t_werasc = tty->termios->c_cc[VWERASE];
 345        tmp.t_lnextc = tty->termios->c_cc[VLNEXT];
 346        return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 347}
 348
 349static int set_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
 350{
 351        struct ltchars tmp;
 352
 353        if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
 354                return -EFAULT;
 355
 356        tty->termios->c_cc[VSUSP] = tmp.t_suspc;
 357        tty->termios->c_cc[VEOL2] = tmp.t_dsuspc;       /* what is dsuspc anyway? */
 358        tty->termios->c_cc[VREPRINT] = tmp.t_rprntc;
 359        tty->termios->c_cc[VEOL2] = tmp.t_flushc;       /* what is flushc anyway? */
 360        tty->termios->c_cc[VWERASE] = tmp.t_werasc;
 361        tty->termios->c_cc[VLNEXT] = tmp.t_lnextc;
 362        return 0;
 363}
 364#endif
 365
 366/*
 367 * Send a high priority character to the tty.
 368 */
 369void send_prio_char(struct tty_struct *tty, char ch)
 370{
 371        int     was_stopped = tty->stopped;
 372
 373        if (tty->driver.send_xchar) {
 374                tty->driver.send_xchar(tty, ch);
 375                return;
 376        }
 377        if (was_stopped)
 378                start_tty(tty);
 379        tty->driver.write(tty, 0, &ch, 1);
 380        if (was_stopped)
 381                stop_tty(tty);
 382}
 383
 384int n_tty_ioctl(struct tty_struct * tty, struct file * file,
 385                       unsigned int cmd, unsigned long arg)
 386{
 387        struct tty_struct * real_tty;
 388        int retval;
 389        struct tty_ldisc *ld;
 390
 391        if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
 392            tty->driver.subtype == PTY_TYPE_MASTER)
 393                real_tty = tty->link;
 394        else
 395                real_tty = tty;
 396
 397        switch (cmd) {
 398#ifdef TIOCGETP
 399                case TIOCGETP:
 400                        return get_sgttyb(real_tty, (struct sgttyb *) arg);
 401                case TIOCSETP:
 402                case TIOCSETN:
 403                        return set_sgttyb(real_tty, (struct sgttyb *) arg);
 404#endif
 405#ifdef TIOCGETC
 406                case TIOCGETC:
 407                        return get_tchars(real_tty, (struct tchars *) arg);
 408                case TIOCSETC:
 409                        return set_tchars(real_tty, (struct tchars *) arg);
 410#endif
 411#ifdef TIOCGLTC
 412                case TIOCGLTC:
 413                        return get_ltchars(real_tty, (struct ltchars *) arg);
 414                case TIOCSLTC:
 415                        return set_ltchars(real_tty, (struct ltchars *) arg);
 416#endif
 417                case TCGETS:
 418                        if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios))
 419                                return -EFAULT;
 420                        return 0;
 421                case TCSETSF:
 422                        return set_termios(real_tty, arg,  TERMIOS_FLUSH | TERMIOS_WAIT);
 423                case TCSETSW:
 424                        return set_termios(real_tty, arg, TERMIOS_WAIT);
 425                case TCSETS:
 426                        return set_termios(real_tty, arg, 0);
 427                case TCGETA:
 428                        return get_termio(real_tty,(struct termio *) arg);
 429                case TCSETAF:
 430                        return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO);
 431                case TCSETAW:
 432                        return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO);
 433                case TCSETA:
 434                        return set_termios(real_tty, arg, TERMIOS_TERMIO);
 435                case TCXONC:
 436                        retval = tty_check_change(tty);
 437                        if (retval)
 438                                return retval;
 439                        switch (arg) {
 440                        case TCOOFF:
 441                                if (!tty->flow_stopped) {
 442                                        tty->flow_stopped = 1;
 443                                        stop_tty(tty);
 444                                }
 445                                break;
 446                        case TCOON:
 447                                if (tty->flow_stopped) {
 448                                        tty->flow_stopped = 0;
 449                                        start_tty(tty);
 450                                }
 451                                break;
 452                        case TCIOFF:
 453                                if (STOP_CHAR(tty) != __DISABLED_CHAR)
 454                                        send_prio_char(tty, STOP_CHAR(tty));
 455                                break;
 456                        case TCION:
 457                                if (START_CHAR(tty) != __DISABLED_CHAR)
 458                                        send_prio_char(tty, START_CHAR(tty));
 459                                break;
 460                        default:
 461                                return -EINVAL;
 462                        }
 463                        return 0;
 464                case TCFLSH:
 465                        retval = tty_check_change(tty);
 466                        if (retval)
 467                                return retval;
 468
 469                        ld = tty_ldisc_ref(tty);
 470                        switch (arg) {
 471                        case TCIFLUSH:
 472                                if (ld->flush_buffer)
 473                                        ld->flush_buffer(tty);
 474                                break;
 475                        case TCIOFLUSH:
 476                                if (ld->flush_buffer)
 477                                        ld->flush_buffer(tty);
 478                                /* fall through */
 479                        case TCOFLUSH:
 480                                if (tty->driver.flush_buffer)
 481                                        tty->driver.flush_buffer(tty);
 482                                break;
 483                        default:
 484                                tty_ldisc_deref(ld);
 485                                return -EINVAL;
 486                        }
 487                        tty_ldisc_deref(ld);
 488                        return 0;
 489                case TIOCOUTQ:
 490                        return put_user(tty->driver.chars_in_buffer ?
 491                                        tty->driver.chars_in_buffer(tty) : 0,
 492                                        (int *) arg);
 493                case TIOCINQ:
 494                        retval = tty->read_cnt;
 495                        if (L_ICANON(tty))
 496                                retval = inq_canon(tty);
 497                        return put_user(retval, (unsigned int *) arg);
 498                case TIOCGLCKTRMIOS:
 499                        if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios_locked))
 500                                return -EFAULT;
 501                        return 0;
 502
 503                case TIOCSLCKTRMIOS:
 504                        if (!capable(CAP_SYS_ADMIN))
 505                                return -EPERM;
 506                        if (user_termios_to_kernel_termios(real_tty->termios_locked, (struct termios *) arg))
 507                                return -EFAULT;
 508                        return 0;
 509
 510                case TIOCPKT:
 511                {
 512                        int pktmode;
 513
 514                        if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
 515                            tty->driver.subtype != PTY_TYPE_MASTER)
 516                                return -ENOTTY;
 517                        if (get_user(pktmode, (int *) arg))
 518                                return -EFAULT;
 519                        if (pktmode) {
 520                                if (!tty->packet) {
 521                                        tty->packet = 1;
 522                                        tty->link->ctrl_status = 0;
 523                                }
 524                        } else
 525                                tty->packet = 0;
 526                        return 0;
 527                }
 528                case TIOCGSOFTCAR:
 529                        return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
 530                case TIOCSSOFTCAR:
 531                        if (get_user(arg, (unsigned int *) arg))
 532                                return -EFAULT;
 533                        down(&tty->termios_sem);
 534                        tty->termios->c_cflag =
 535                                ((tty->termios->c_cflag & ~CLOCAL) |
 536                                 (arg ? CLOCAL : 0));
 537                        up(&tty->termios_sem);
 538                        return 0;
 539                default:
 540                        return -ENOIOCTLCMD;
 541                }
 542}
 543
 544EXPORT_SYMBOL(n_tty_ioctl);
 545
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.