*** /dev/null Thu Mar 19 11:10:38 1992 --- include/asm/bitops.h Mon Oct 5 01:24:18 1992 *************** *** 0 **** --- 1,96 ---- + #ifndef _ASM_BITOPS_H + /* + * Copyright 1992, Linus Torvalds. + */ + + #ifdef i386 + /* + * These have to be done with inline assembly: that way the bit-setting + * is guaranteed to be atomic. Both set_bit and clear_bit return 0 + * if the bit-setting went ok, != 0 if the bit already was set/cleared. + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ + extern inline int set_bit(int nr,int * addr) + { + char ok; + + __asm__ __volatile__("btsl %1,%2\n\tsetb %0": + "=q" (ok):"r" (nr),"m" (*(addr))); + return ok; + } + + extern inline int clear_bit(int nr, int * addr) + { + char ok; + + __asm__ __volatile__("btrl %1,%2\n\tsetnb %0": + "=q" (ok):"r" (nr),"m" (*(addr))); + return ok; + } + + /* + * This routine doesn't need to be atomic, but it's faster to code it + * this way. + */ + extern inline int test_bit(int nr, int * addr) + { + char ok; + + __asm__ __volatile__("btl %1,%2\n\tsetb %0": + "=q" (ok):"r" (nr),"m" (*(addr))); + return ok; + } + + #else + /* + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. You should + * recode these in the native assmebly language, if at all possible. + * To guarantee atomicity, these routines call cli() and sti() to + * disable interrupts while they operate. (You have to provide inline + * routines to cli() and sti().) + * + * Also note, these routines assume that you have 32 bit integers. + * You will have to change this if you are trying to port Linux to the + * Alpha architecture or to a Cray. :-) + * + * C language equivalents written by Theodore Ts'o, 9/26/92 + */ + + extern inline int set_bit(int nr,int * addr) + { + int mask, retval; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + cli(); + retval = (mask & *addr) != 0; + *addr |= mask; + sti(); + return retval; + } + + extern inline int clear_bit(int nr, int * addr) + { + int mask, retval; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + cli(); + retval = (mask & *addr) == 0; + *addr &= ~mask; + sti(); + return retval; + } + + extern inline int test_bit(int nr, int * addr) + { + int mask; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + return ((mask & *addr) != 0); + } + #endif /* i386 */ + #endif /* _ASM_BITOPS_H */ =================================================================== RCS file: include/linux/RCS/termios.h,v retrieving revision 1.1 diff -c -r1.1 include/linux/termios.h *** 1.1 1992/10/05 01:25:34 --- include/linux/termios.h 1992/10/05 01:25:40 *************** *** 174,180 **** #define HUPCL 0002000 #define CLOCAL 0004000 #define CIBAUD 03600000 /* input baud rate (not used) */ ! #define CRTSCTS 020000000000 /* flow control */ /* c_lflag bits */ #define ISIG 0000001 --- 174,181 ---- #define HUPCL 0002000 #define CLOCAL 0004000 #define CIBAUD 03600000 /* input baud rate (not used) */ ! #define CNORTSCTS 010000000000 /* no flow control */ ! #define CRTSCTS 020000000000 /* flow control */ /* c_lflag bits */ #define ISIG 0000001 =================================================================== RCS file: include/linux/RCS/tty.h,v retrieving revision 1.1 diff -c -r1.1 include/linux/tty.h *** 1.1 1992/10/05 01:23:18 --- include/linux/tty.h 1992/10/05 01:23:30 *************** *** 93,98 **** --- 93,99 ---- */ #define ASYNC_NOSCRATCH 0x0001 /* 16XXX UART with no scratch register */ #define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ + #define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */ #define ASYNC_SPD_MASK 0x0030 #define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ *************** *** 148,153 **** --- 149,155 ---- #define I_CRNL(tty) _I_FLAG((tty),ICRNL) #define I_NOCR(tty) _I_FLAG((tty),IGNCR) #define I_IXON(tty) _I_FLAG((tty),IXON) + #define I_IXANY(tty) _I_FLAG((tty),IXANY) #define I_STRP(tty) _I_FLAG((tty),ISTRIP) #define O_POST(tty) _O_FLAG((tty),OPOST) *************** *** 221,239 **** * Again, the low-level driver is free to ignore any of these, and has * to implement RQ_THREHOLD_LW for itself if it wants it. */ ! #define SQ_THRESHOLD_LW 0 #define SQ_THRESHOLD_HW 768 ! #define RQ_THRESHOLD_LW 64 #define RQ_THRESHOLD_HW 768 /* - * so that interrupts won't be able to mess up the - * queues, copy_to_cooked must be atomic with repect - * to itself, as must tty->write. These are the flag - * bit-numbers. Use the set_bit() and clear_bit() - * macros to make it all atomic. - * * These bits are used in the flags field of the tty structure. */ #define TTY_WRITE_BUSY 0 #define TTY_READ_BUSY 1 --- 223,240 ---- * Again, the low-level driver is free to ignore any of these, and has * to implement RQ_THREHOLD_LW for itself if it wants it. */ ! #define SQ_THRESHOLD_LW 16 #define SQ_THRESHOLD_HW 768 ! #define RQ_THRESHOLD_LW 16 #define RQ_THRESHOLD_HW 768 /* * These bits are used in the flags field of the tty structure. + * + * So that interrupts won't be able to mess up the queues, + * copy_to_cooked must be atomic with repect to itself, as must + * tty->write. Thus, you must use the inline functions set_bit() and + * clear_bit() to make things atomic. */ #define TTY_WRITE_BUSY 0 #define TTY_READ_BUSY 1 *************** *** 241,269 **** #define TTY_SQ_THROTTLED 3 #define TTY_RQ_THROTTLED 4 - /* - * These have to be done with inline assembly: that way the bit-setting - * is guaranteed to be atomic. Both set_bit and clear_bit return 0 - * if the bit-setting went ok, != 0 if the bit already was set/cleared. - */ - extern inline int set_bit(int nr,int * addr) - { - char ok; - - __asm__ __volatile__("btsl %1,%2\n\tsetb %0": - "=q" (ok):"r" (nr),"m" (*(addr))); - return ok; - } - - extern inline int clear_bit(int nr, int * addr) - { - char ok; - - __asm__ __volatile__("btrl %1,%2\n\tsetnb %0": - "=q" (ok):"r" (nr),"m" (*(addr))); - return ok; - } - #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) #define TTY_READ_FLUSH(tty) tty_read_flush((tty)) --- 242,247 ---- *************** *** 303,308 **** --- 281,287 ---- extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); extern int kill_pg(int pgrp, int sig, int priv); + extern void do_SAK(struct tty_struct *tty); /* tty write functions */ =================================================================== RCS file: include/linux/RCS/serial.h,v retrieving revision 1.1 diff -c -r1.1 include/linux/serial.h *** 1.1 1992/10/05 01:23:56 --- include/linux/serial.h 1992/10/05 01:24:03 *************** *** 45,54 **** --- 45,66 ---- int timeout; int xmit_fifo_size; int custom_divisor; + int x_char; /* xon/xoff characater */ + int event; int line; }; /* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ + #define RS_EVENT_READ_PROCESS 0 + #define RS_EVENT_WRITE_WAKEUP 1 + #define RS_EVENT_HUP_PGRP 2 + #define RS_EVENT_BREAK_INT 3 + #define RS_EVENT_DO_SAK 4 + + /* * These are the UART port assignments, expressed as offsets from the base * register. These assignments should hold for any serial port based on * a 8250, 16450, or 16550(A). *************** *** 113,119 **** /* * These are the definitions for the Interrupt Indentification Register */ ! #define UART_IIR_PEND 0x01 /* Interrupt pending */ #define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ #define UART_IIR_MSI 0x00 /* Modem status interrupt */ --- 125,131 ---- /* * These are the definitions for the Interrupt Indentification Register */ ! #define UART_IIR_NO_INT 0x01 /* No interrupts pending */ #define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ #define UART_IIR_MSI 0x00 /* Modem status interrupt */ *************** *** 149,151 **** --- 161,164 ---- #define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ #define UART_MSR_DDSR 0x02 /* Delta DSR */ #define UART_MSR_DCTS 0x01 /* Delta CTS */ + #define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ =================================================================== RCS file: kernel/chr_drv/RCS/serial.c,v retrieving revision 1.1 diff -c -r1.1 kernel/chr_drv/serial.c *** 1.1 1992/10/05 01:28:27 --- kernel/chr_drv/serial.c 1992/10/06 11:29:27 *************** *** 27,45 **** #include #include #include #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4) /* ! * rs_read_process - Bitfield of serial lines that have I/O processing ! * to be done at the next clock tick. ! * rs_write_timeout - Bitfield of serial lines that have a possible ! * write timeout pending. (In case the THRE ! * interrupt gets lost.) */ ! static unsigned long rs_read_process = 0; ! static unsigned long rs_write_timeout = 0; static void UART_ISR_proc(async_ISR ISR, int line); static void FourPort_ISR_proc(async_ISR ISR, int line); --- 27,52 ---- #include #include #include + #include #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4) + #define AUTO_IRQ /* ! * rs_event - Bitfield of serial lines that events pending ! * to be processed at the next clock tick. ! * rs_write_active - Bitfield of serial lines that are actively ! * transmitting (and therefore have a ! * write timeout pending, in case the ! * THRE interrupt gets lost.) ! * IRQ_ISR[] - Array to store the head of the ISR linked list ! * for each IRQ. */ ! static unsigned long rs_event = 0; ! static unsigned long rs_write_active = 0; + static async_ISR IRQ_ISR[16]; + static void UART_ISR_proc(async_ISR ISR, int line); static void FourPort_ISR_proc(async_ISR ISR, int line); *************** *** 85,93 **** #define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct)) - static async_ISR IRQ_ISR[16]; /* Used to store the head of the */ - /* ISR linked list chain for each IRQ */ - /* * This is used to figure out the divsor speeds and the timeouts */ --- 92,97 ---- *************** *** 95,100 **** --- 99,108 ---- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 56000, 115200, 0 }; + static void startup(struct async_struct * info); + static void shutdown(struct async_struct * info); + static void rs_throttle(struct tty_struct * tty, int status); + static void send_break( struct async_struct * info) { unsigned short port; *************** *** 106,250 **** current->timeout = jiffies + 25; outb_p(inb_p(port) | UART_LCR_SBC, port); schedule(); ! outb_p(inb_p(port) & ~UART_LCR_SBC, port); ! } ! ! /* ! * There are several races here: we avoid most of them by disabling ! * timer_active for the crucial part of the process.. That's a good ! * idea anyway. ! * ! * The problem is that we have to output characters /both/ from interrupts ! * and from the normal write: the latter to be sure the interrupts start up ! * again. With serial lines, the interrupts can happen so often that the ! * races actually are noticeable. ! */ ! static void send_intr(struct async_struct * info) ! { ! unsigned short port = info->port; ! int line = info->line; ! struct tty_queue * queue = &info->tty->write_q; ! int c, count = 0; ! ! if (info->tty->stopped) ! return; ! ! if (info->type == PORT_16550A) ! count = 16; ! else ! count = 1; ! ! rs_write_timeout &= ~(1 << line); ! ! if (inb_p(UART_LSR + info->port) & UART_LSR_THRE) { ! while (count-- && !info->tty->stopped) { ! if (queue->tail == queue->head) ! goto end_send; ! c = queue->buf[queue->tail]; ! queue->tail++; ! queue->tail &= TTY_BUF_SIZE-1; ! outb(c, UART_TX + port); ! } ! } ! info->timer = jiffies + info->timeout; ! if (info->timer < timer_table[RS_TIMER].expires) ! timer_table[RS_TIMER].expires = info->timer; ! rs_write_timeout |= 1 << line; ! timer_active |= 1 << RS_TIMER; ! end_send: ! if (LEFT(queue) > WAKEUP_CHARS) ! wake_up(&queue->proc_list); } ! static void receive_intr(struct async_struct * info) { ! unsigned short port = info->port; ! struct tty_queue * queue = &info->tty->read_q; ! int head = queue->head; ! int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1); ! int count = 0; ! ! rs_read_process &= ~(1 << info->line); ! do { ! count++; ! queue->buf[head] = inb(UART_TX + port); ! if (head != maxhead) { ! head++; ! head &= TTY_BUF_SIZE-1; ! } ! } while (inb(UART_LSR + port) & UART_LSR_DR); ! queue->head = head; ! rs_read_process |= 1 << info->line; timer_table[RS_TIMER].expires = 0; ! timer_active |= 1<port); ! ! /* printk("line status: %02x\n",status); */ ! } ! ! static void modem_status_intr(struct async_struct * info) ! { ! unsigned char status = inb(UART_MSR + info->port); ! ! if (!(info->tty->termios->c_cflag & CLOCAL)) { ! if (((status & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD) ! && info->tty->pgrp > 0) ! kill_pg(info->tty->pgrp,SIGHUP,1); ! ! if (info->tty->termios->c_cflag & CRTSCTS) ! info->tty->stopped = !(status & UART_MSR_CTS); ! ! if (!info->tty->stopped) ! send_intr(info); ! } } - static void (*jmp_table[4])(struct async_struct *) = { - modem_status_intr, - send_intr, - receive_intr, - line_status_intr - }; - /* * This ISR handles the COM1-4 8250, 16450, and 16550A UART's. It is * also called by the FourPort ISR, since the FourPort also uses the * same National Semiconduct UART's, with some interrupt multiplexing * thrown in. */ static void UART_ISR_proc(async_ISR ISR, int line) { ! unsigned char ident; struct async_struct * info = rs_table + line; if (!info || !info->tty || !info->port) - return; - while (1) { - ident = inb(UART_IIR + info->port) & 7; - if (ident & 1) - return; - ident = ident >> 1; - jmp_table[ident](info); - } - } - - /* - * Again, we disable interrupts to be sure there aren't any races: - * see send_intr for details. - */ - static inline void do_rs_write(struct async_struct * info) - { - if (!info->tty || !info->port) return; ! if (EMPTY(&info->tty->write_q)) ! return; ! cli(); ! send_intr(info); ! sti(); } /* --- 114,267 ---- current->timeout = jiffies + 25; outb_p(inb_p(port) | UART_LCR_SBC, port); schedule(); ! outb(inb_p(port) & ~UART_LCR_SBC, port); } ! static inline void rs_sched_event(int line, ! struct async_struct *info, ! int event) { ! info->event |= 1 << event; ! rs_event |= 1 << line; timer_table[RS_TIMER].expires = 0; ! timer_active |= 1 << RS_TIMER; } /* * This ISR handles the COM1-4 8250, 16450, and 16550A UART's. It is * also called by the FourPort ISR, since the FourPort also uses the * same National Semiconduct UART's, with some interrupt multiplexing * thrown in. + * + * This routine assumes nobody else will be mucking with the tty + * queues its working on. It should be called with the interrupts + * disabled, since it is not reentrant, and it assumes it doesn't need + * to worry about other routines mucking about its data structures + * while it keeps copies of critical pointers in registers. */ static void UART_ISR_proc(async_ISR ISR, int line) { ! unsigned char status; struct async_struct * info = rs_table + line; + struct tty_queue * queue; + int head, tail, count, ch; + int cflag, iflag; + + /* + * Just like the LEFT(x) macro, except it uses the loal tail + * and head variables. + */ + #define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1)) if (!info || !info->tty || !info->port) return; ! cflag = info->tty->termios->c_cflag; ! iflag = info->tty->termios->c_iflag; ! ! do { ! restart: ! status = inb(UART_LSR + info->port); ! if (status & UART_LSR_DR) { ! queue = &info->tty->read_q; ! head = queue->head; ! tail = queue->tail; ! do { ! ch = inb(UART_RX + info->port); ! /* ! * There must be at least 3 characters ! * free in the queue; otherwise we punt. ! */ ! if (VLEFT < 3) ! continue; ! if (status & (UART_LSR_BI | ! UART_LSR_FE | ! UART_LSR_PE)) { ! if (status & (UART_LSR_BI)) { ! if (info->flags & ASYNC_SAK) ! rs_sched_event(line, info, RS_EVENT_DO_SAK); ! else if (iflag & IGNBRK) ! continue; ! else if (iflag & BRKINT) ! rs_sched_event(line, info, RS_EVENT_BREAK_INT); ! else ! ch = 0; ! } else if (iflag & IGNPAR) ! continue; ! if (iflag & PARMRK) { ! queue->buf[head++] = 0xff; ! head &= TTY_BUF_SIZE-1; ! queue->buf[head++] = 0; ! head &= TTY_BUF_SIZE-1; ! } else ! ch = 0; ! } else if ((iflag & PARMRK) && (ch == 0xff)) { ! queue->buf[head++] = 0xff; ! head &= TTY_BUF_SIZE-1; ! } ! queue->buf[head++] = ch; ! head &= TTY_BUF_SIZE-1; ! } while ((status = inb(UART_LSR + info->port)) & ! UART_LSR_DR); ! queue->head = head; ! if ((VLEFT < RQ_THRESHOLD_LW) ! && !set_bit(TTY_RQ_THROTTLED, &info->tty->flags)) ! rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL); ! rs_sched_event(line, info, RS_EVENT_READ_PROCESS); ! } ! if ((status & UART_LSR_THRE) && ! !info->tty->stopped) { ! queue = &info->tty->write_q; ! head = queue->head; ! tail = queue->tail; ! if (head==tail && !info->x_char) ! goto no_xmit; ! if (info->x_char) { ! outb_p(info->x_char, UART_TX + info->port); ! info->x_char = 0; ! } else { ! count = info->xmit_fifo_size; ! while (count--) { ! if (tail == head) ! break; ! outb_p(queue->buf[tail++], ! UART_TX + info->port); ! tail &= TTY_BUF_SIZE-1; ! } ! } ! queue->tail = tail; ! if (VLEFT > WAKEUP_CHARS) ! rs_sched_event(line, info, ! RS_EVENT_WRITE_WAKEUP); ! info->timer = jiffies + info->timeout; ! if (info->timer < timer_table[RS_TIMER].expires) ! timer_table[RS_TIMER].expires = info->timer; ! rs_write_active |= 1 << line; ! timer_active |= 1 << RS_TIMER; ! } ! no_xmit: ! status = inb(UART_MSR + info->port); ! ! if (!(cflag & CLOCAL) && (status & UART_MSR_DDCD)) { ! if (!(status & UART_MSR_DCD)) ! rs_sched_event(line, info, RS_EVENT_HUP_PGRP); ! } ! /* ! * Because of the goto statement, this block must be ! * last. We have to skip the do/while test condition ! * because the THRE interrupt has probably been lost. ! */ ! if ((cflag & CRTSCTS) || ! ((status & UART_MSR_DSR) && ! !(cflag & CNORTSCTS))) { ! if (info->tty->stopped) { ! if (status & UART_MSR_CTS) { ! info->tty->stopped = 0; ! goto restart; ! } ! } else ! info->tty->stopped = !(status & UART_MSR_CTS); ! } ! } while (!(inb(UART_IIR + info->port) & UART_IIR_NO_INT)); } /* *************** *** 256,266 **** unsigned char ivec; ivec = ~inb(ISR->port) & 0x0F; ! for (i = line; ivec; i++) { ! if (ivec & 1) ! UART_ISR_proc(ISR, i); ! ivec = ivec >> 1; ! } } /* --- 273,286 ---- unsigned char ivec; ivec = ~inb(ISR->port) & 0x0F; ! do { ! for (i = line; ivec; i++) { ! if (ivec & 1) ! UART_ISR_proc(ISR, i); ! ivec = ivec >> 1; ! } ! ivec = ~inb(ISR->port) & 0x0F; ! } while (ivec); } /* *************** *** 276,281 **** --- 296,315 ---- } } + #ifdef AUTO_IRQ + /* + * This is the serial driver's interrupt routine while we are probing + * for submarines. + */ + static volatile int rs_irq_triggered; + + static void rs_probe(int irq) + { + rs_irq_triggered = irq; + return; + } + #endif + /* * This subroutine handles all of the timer functionality required for * the serial ports. *************** *** 291,309 **** info = rs_table; next_timeout = END_OF_TIME; for (mask = 1 ; mask ; info++, mask <<= 1) { ! if ((mask > rs_read_process) && ! (mask > rs_write_timeout)) break; ! if (mask & rs_read_process) { ! TTY_READ_FLUSH(info->tty); ! rs_read_process &= ~mask; ! } ! if (mask & rs_write_timeout) { ! if (info->timer > jiffies) { ! rs_write_timeout &= ~mask; ! do_rs_write(info); } ! if ((mask & rs_write_timeout) && (info->timer < next_timeout)) next_timeout = info->timer; } --- 325,366 ---- info = rs_table; next_timeout = END_OF_TIME; for (mask = 1 ; mask ; info++, mask <<= 1) { ! if ((mask > rs_event) && ! (mask > rs_write_active)) break; ! if (mask & rs_event) { ! if (!clear_bit(RS_EVENT_READ_PROCESS, &info->event)) { ! TTY_READ_FLUSH(info->tty); ! } ! if (!clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { ! wake_up(&info->tty->write_q.proc_list); ! } ! if (!clear_bit(RS_EVENT_HUP_PGRP, &info->event)) { ! if (info->tty->pgrp > 0) ! kill_pg(info->tty->pgrp,SIGHUP,1); ! } ! if (!clear_bit(RS_EVENT_BREAK_INT, &info->event)) { ! flush_input(info->tty); ! flush_output(info->tty); ! if (info->tty->pgrp > 0) ! kill_pg(info->tty->pgrp,SIGINT,1); ! } ! if (!clear_bit(RS_EVENT_DO_SAK, &info->event)) { ! do_SAK(info->tty); ! } ! cli(); ! if (info->event) ! next_timeout = 0; ! else ! rs_event &= ~mask; ! sti(); ! } ! if (mask & rs_write_active) { ! if (info->timer <= jiffies) { ! rs_write_active &= ~mask; ! rs_write(info->tty); } ! if ((mask & rs_write_active) && (info->timer < next_timeout)) next_timeout = info->timer; } *************** *** 310,385 **** } if (next_timeout != END_OF_TIME) { timer_table[RS_TIMER].expires = next_timeout; timer_active |= 1 << RS_TIMER; } } ! static void init(struct async_struct * info) { ! unsigned char status1, status2, scratch, scratch2; ! unsigned short port = info->port; ! /* ! * Check to see if a UART is really there. ! */ ! scratch = inb_p(UART_MCR + port); ! outb_p(UART_MCR_LOOP | scratch, UART_MCR + port); ! scratch2 = inb_p(UART_MSR + port); ! outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port); ! status1 = inb_p(UART_MSR + port) & 0xF0; ! outb_p(scratch, UART_MCR + port); ! outb_p(scratch2, UART_MSR + port); ! if (status1 != 0x90) { ! info->type = PORT_UNKNOWN; return; } - if (!(info->flags & ASYNC_NOSCRATCH)) { - scratch = inb(UART_SCR + port); - outb_p(0xa5, UART_SCR + port); - status1 = inb(UART_SCR + port); - outb_p(0x5a, UART_SCR + port); - status2 = inb(UART_SCR + port); - outb_p(scratch, UART_SCR + port); - } else { - status1 = 0xa5; - status2 = 0x5a; - } - if (status1 == 0xa5 && status2 == 0x5a) { - outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port); - scratch = inb(UART_IIR + port) >> 6; - info->xmit_fifo_size = 1; - switch (scratch) { - case 0: - info->type = PORT_16450; - break; - case 1: - info->type = PORT_UNKNOWN; - break; - case 2: - info->type = PORT_16550; - break; - case 3: - info->type = PORT_16550A; - info->xmit_fifo_size = 16; - break; - } - } else - info->type = PORT_8250; - change_speed(info->line); - outb_p(0x00, UART_IER + port); /* disable all intrs */ - outb_p(0x00, UART_MCR + port); /* reset DTR,RTS,OUT_2 */ - (void)inb(UART_RX + port); /* read data port to reset things (?) */ } ! /* ! * This routine gets called when tty_write has put something into ! * the write_queue. It must check wheter the queue is empty, and ! * set the interrupt register accordingly ! */ ! void rs_write(struct tty_struct * tty) { ! do_rs_write(rs_table+DEV_TO_SL(tty->line)); } /* --- 367,443 ---- } if (next_timeout != END_OF_TIME) { timer_table[RS_TIMER].expires = next_timeout; + #ifdef i386 + /* + * This must compile to a single, atomic instruction. + * It does using 386 with GCC; if you're not sure, use + * the set_bit function, which is supposed to be atomic. + */ timer_active |= 1 << RS_TIMER; + #else + set_bit(RS_TIMER, &timer_active); + #endif } } ! /* ! * This routine gets called when tty_write has put something into ! * the write_queue. It calls UART_ISR_proc to simulate an interrupt, ! * which gets things going. ! */ ! void rs_write(struct tty_struct * tty) { ! struct async_struct *info; ! if (!tty || tty->stopped || EMPTY(&tty->write_q)) return; + info = rs_table + DEV_TO_SL(tty->line); + if (!test_bit(info->line, &rs_write_active)) { + cli(); + UART_ISR_proc(info->ISR, info->line); + sti(); } } ! static void rs_throttle(struct tty_struct * tty, int status) { ! struct async_struct *info; ! unsigned char mcr; ! ! printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line), ! status, LEFT(&tty->read_q), LEFT(&tty->secondary)); ! switch (status) { ! case TTY_THROTTLE_RQ_FULL: ! info = rs_table + DEV_TO_SL(tty->line); ! if (tty->termios->c_iflag & IXOFF) { ! info->x_char = STOP_CHAR(tty); ! } else if ((tty->termios->c_cflag & CRTSCTS) || ! ((inb(UART_MSR + info->port) & UART_MSR_DSR) && ! !(tty->termios->c_cflag & CNORTSCTS))) { ! mcr = inb(UART_MCR + info->port); ! mcr &= ~UART_MCR_RTS; ! outb_p(mcr, UART_MCR + info->port); ! } ! break; ! case TTY_THROTTLE_RQ_AVAIL: ! info = rs_table + DEV_TO_SL(tty->line); ! if (tty->termios->c_iflag & IXOFF) { ! cli(); ! if (info->x_char) ! info->x_char = 0; ! else ! info->x_char = START_CHAR(tty); ! sti(); ! } else if ((tty->termios->c_cflag & CRTSCTS) || ! ((inb(UART_MSR + info->port) & UART_MSR_DSR) && ! !(tty->termios->c_cflag & CNORTSCTS))) { ! mcr = inb(UART_MCR + info->port); ! mcr |= UART_MCR_RTS; ! outb_p(mcr, UART_MCR + info->port); ! } ! break; ! } } /* *************** *** 401,410 **** info = rs_table + line; if (!info->port) return; info->tty = 0; - outb_p(0x00, UART_IER + info->port); /* disable all intrs */ - outb_p(0x00, UART_MCR + info->port); /* reset DTR, RTS, */ - outb(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */ ISR = info->ISR; irq = ISR->irq; if (irq == 2) --- 459,466 ---- info = rs_table + line; if (!info->port) return; + shutdown(info); info->tty = 0; ISR = info->ISR; irq = ISR->irq; if (irq == 2) *************** *** 473,478 **** --- 529,548 ---- } } + static void shutdown(struct async_struct * info) + { + unsigned short port = info->port; + + outb_p(0x00, UART_IER + port); /* disable all intrs */ + if (info->tty && !(info->tty->termios->c_cflag & HUPCL)) + outb_p(UART_MCR_DTR, UART_MCR + port); + else + /* reset DTR,RTS,OUT_2 */ + outb_p(0x00, UART_MCR + port); + outb_p(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */ + (void)inb(UART_RX + port); /* read data port to reset things */ + } + void change_speed(unsigned int line) { struct async_struct * info; *************** *** 509,519 **** quot = 0; info->timeout = 0; } ! if (!quot) ! outb(0x00,UART_MCR + port); ! else if (!inb(UART_MCR + port)) ! startup(info); ! /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); cval >>= 4; if (cflag & PARENB) --- 579,589 ---- quot = 0; info->timeout = 0; } ! if (!quot) { ! shutdown(info); ! return; ! } ! /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); cval >>= 4; if (cflag & PARENB) *************** *** 569,578 **** irq = ISR->irq; if (irq == 2) irq = 9; - if (!new_irq) - new_irq = irq; - if (!new_port) - new_port = info->port; if (irq != new_irq) { /* * We need to change the IRQ for this board. OK, if --- 639,644 ---- *************** *** 614,624 **** } cli(); if (new_port != info->port) { ! outb_p(0x00, UART_IER + info->port); /* disable all intrs */ ! outb(0x00, UART_MCR + info->port); /* reset DTR, RTS, */ info->port = new_port; - init(info); startup(info); } sti(); return 0; --- 680,689 ---- } cli(); if (new_port != info->port) { ! shutdown(info); info->port = new_port; startup(info); + change_speed(info->line); } sti(); return 0; *************** *** 737,748 **** if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; info = rs_table + line; ! if (!info->port) return -ENODEV; info->tty = tty; tty->write = rs_write; tty->close = rs_close; tty->ioctl = rs_ioctl; ISR = info->ISR; irq = ISR->irq; if (irq == 2) --- 802,814 ---- if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; info = rs_table + line; ! if (!info->port || !info->ISR->irq) return -ENODEV; info->tty = tty; tty->write = rs_write; tty->close = rs_close; tty->ioctl = rs_ioctl; + tty->throttle = rs_throttle; ISR = info->ISR; irq = ISR->irq; if (irq == 2) *************** *** 771,785 **** return 0; } long rs_init(long kmem_start) { int i; struct async_struct * info; ! timer_table[RS_TIMER].fn = rs_timer; timer_table[RS_TIMER].expires = 0; for (i = 0; i < 16; i++) { IRQ_ISR[i] = 0; } for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { info->line = i; --- 837,969 ---- return 0; } + static void init(struct async_struct * info) + { + #ifdef AUTO_IRQ + unsigned char status1, status2, scratch, save_ICP=0; + unsigned short ICP=0, port = info->port; + unsigned long timeout; + + /* + * Enable interrupts and see who answers + */ + rs_irq_triggered = 0; + scratch = inb_p(UART_IER + port); + status1 = inb_p(UART_MCR + port); + if (info->flags & ASYNC_FOURPORT) { + outb_p(UART_MCR_DTR | UART_MCR_RTS, UART_MCR + port); + outb_p(0x0f,UART_IER + port); /* enable all intrs */ + ICP = (port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb(ICP); + } else { + outb_p(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, + UART_MCR + port); + outb_p(0x0f,UART_IER + port); /* enable all intrs */ + } + timeout = jiffies+2; + while (timeout >= jiffies) { + if (rs_irq_triggered) + break; + } + /* + * Now check to see if we got any business, and clean up. + */ + if (rs_irq_triggered) { + outb_p(0, UART_IER + port); + info->ISR->irq = rs_irq_triggered; + } else { + outb_p(scratch, UART_IER + port); + outb_p(status1, UART_MCR + port); + if (info->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); + info->type = PORT_UNKNOWN; + return; + } + #else /* AUTO_IRQ */ + unsigned char status1, status2, scratch, scratch2; + unsigned short port = info->port; + + /* + * Check to see if a UART is really there. + */ + scratch = inb_p(UART_MCR + port); + outb_p(UART_MCR_LOOP | scratch, UART_MCR + port); + scratch2 = inb_p(UART_MSR + port); + outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port); + status1 = inb_p(UART_MSR + port) & 0xF0; + outb_p(scratch, UART_MCR + port); + outb_p(scratch2, UART_MSR + port); + if (status1 != 0x90) { + info->type = PORT_UNKNOWN; + return; + } + #endif /* AUTO_IRQ */ + + if (!(info->flags & ASYNC_NOSCRATCH)) { + scratch = inb(UART_SCR + port); + outb_p(0xa5, UART_SCR + port); + status1 = inb(UART_SCR + port); + outb_p(0x5a, UART_SCR + port); + status2 = inb(UART_SCR + port); + outb_p(scratch, UART_SCR + port); + } else { + status1 = 0xa5; + status2 = 0x5a; + } + if (status1 == 0xa5 && status2 == 0x5a) { + outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port); + scratch = inb(UART_IIR + port) >> 6; + info->xmit_fifo_size = 1; + switch (scratch) { + case 0: + info->type = PORT_16450; + break; + case 1: + info->type = PORT_UNKNOWN; + break; + case 2: + info->type = PORT_16550; + break; + case 3: + info->type = PORT_16550A; + info->xmit_fifo_size = 16; + break; + } + } else + info->type = PORT_8250; + startup(info); + change_speed(info->line); + shutdown(info); + } + long rs_init(long kmem_start) { int i; struct async_struct * info; ! #ifdef AUTO_IRQ ! int irq_lines = 0; ! struct sigaction sa; ! /* ! * We will be auto probing for irq's, so turn on interrupts now! ! */ ! sti(); ! ! sa.sa_handler = rs_probe; ! sa.sa_flags = (SA_INTERRUPT); ! sa.sa_mask = 0; ! sa.sa_restorer = NULL; ! #endif timer_table[RS_TIMER].fn = rs_timer; timer_table[RS_TIMER].expires = 0; + for (i = 0; i < 16; i++) { IRQ_ISR[i] = 0; + #ifdef AUTO_IRQ + if (!irqaction(i, &sa)) + irq_lines |= 1 << i; + #endif } for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { info->line = i; *************** *** 787,792 **** --- 971,978 ---- info->type = PORT_UNKNOWN; info->timer = 0; info->custom_divisor = 0; + info->x_char = 0; + info->event = 0; if (!info->ISR->line) { info->ISR->line = i; info->ISR->refcnt = 0; *************** *** 817,822 **** --- 1003,1015 ---- break; } } + #ifdef AUTO_IRQ + cli(); + for (i = 0; i < 16; i++) { + if (irq_lines & (1 << i)) + free_irq(i); + } + #endif return kmem_start; } =================================================================== RCS file: kernel/chr_drv/RCS/tty_io.c,v retrieving revision 1.1 diff -c -r1.1 kernel/chr_drv/tty_io.c *** 1.1 1992/10/05 01:27:12 --- kernel/chr_drv/tty_io.c 1992/10/06 11:27:34 *************** *** 33,41 **** #include #include - #include #include #include #include "vt_kern.h" --- 33,41 ---- #include #include #include #include + #include #include "vt_kern.h" *************** *** 207,214 **** tty->stopped=1; continue; } ! if ((START_CHAR(tty) != __DISABLED_CHAR) && ! (c==START_CHAR(tty))) { tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_START; tty->stopped=0; --- 207,215 ---- tty->stopped=1; continue; } ! if (((I_IXANY(tty)) && tty->stopped) || ! ((START_CHAR(tty) != __DISABLED_CHAR) && ! (c==START_CHAR(tty)))) { tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_START; tty->stopped=0; *************** *** 258,264 **** if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW) && !clear_bit(TTY_RQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL); ! } int is_ignored(int sig) --- 259,267 ---- if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW) && !clear_bit(TTY_RQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL); ! if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW) ! && !clear_bit(TTY_SQ_THROTTLED, &tty->flags)) ! tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL); } int is_ignored(int sig) *************** *** 373,378 **** --- 376,388 ---- break; }; wake_up(&tty->read_q.proc_list); + /* + * If there is enough space in the secondary queue + * now, let the low-level driver know. + */ + if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW) + && !clear_bit(TTY_SQ_THROTTLED, &tty->flags)) + tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL); if (b-buf >= minimum || !current->timeout) break; if (current->signal & ~current->blocked) *************** *** 382,394 **** TTY_READ_FLUSH(tty); if (tty->link) TTY_WRITE_FLUSH(tty->link); - /* - * If there is enough space in the secondary queue - * now, let the low-level driver know. - */ - if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW) - && !clear_bit(TTY_SQ_THROTTLED, &tty->flags)) - tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL); cli(); if (EMPTY(&tty->secondary)) interruptible_sleep_on(&tty->secondary.proc_list); --- 392,397 ---- *************** *** 558,565 **** return retval; } if (IS_A_PTY(dev) && !tty_table[PTY_OTHER(dev)]) { ! o_tty = tty_table[PTY_OTHER(dev)] = ! (struct tty_struct *) get_free_page(GFP_USER); if (!o_tty) { free_page((unsigned long)tty); return -ENOMEM; --- 561,575 ---- return retval; } if (IS_A_PTY(dev) && !tty_table[PTY_OTHER(dev)]) { ! o_tty = (struct tty_struct *) get_free_page(GFP_USER); ! /* ! * Check for race condition, since get_free_page may sleep. ! */ ! if (tty_table[PTY_OTHER(dev)]) { ! free_page((unsigned long) o_tty); ! goto other_done; ! } ! tty_table[PTY_OTHER(dev)] = o_tty; if (!o_tty) { free_page((unsigned long)tty); return -ENOMEM; *************** *** 573,578 **** --- 583,589 ---- tty->link = o_tty; o_tty->link = tty; } + other_done: } if (IS_A_PTY_MASTER(dev)) { if (tty->count) *************** *** 650,661 **** return; if (tty->close) tty->close(tty, filp); ! if (!tty->count && (tty == redirect)) redirect = NULL; ! if (tty = tty->link) ! if (!tty->count && (tty == redirect)) ! redirect = NULL; ! if (!tty->count && !(tty->link && tty->link->count)) { if (tty->link) { free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev))); TTY_TABLE(PTY_OTHER(dev)) = 0; --- 661,671 ---- return; if (tty->close) tty->close(tty, filp); ! if (tty == redirect) redirect = NULL; ! if (tty->link && !tty->link->count && (tty->link == redirect)) ! redirect = NULL; ! if (!tty->link || !tty->link->count) { if (tty->link) { free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev))); TTY_TABLE(PTY_OTHER(dev)) = 0; *************** *** 720,725 **** --- 730,776 ---- }; /* + * This implements the "Secure Attention Key" --- the idea is to + * prevent trojan horses by killing all processes associated with this + * tty when the user hits the "Secure Attention Key". Required for + * super-paranoid applications --- see the Orange Book for more details. + * + * This code could be nicer; ideally it should send a HUP, wait a few + * seconds, then send a INT, and then a KILL signal. But you then + * have to coordinate with the init process, since all processes associated + * with the current tty must be dead before the new getty is allowed + * to spawn. + */ + void do_SAK( struct tty_struct *tty) + { + struct task_struct **p; + int line = tty->line; + int session = tty->session; + int i; + struct file *filp; + + flush_input(tty); + flush_output(tty); + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!(*p)) + continue; + if (((*p)->tty == line) || + ((session > 0) && ((*p)->session == session))) + send_sig(SIGKILL, *p, 1); + else { + for (i=0; i < NR_FILE; i++) { + filp = (*p)->filp[i]; + if (filp && (filp->f_op == &tty_fops) && + (MINOR(filp->f_rdev) == line)) { + send_sig(SIGKILL, *p, 1); + break; + } + } + } + } + } + + /* * This subroutine initializes a tty structure. We have to set up * things correctly for each different type of tty. */ *************** *** 739,756 **** memset(tp, 0, sizeof(struct termios)); memcpy(tp->c_cc, INIT_C_CC, NCCS); if (IS_A_CONSOLE(line)) { ! tp->c_iflag = ICRNL; tp->c_oflag = OPOST | ONLCR; ! tp->c_cflag = B38400 | CS8; ! tp->c_lflag = IXON | ISIG | ICANON | ECHO | ! ECHOCTL | ECHOKE; } else if IS_A_SERIAL(line) { ! tp->c_cflag = B2400 | CS8; } else if IS_A_PTY_MASTER(line) { ! tp->c_cflag = B9600 | CS8; } else if IS_A_PTY_SLAVE(line) { ! tp->c_cflag = B9600 | CS8; ! tp->c_lflag = IXON | ISIG | ICANON; } } tty->termios = tty_termios[line]; --- 790,806 ---- memset(tp, 0, sizeof(struct termios)); memcpy(tp->c_cc, INIT_C_CC, NCCS); if (IS_A_CONSOLE(line)) { ! tp->c_iflag = ICRNL | IXON; tp->c_oflag = OPOST | ONLCR; ! tp->c_cflag = B38400 | CS8 | CREAD; ! tp->c_lflag = ISIG | ICANON | ECHO | ECHOCTL | ECHOKE; } else if IS_A_SERIAL(line) { ! tp->c_cflag = B2400 | CS8 | CREAD | HUPCL; } else if IS_A_PTY_MASTER(line) { ! tp->c_cflag = B9600 | CS8 | CREAD; } else if IS_A_PTY_SLAVE(line) { ! tp->c_cflag = B9600 | CS8 | CREAD; ! tp->c_lflag = ISIG | ICANON; } } tty->termios = tty_termios[line];