--- /dev/null Tue Nov 30 14:30:13 1993 +++ drivers/char/ChangeLog Mon Oct 10 13:01:51 1994 @@ -0,0 +1,84 @@ +Sun Oct 9 23:46:03 1994 Theodore Y. Ts'o (tytso@rt-11) + + * tty_io.c (do_tty_hangup): If the tty driver flags + TTY_DRIVER_RESET_TERMIOS is set, then reset the termios + settings back to the driver's initial configuration. This + allows the termios settings to be reset even if a process + has hung up file descriptors keeping a pty's termios from + being freed and reset. + + * tty_io.c (release_dev): Fix memory leak. The pty's other + termios structure should also be freed. + + * serial.c (rs_close, shutdown): Change how we wait for the + transmitter to completely drain before shutting down the + serial port. We now do it by scheduling in another + process instead of busy looping with the interrupts turned + on. This may eliminate some race condition problems that + some people seem to be reporting. + +Sun Sep 25 14:18:14 1994 Theodore Y. Ts'o (tytso@rt-11) + + * tty_io.c (release_dev): When freeing a tty make sure that both + the tty and the o_tty (if present) aren't a process's + controlling tty. (Previously, we only checked the tty.) + + * serial.c (change_speed): Only enable the Modem Status + Interrupt for a port if CLOCAL is not set or CRTSCTS + is set. If we're not checking the carrier detect and + CTS line, there's no point in enabling the modem + status interrupt. This will save spurious interrupts + from slowing down systems who have terminals that + don't support either line. (Of course, if you want + only one of CD and CTS support, you will need a + properly wired serial cable.) + +Thu Sep 22 08:32:48 1994 Theodore Y. Ts'o (tytso@rt-11) + + * tty_io.c (do_SAK): Return if tty is null. + + * tty_io.c (_tty_name): Return "NULL tty" if the passed in tty is + NULL. + +Sat Sep 17 13:19:25 1994 Theodore Y. Ts'o (tytso@rt-11) + + * tty_ioctl.c (n_tty_ioctl): Fix TIOCGLCKTRMIOS and + TIOCSLCKTRMIOS, which were totally broken. Remove + extra indirection from argument; it should be a struct + termios *, not a struct termios **. + &real_tty->termios_locked should have been + real_tty->termios_locked. This caused us to be + reading and writing the termios_locked structure to + random places in kernel memory. + + * tty_io.c (release_dev): Oops! Forgot to delete a critical kfree + of the locked_termios. This leaves the locked_termios + structure pointed at a freed object. + +Fri Sep 16 08:13:25 1994 Theodore Y. Ts'o (tytso@rt-11) + + * tty_io.c (tty_open): Don't check for an exclusive open until + after the device specific open routine has been called. + Otherwise, the serial device ref counting will be screwed + up. + + * serial.c (rs_open, block_til_ready): Don't set termios structure + until after block_til_ready has returned successfully. + Modify block_til_ready to check the normal_termios + structure directly, so it doesn't rely on termios being + set before its called. + +Thu Sep 15 23:34:01 1994 Theodore Y. Ts'o (tytso@rt-11) + + * serial.c (rs_close): Turn off interrupts during rs_close() to + prevent a race condition with the hangup code (which + runs during a software interrupt). + + * tty_io.c (release_dev): Don't free the locked_termios structure; + its state must be retained across device opens. + + + * tty_io.c (tty_unregister_driver): Added function to unregister a + tty driver. (For loadable device drivers.) + + =================================================================== RCS file: drivers/char/RCS/tty_io.c,v retrieving revision 1.1 diff -u -r1.1 drivers/char/tty_io.c --- 1.1 1994/10/10 03:16:12 +++ drivers/char/tty_io.c 1994/10/10 15:00:57 @@ -109,9 +109,12 @@ */ char *_tty_name(struct tty_struct *tty, char *buf) { - sprintf(buf, "%s%d", tty->driver.name, - MINOR(tty->device) - tty->driver.minor_start + - tty->driver.name_base); + if (tty) + sprintf(buf, "%s%d", tty->driver.name, + MINOR(tty->device) - tty->driver.minor_start + + tty->driver.name_base); + else + strcpy(buf, "NULL tty"); return buf; } @@ -363,6 +366,8 @@ if (p->tty == tty) p->tty = NULL; } + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) + *tty->termios = tty->driver.init_termios; if (tty->driver.hangup) (tty->driver.hangup)(tty); } @@ -971,21 +976,12 @@ if (tty->count) return; - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if ((*p) && (*p)->tty == tty) - (*p)->tty = NULL; - } - if (o_tty) { if (o_tty->count) return; tty->driver.other->table[idx] = NULL; tty->driver.other->termios[idx] = NULL; - tty->driver.other->termios_locked[idx] = NULL; + kfree_s(o_tp, sizeof(struct termios)); } #ifdef TTY_DEBUG_HANGUP @@ -993,6 +989,19 @@ #endif /* + * Make sure there aren't any processes that still think this + * tty is their controlling tty. + */ + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (*p == 0) + continue; + if ((*p)->tty == tty) + (*p)->tty = NULL; + if (o_tty && (*p)->tty == o_tty) + (*p)->tty = NULL; + } + + /* * Shutdown the current line discipline, and reset it to * N_TTY. */ @@ -1004,9 +1013,7 @@ tty->driver.table[idx] = NULL; if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { tty->driver.termios[idx] = NULL; - tty->driver.termios_locked[idx] = NULL; kfree_s(tp, sizeof(struct termios)); - kfree_s(ltp, sizeof(struct termios)); } if (tty == redirect || o_tty == redirect) redirect = NULL; @@ -1037,10 +1044,6 @@ (*o_tty->driver.refcount)--; free_page((unsigned long) o_tty); } - if (o_tp) - kfree_s(o_tp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); } /* @@ -1087,13 +1090,14 @@ #ifdef TTY_DEBUG_HANGUP printk("opening %s...", tty_name(tty)); #endif - if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser()) - retval = -EBUSY; - else if (tty->driver.open) + if (tty->driver.open) retval = tty->driver.open(tty, filp); else retval = -ENODEV; + if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser()) + retval = -EBUSY; + if (retval) { #ifdef TTY_DEBUG_HANGUP printk("error %d in opening %s...", retval, tty_name(tty)); @@ -1451,6 +1455,8 @@ int i; struct file *filp; + if (!tty) + return; if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) @@ -1562,6 +1568,44 @@ tty_drivers = driver; return error; } + +/* + * Called by a tty driver to unregister itself. + */ +int tty_unregister_driver(struct tty_driver *driver) +{ + int retval; + struct tty_driver *p; + int found = 0; + int major_inuse = 0; + + if (driver->refcount) + return -EBUSY; + + for (p = tty_drivers; p; p = p->next) { + if (p == driver) + found++; + else if (p->major == driver->major) + major_inuse++; + } + + if (!major_inuse) { + retval = unregister_chrdev(driver->major, driver->name); + if (retval) + return retval; + } + + if (driver->prev) + driver->prev->next = driver->next; + else + tty_drivers = driver->next; + + if (driver->next) + driver->next = driver->next->prev; + + return 0; +} + /* * Initialize the console device. This is called *early*, so =================================================================== RCS file: drivers/char/RCS/tty_ioctl.c,v retrieving revision 1.1 diff -u -r1.1 drivers/char/tty_ioctl.c --- 1.1 1994/10/10 03:16:12 +++ drivers/char/tty_ioctl.c 1994/10/10 03:22:59 @@ -314,32 +314,22 @@ (unsigned long *) arg); return 0; case TIOCGLCKTRMIOS: - retval = verify_area(VERIFY_READ, (void *) arg, - sizeof (unsigned long)); - if (retval) - return retval; - arg = get_fs_long((unsigned long *) arg); retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct termios)); if (retval) return retval; memcpy_tofs((struct termios *) arg, - &real_tty->termios_locked, + real_tty->termios_locked, sizeof (struct termios)); return 0; case TIOCSLCKTRMIOS: if (!suser()) return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, - sizeof (unsigned long)); - if (retval) - return retval; - arg = get_fs_long((unsigned long *) arg); - retval = verify_area(VERIFY_READ, (void *) arg, sizeof (struct termios)); if (retval) return retval; - memcpy_fromfs(&real_tty->termios_locked, + memcpy_fromfs(real_tty->termios_locked, (struct termios *) arg, sizeof (struct termios)); return 0; =================================================================== RCS file: drivers/char/RCS/serial.c,v retrieving revision 1.1 diff -u -r1.1 drivers/char/serial.c --- 1.1 1994/10/10 03:16:12 +++ drivers/char/serial.c 1994/10/10 15:01:22 @@ -1039,24 +1039,6 @@ (void) inb((info->port & 0xFE0) | 0x01F); } - /* - * Before we drop DTR, make sure the UART transmitter has - * completely drained; this is especially important if there - * is a transmit FIFO! - * - * We busy loop here, which is not great; unfortunately the - * UART does not provide an interrupt for TEMT, and putting it - * in the interrupt handler would slow down normal accesses - * anyway. - */ - sti(); - timeout = jiffies + info->timeout; - while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) { - if (jiffies > timeout) - break; - } - cli(); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS); @@ -1142,15 +1124,20 @@ } else fcr = 0; - /* CTS flow control flag */ - if (cflag & CRTSCTS) + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; - else + info->IER |= UART_IER_MSI; + } else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; - else + else { info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); /* * Set up parity check flag @@ -1749,12 +1736,17 @@ static void rs_close(struct tty_struct *tty, struct file * filp) { struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; - if (tty_hung_up_p(filp)) + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); return; + } #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, info->count); @@ -1776,8 +1768,10 @@ info->line, info->count); info->count = 0; } - if (info->count) + if (info->count) { + restore_flags(flags); return; + } info->flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have @@ -1787,8 +1781,19 @@ info->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) + if (info->flags & ASYNC_INITIALIZED) { wait_until_sent(tty, 3000); /* 30 seconds timeout */ + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->timeout; + schedule(); + } + } shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); @@ -1815,6 +1820,7 @@ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); + restore_flags(flags); } /* @@ -1845,7 +1851,7 @@ { struct wait_queue wait = { current, NULL }; int retval; - int do_clocal = C_CLOCAL(tty); + int do_clocal = 0; /* * If the device is in the middle of being closed, then block @@ -1893,6 +1899,9 @@ return 0; } + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in @@ -1990,12 +1999,6 @@ return -ENOMEM; } - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - } /* * Start up serial port */ @@ -2010,6 +2013,14 @@ retval); #endif return retval; + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); } info->session = current->session;