aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Benedict Torvalds <torvalds@klaava.Helsinki.FI>1992-06-23 17:28:24 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:05 -0400
commita5760f09ab1472f9d89669ce9e16702d617cab76 (patch)
treee85b6becf3cf1c0fbf0596d7138ecaf34c952f41
parente38481072b4a12a490335fbe1917ffc7bb9ce32e (diff)
downloadarchive-a5760f09ab1472f9d89669ce9e16702d617cab76.tar.gz
Re: Too much uneaten serial causes system hang?v0.96b-pl1
In article <arumble.709312764@extro.ucc.su.OZ.AU> arumble@extro.ucc.su.OZ.AU (Anthony Rumble) writes: > >YES! I have noticed this VERY exact thing also! Oh, well: it's a bug in the serial drivers that I have already fixed, but I haven't done the c-diffs yet. I have rewritten big parts of the serial line code to be more easily configured for different IRQ numbers, and I noticed the bug while doing that. I'll make patch1 for 0.96b available later today or tomorrow. patch1 will be mostly just the serial driver code: it allows changing the irq's (and port addresses) of serial devices on the fly (with an ioctl call), so people that have ser4 on irq5 etc shouldn't have to recompile the kernel. It also returns EBUSY if you try to open a serial line that shares the irq-line with another line etc. Another change in patch1 will the the handling of ctrl-alt-del: it will send a SIGINT to the init process if the reset-function is disabled. This makes it ideal for a controlled shutdown, but it does need a /bin/init that knows about this. Linus PS. It seems both the DOS-fs and the extended fs will be out for alpha-testing next week, so I assume 0.97 will have them both if things work out ok.
-rw-r--r--include/linux/tty.h23
-rw-r--r--include/termios.h2
-rw-r--r--kernel/chr_drv/serial.c341
-rw-r--r--kernel/chr_drv/tty_io.c17
-rw-r--r--kernel/chr_drv/tty_ioctl.c29
-rw-r--r--kernel/exit.c10
-rw-r--r--kernel/math/ea.c2
-rw-r--r--kernel/math/emulate.c22
-rw-r--r--kernel/sys.c3
-rw-r--r--kernel/sys_call.S24
10 files changed, 329 insertions, 144 deletions
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 29d539e..d73fcad 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -34,6 +34,23 @@ struct tty_queue {
unsigned char buf[TTY_BUF_SIZE];
};
+struct serial_struct {
+ unsigned short type;
+ unsigned short line;
+ unsigned short port;
+ unsigned short irq;
+ struct tty_struct * tty;
+};
+
+/*
+ * These are the supported serial types.
+ */
+#define PORT_UNKNOWN 0
+#define PORT_8250 1
+#define PORT_16450 2
+#define PORT_16550 3
+#define PORT_16550A 4
+
#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40)
#define IS_A_PTY(min) ((min) & 0x80)
@@ -161,6 +178,7 @@ do { \
} while (0)
extern struct tty_struct tty_table[];
+extern struct serial_struct serial_table[];
extern struct tty_struct * redirect;
extern int fg_console;
extern unsigned long video_num_columns;
@@ -195,7 +213,10 @@ extern void con_write(struct tty_struct * tty);
extern void mpty_write(struct tty_struct * tty);
extern void spty_write(struct tty_struct * tty);
-extern void serial_open(unsigned int line);
+extern int serial_open(unsigned int line, struct file * filp);
+extern void serial_close(unsigned int line, struct file * filp);
+extern int get_serial_info(unsigned int, struct serial_struct *);
+extern int set_serial_info(unsigned int, struct serial_struct *);
void copy_to_cooked(struct tty_struct * tty);
diff --git a/include/termios.h b/include/termios.h
index 42410af..da7b09a 100644
--- a/include/termios.h
+++ b/include/termios.h
@@ -35,6 +35,8 @@
#define TIOCINQ FIONREAD
#define TIOCLINUX 0x541C
#define TIOCCONS 0x541D
+#define TIOCGSERIAL 0x541E
+#define TIOCSSERIAL 0x541F
struct winsize {
unsigned short ws_row;
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index 5e8e7f7..bf7a0f4 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -14,44 +14,48 @@
*/
#include <signal.h>
-
+#include <errno.h>
+
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/segment.h>
#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
+/*
+ * note that IRQ9 is what many docs call IRQ2 - on the AT hardware
+ * the old IRQ2 line has been changed to IRQ9. The serial_table
+ * structure considers IRQ2 to be the same as IRQ9.
+ */
+extern void IRQ9_interrupt(void);
extern void IRQ3_interrupt(void);
extern void IRQ4_interrupt(void);
+extern void IRQ5_interrupt(void);
+
+struct serial_struct serial_table[NR_SERIALS] = {
+ { PORT_UNKNOWN, 0, 0x3F8, 4, NULL},
+ { PORT_UNKNOWN, 1, 0x2F8, 3, NULL},
+ { PORT_UNKNOWN, 2, 0x3E8, 4, NULL},
+ { PORT_UNKNOWN, 3, 0x2E8, 3, NULL},
+};
+
+static struct serial_struct * irq_info[16] = { NULL, };
-#define PORT_UNKNOWN 0
-#define PORT_8250 1
-#define PORT_16450 2
-#define PORT_16550 3
-#define PORT_16550A 4
-
-int port_table[] = {
- PORT_UNKNOWN,
- PORT_UNKNOWN,
- PORT_UNKNOWN,
- PORT_UNKNOWN,
- PORT_UNKNOWN
-};
-
-static void modem_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
+static void modem_status_intr(struct serial_struct * info)
{
- unsigned char status = inb(port+6);
+ unsigned char status = inb(info->port+6);
- if ((status & 0x88) == 0x08 && tty->pgrp > 0)
- kill_pg(tty->pgrp,SIGHUP,1);
+ if ((status & 0x88) == 0x08 && info->tty->pgrp > 0)
+ kill_pg(info->tty->pgrp,SIGHUP,1);
#if 0
if ((status & 0x10) == 0x10)
- tty->stopped = 0;
+ info->tty->stopped = 0;
else
- tty->stopped = 1;
+ info->tty->stopped = 1;
#endif
}
@@ -64,65 +68,65 @@ static void modem_status_intr(unsigned line, unsigned port, struct tty_struct *
* again. With serial lines, the interrupts can happen so often that the
* races actually are noticeable.
*/
-static void send_intr(unsigned line, unsigned port, struct tty_struct * tty)
+static void send_intr(struct serial_struct * info)
{
+ unsigned short port = info->port;
+ unsigned int timer = SER1_TIMEOUT + info->line;
+ struct tty_queue * queue = info->tty->write_q;
int c, i = 0;
-#define TIMER ((SER1_TIMEOUT-1)+line)
- timer_active &= ~(1 << TIMER);
+ timer_active &= ~(1 << timer);
do {
- if ((c = GETCH(tty->write_q)) < 0)
+ if ((c = GETCH(queue)) < 0)
return;
outb(c,port);
i++;
- } while ( port_table[line] == PORT_16550A &&
- i < 14 && !EMPTY(tty->write_q));
- timer_table[TIMER].expires = jiffies + 10;
- timer_active |= 1 << TIMER;
- if (LEFT(tty->write_q) > WAKEUP_CHARS)
- wake_up(&tty->write_q->proc_list);
-#undef TIMER
+ } while (info->type == PORT_16550A &&
+ i < 14 && !EMPTY(queue));
+ timer_table[timer].expires = jiffies + 10;
+ timer_active |= 1 << timer;
+ if (LEFT(queue) > WAKEUP_CHARS)
+ wake_up(&queue->proc_list);
}
-static void receive_intr(unsigned line, unsigned port, struct tty_struct * tty)
+static void receive_intr(struct serial_struct * info)
{
- while (!FULL(tty->read_q)) {
- if (!(inb(port+5) & 1))
- break;
- PUTCH(inb(port),tty->read_q);
- };
- timer_active |= (1<<(SER1_TIMER-1))<<line;
+ unsigned short port = info->port;
+ struct tty_queue * queue = info->tty->read_q;
+
+ while (inb(port+5) & 1)
+ PUTCH(inb(port),queue);
+ timer_active |= (1<<SER1_TIMER)<<info->line;
}
-static void line_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
+static void line_status_intr(struct serial_struct * info)
{
- unsigned char status = inb(port+5);
+ unsigned char status = inb(info->port+5);
/* printk("line status: %02x\n",status); */
}
-static void (*jmp_table[4])(unsigned,unsigned,struct tty_struct *) = {
+static void (*jmp_table[4])(struct serial_struct *) = {
modem_status_intr,
send_intr,
receive_intr,
line_status_intr
};
-static void check_tty(unsigned line,struct tty_struct * tty)
+static void check_tty(struct serial_struct * info)
{
- unsigned short port;
unsigned char ident;
- if (!(port = tty->read_q->data))
+ if (!info || !info->tty || !info->port)
return;
while (1) {
- ident = inb(port+2) & 7;
+ ident = inb(info->port+2) & 7;
if (ident & 1)
return;
ident >>= 1;
if (ident > 3)
return;
- jmp_table[ident](line,port,tty);
+ jmp_table[ident](info);
}
}
@@ -131,8 +135,7 @@ static void check_tty(unsigned line,struct tty_struct * tty)
*/
void do_IRQ3(void)
{
- check_tty(2,tty_table+65);
- check_tty(4,tty_table+67);
+ check_tty(irq_info[3]);
}
/*
@@ -140,8 +143,17 @@ void do_IRQ3(void)
*/
void do_IRQ4(void)
{
- check_tty(1,tty_table+64);
- check_tty(3,tty_table+66);
+ check_tty(irq_info[4]);
+}
+
+void do_IRQ5(void)
+{
+ check_tty(irq_info[5]);
+}
+
+void do_IRQ9(void)
+{
+ check_tty(irq_info[9]);
}
static void com1_timer(void)
@@ -168,52 +180,51 @@ static void com4_timer(void)
* Again, we disable interrupts to be sure there aren't any races:
* see send_intr for details.
*/
-static inline void do_rs_write(unsigned line, struct tty_struct * tty)
+static inline void do_rs_write(struct serial_struct * info)
{
- int port;
-
-#define TIMER ((SER1_TIMEOUT-1)+line)
- if (!tty || !tty->write_q || EMPTY(tty->write_q))
+ if (!info->tty || !info->port)
return;
- if (!(port = tty->write_q->data))
+ if (!info->tty->write_q || EMPTY(info->tty->write_q))
return;
cli();
- if (inb_p(port+5) & 0x20)
- send_intr(line,port,tty);
+ if (inb_p(info->port+5) & 0x20)
+ send_intr(info);
else {
- timer_table[TIMER].expires = jiffies + 10;
- timer_active |= 1 << TIMER;
+ unsigned int timer = SER1_TIMEOUT+info->line;
+
+ timer_table[timer].expires = jiffies + 10;
+ timer_active |= 1 << timer;
}
sti();
-#undef TIMER
}
static void com1_timeout(void)
{
- do_rs_write(1,tty_table+64);
+ do_rs_write(serial_table);
}
static void com2_timeout(void)
{
- do_rs_write(2,tty_table+65);
+ do_rs_write(serial_table + 1);
}
static void com3_timeout(void)
{
- do_rs_write(3,tty_table+66);
+ do_rs_write(serial_table + 2);
}
static void com4_timeout(void)
{
- do_rs_write(4,tty_table+67);
+ do_rs_write(serial_table + 3);
}
-static void init(int port, int line)
+static void init(struct serial_struct * info)
{
unsigned char status1, status2, scratch;
+ unsigned short port = info->port;
if (inb(port+5) == 0xff) {
- port_table[line] = PORT_UNKNOWN;
+ info->type = PORT_UNKNOWN;
return;
}
@@ -227,51 +238,57 @@ static void init(int port, int line)
outb_p(0x01, port+2);
scratch = inb(port+2) >> 6;
switch (scratch) {
- case 0: printk("serial port at 0x%04x is a 16450\n", port);
- port_table[line] = PORT_16450;
- break;
- case 1: printk("serial port at 0x%04x is unknown\n", port);
- port_table[line] = PORT_UNKNOWN;
- break;
- case 2: printk("serial port at 0x%04x is a 16550 (FIFO's disabled)\n", port);
- port_table[line] = PORT_16550;
- outb_p(0x00, port+2);
- break;
- case 3: printk("serial port at 0x%04x is a 16550a (FIFO's enabled)\n", port);
- port_table[line] = PORT_16550A;
- outb_p(0xc7, port+2);
- break;
+ case 0:
+ info->type = PORT_16450;
+ break;
+ case 1:
+ info->type = PORT_UNKNOWN;
+ break;
+ case 2:
+ info->type = PORT_16550;
+ outb_p(0x00, port+2);
+ break;
+ case 3:
+ info->type = PORT_16550A;
+ outb_p(0xc7, port+2);
+ break;
}
} else
- printk("serial port at 0x%04x is a 8250\n", port);
-
+ info->type = PORT_8250;
outb_p(0x80,port+3); /* set DLAB of line control reg */
- outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */
+ outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps) */
outb_p(0x00,port+1); /* MS of divisor */
outb_p(0x03,port+3); /* reset DLAB */
outb_p(0x00,port+4); /* reset DTR,RTS, OUT_2 */
- outb_p(0x0f,port+1); /* enable all intrs */
+ outb_p(0x00,port+1); /* disable all intrs */
(void)inb(port); /* read data port to reset things (?) */
}
-/*
- * this routine enables interrupts on 'line', and disables them on
- * 'line ^ 2', as they share the same IRQ. Braindamaged AT hardware.
- */
-void serial_open(unsigned line)
+void serial_close(unsigned line, struct file * filp)
{
- unsigned short port;
- unsigned short port2;
+ struct serial_struct * info;
+ int irq;
- if (line>3)
+ if (line >= NR_SERIALS)
return;
- port = tty_table[64+line].read_q->data;
- if (!port)
+ info = serial_table + line;
+ if (!info->port)
return;
- port2 = tty_table[64+(line ^ 2)].read_q->data;
- cli();
- if (port2)
- outb_p(0x00,port2+4);
+ outb(0x00,info->port+4); /* reset DTR, RTS, */
+ irq = info->irq;
+ if (irq == 2)
+ irq = 9;
+ if (irq_info[irq] == info) {
+ irq_info[irq] = NULL;
+ if (irq < 8)
+ outb(inb_p(0x21) | (1<<irq),0x21);
+ else
+ outb(inb_p(0xA1) | (1<<(irq-8)),0xA1);
+ }
+}
+
+static void startup(unsigned short port)
+{
outb_p(0x03,port+3); /* reset DLAB */
outb_p(0x0f,port+4); /* set DTR,RTS, OUT_2 */
outb_p(0x0f,port+1); /* enable all intrs */
@@ -279,11 +296,106 @@ void serial_open(unsigned line)
inb_p(port+0);
inb_p(port+6);
inb(port+2);
+}
+
+/*
+ * this routine enables interrupts on 'line', and disables them for any
+ * other serial line that shared the same IRQ. Braindamaged AT hardware.
+ */
+int serial_open(unsigned line, struct file * filp)
+{
+ struct serial_struct * info;
+ int irq;
+ unsigned short port;
+
+ if (line >= NR_SERIALS)
+ return -ENODEV;
+ info = serial_table + line;
+ if (!(port = info->port))
+ return -ENODEV;
+ irq = info->irq;
+ if (irq == 2)
+ irq = 9;
+ if (irq_info[irq] && irq_info[irq] != info)
+ return -EBUSY;
+ cli();
+ startup(port);
+ irq_info[irq] = info;
+ if (irq < 8)
+ outb(inb_p(0x21) & ~(1<<irq),0x21);
+ else
+ outb(inb_p(0xA1) & ~(1<<(irq-8)),0xA1);
+ sti();
+ return 0;
+}
+
+int get_serial_info(unsigned int line, struct serial_struct * info)
+{
+ if (line >= NR_SERIALS)
+ return -ENODEV;
+ if (!info)
+ return -EFAULT;
+ memcpy_tofs(info,serial_table+line,sizeof(*info));
+ return 0;
+}
+
+int set_serial_info(unsigned int line, struct serial_struct * info)
+{
+ struct serial_struct tmp;
+ unsigned new_port;
+ unsigned irq,new_irq;
+
+ if (!suser())
+ return -EPERM;
+ if (line >= NR_SERIALS)
+ return -ENODEV;
+ if (!info)
+ return -EFAULT;
+ memcpy_fromfs(&tmp,info,sizeof(tmp));
+ info = serial_table + line;
+ if (!(new_port = tmp.port))
+ new_port = info->port;
+ if (!(new_irq = tmp.irq))
+ new_irq = info->irq;
+ if (new_irq > 15 || new_port > 0xffff)
+ return -EINVAL;
+ if (new_irq == 2)
+ new_irq = 9;
+ irq = info->irq;
+ if (irq == 2)
+ irq = 9;
+ if (irq != new_irq) {
+ if (irq_info[new_irq])
+ return -EBUSY;
+ cli();
+ irq_info[new_irq] = irq_info[irq];
+ irq_info[irq] = NULL;
+ info->irq = new_irq;
+ if (irq < 8)
+ outb(inb_p(0x21) | (1<<irq),0x21);
+ else
+ outb(inb_p(0xA1) | (1<<(irq-8)),0xA1);
+ if (new_irq < 8)
+ outb(inb_p(0x21) & ~(1<<new_irq),0x21);
+ else
+ outb(inb_p(0xA1) & ~(1<<(new_irq-8)),0xA1);
+ }
+ cli();
+ if (new_port != info->port) {
+ outb(0x00,info->port+4); /* reset DTR, RTS, */
+ info->port = new_port;
+ init(info);
+ startup(new_port);
+ }
sti();
+ return 0;
}
long rs_init(long kmem_start)
{
+ int i;
+ struct serial_struct * info;
+
/* SERx_TIMER timers are used for receiving: timeout is always 0 (immediate) */
timer_table[SER1_TIMER].fn = com1_timer;
timer_table[SER1_TIMER].expires = 0;
@@ -304,11 +416,26 @@ long rs_init(long kmem_start)
timer_table[SER4_TIMEOUT].expires = 0;
set_intr_gate(0x23,IRQ3_interrupt);
set_intr_gate(0x24,IRQ4_interrupt);
- init(tty_table[64].read_q->data, 1);
- init(tty_table[65].read_q->data, 2);
- init(tty_table[66].read_q->data, 3);
- init(tty_table[67].read_q->data, 4);
- outb(inb_p(0x21)&0xE7,0x21);
+ set_intr_gate(0x25,IRQ5_interrupt);
+ set_intr_gate(0x29,IRQ9_interrupt);
+ for (i = 0, info = serial_table; i < NR_SERIALS; i++,info++) {
+ info->tty = (tty_table+64) + i;
+ init(info);
+ switch (info->type) {
+ case PORT_8250:
+ printk("serial port at 0x%04x is a 8250\n", info->port);
+ break;
+ case PORT_16450:
+ printk("serial port at 0x%04x is a 16450\n", info->port);
+ break;
+ case PORT_16550:
+ printk("serial port at 0x%04x is a 16550\n", info->port);
+ break;
+ case PORT_16550A:
+ printk("serial port at 0x%04x is a 16550A\n", info->port);
+ break;
+ }
+ }
return kmem_start;
}
@@ -321,7 +448,7 @@ long rs_init(long kmem_start)
*/
void rs_write(struct tty_struct * tty)
{
- int line = tty - tty_table - 63;
+ int line = tty - tty_table - 64;
- do_rs_write(line,tty);
+ do_rs_write(serial_table+line);
}
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index 37a7988..91bb21c 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -485,14 +485,13 @@ static int tty_open(struct inode * inode, struct file * filp)
tty->pgrp = current->pgrp;
}
if (IS_A_SERIAL(dev))
- serial_open(dev-64);
+ return serial_open(dev-64,filp);
return 0;
}
static void tty_release(struct inode * inode, struct file * filp)
{
int dev;
- unsigned short port;
struct tty_struct * tty, * slave;
dev = inode->i_rdev;
@@ -507,9 +506,9 @@ static void tty_release(struct inode * inode, struct file * filp)
return;
if (tty == redirect)
redirect = NULL;
- if (port = tty->read_q->data)
- outb(0x0c,port+4); /* reset DTR, RTS, */
- if (IS_A_PTY_MASTER(dev)) {
+ if (IS_A_SERIAL(dev))
+ serial_close(dev-64,filp);
+ else if (IS_A_PTY_MASTER(dev)) {
slave = tty_table + PTY_OTHER(dev);
if (slave->pgrp > 0)
kill_pg(slave->pgrp,SIGHUP,1);
@@ -550,14 +549,6 @@ long tty_init(long kmem_start)
chrdev_fops[5] = &tty_fops;
for (i=0 ; i < QUEUES ; i++)
tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
- rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
- rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
- rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
- rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
- rs_queues[6] = (struct tty_queue) {0x3e8,0,0,0,""};
- rs_queues[7] = (struct tty_queue) {0x3e8,0,0,0,""};
- rs_queues[9] = (struct tty_queue) {0x2e8,0,0,0,""};
- rs_queues[10] = (struct tty_queue) {0x2e8,0,0,0,""};
for (i=0 ; i<256 ; i++) {
tty_table[i] = (struct tty_struct) {
{0, 0, 0, 0, 0, INIT_C_CC},
diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c
index c32265d..b6377e8 100644
--- a/kernel/chr_drv/tty_ioctl.c
+++ b/kernel/chr_drv/tty_ioctl.c
@@ -27,13 +27,13 @@ static unsigned short quotient[] = {
64, 48, 24, 12, 6, 3
};
-static void change_speed(struct tty_struct * tty)
+static void change_speed(struct serial_struct * info)
{
unsigned short port,quot;
- if (!(port = tty->read_q->data))
+ if (!(port = info->port))
return;
- quot = quotient[tty->termios.c_cflag & CBAUD];
+ quot = quotient[info->tty->termios.c_cflag & CBAUD];
cli();
outb_p(0x80,port+3); /* set DLAB */
outb_p(quot & 0xff,port); /* LS of divisor */
@@ -75,11 +75,11 @@ static void wait_until_sent(struct tty_struct * tty)
sti();
}
-static void send_break(struct tty_struct * tty)
+static void send_break(struct serial_struct * info)
{
unsigned short port;
- if (!(port = tty->read_q->data))
+ if (!(port = info->port))
return;
port += 3;
current->state = TASK_INTERRUPTIBLE;
@@ -144,7 +144,8 @@ static int set_termios(struct tty_struct * tty, struct termios * termios,
}
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
- change_speed(tty);
+ if (IS_A_SERIAL(channel))
+ change_speed(serial_table+channel-64);
return 0;
}
@@ -192,7 +193,8 @@ static int set_termio(struct tty_struct * tty, struct termio * termio,
tty->termios.c_line = tmp_termio.c_line;
for(i=0 ; i < NCC ; i++)
tty->termios.c_cc[i] = tmp_termio.c_cc[i];
- change_speed(tty);
+ if (IS_A_SERIAL(channel))
+ change_speed(serial_table+channel-64);
return 0;
}
@@ -279,9 +281,11 @@ int tty_ioctl(struct inode * inode, struct file * file,
case TCSETA:
return set_termio(tty,(struct termio *) arg, dev);
case TCSBRK:
+ if (!IS_A_SERIAL(dev))
+ return -EINVAL;
wait_until_sent(tty);
if (!arg)
- send_break(tty);
+ send_break(serial_table+dev-64);
return 0;
case TCXONC:
switch (arg) {
@@ -394,6 +398,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
else
redirect = tty;
return 0;
+ case TIOCGSERIAL:
+ if (!IS_A_SERIAL(dev))
+ return -EINVAL;
+ verify_area((void *) arg,sizeof(struct serial_struct));
+ return get_serial_info(dev-64,(struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ if (!IS_A_SERIAL(dev))
+ return -EINVAL;
+ return set_serial_info(dev-64,(struct serial_struct *) arg);
default:
return vt_ioctl(tty, dev, cmd, arg);
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 11f126a..00dfdb8 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -284,7 +284,10 @@ static void forget_original_parent(struct task_struct * father)
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p && (*p)->p_opptr == father)
- (*p)->p_opptr = task[1];
+ if (task[1])
+ (*p)->p_opptr = task[1];
+ else
+ (*p)->p_opptr = task[0];
}
volatile void do_exit(long code)
@@ -342,7 +345,10 @@ volatile void do_exit(long code)
current->p_cptr = p->p_osptr;
p->p_ysptr = NULL;
p->flags &= ~PF_PTRACED;
- p->p_pptr = task[1];
+ if (task[1])
+ p->p_pptr = task[1];
+ else
+ p->p_pptr = task[0];
p->p_osptr = p->p_pptr->p_cptr;
p->p_osptr->p_ysptr = p;
p->p_pptr->p_cptr = p;
diff --git a/kernel/math/ea.c b/kernel/math/ea.c
index c4a219b..571dc1e 100644
--- a/kernel/math/ea.c
+++ b/kernel/math/ea.c
@@ -84,7 +84,7 @@ char * ea(struct info * info, unsigned short code)
EIP += 4;
break;
case 3:
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
}
I387.foo = offset;
I387.fos = 0x17;
diff --git a/kernel/math/emulate.c b/kernel/math/emulate.c
index 42a8f13..f3a6829 100644
--- a/kernel/math/emulate.c
+++ b/kernel/math/emulate.c
@@ -79,7 +79,7 @@ static void do_emu(struct info * info)
return;
case 0x1d1: case 0x1d2: case 0x1d3:
case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1e0:
ST(0).exponent ^= 0x8000;
return;
@@ -87,15 +87,15 @@ static void do_emu(struct info * info)
ST(0).exponent &= 0x7fff;
return;
case 0x1e2: case 0x1e3:
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1e4:
ftst(PST(0));
return;
case 0x1e5:
printk("fxam not implemented\n\r");
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1e6: case 0x1e7:
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1e8:
fpush();
ST(0) = CONST1;
@@ -125,7 +125,7 @@ static void do_emu(struct info * info)
ST(0) = CONSTZ;
return;
case 0x1ef:
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1fa:
fsqrt(PST(0),&tmp);
real_to_real(&tmp,&ST(0));
@@ -135,7 +135,7 @@ static void do_emu(struct info * info)
case 0x1f8: case 0x1f9: case 0x1fb: case 0x1fd:
case 0x1fe: case 0x1ff:
printk("%04x fxxx not implemented\n\r",code + 0xd800);
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0x1fc:
frndint(PST(0),&tmp);
real_to_real(&tmp,&ST(0));
@@ -246,7 +246,7 @@ static void do_emu(struct info * info)
return;
case 0xb8:
printk("ffree not implemented\n\r");
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
case 0xb9:
fxchg(&ST(0),&ST(code & 7));
return;
@@ -303,7 +303,7 @@ static void do_emu(struct info * info)
return;
case 0xf8:
printk("ffree not implemented\n\r");
- math_abort(info,1<<(SIGILL-1));
+ math_abort(info,SIGILL);
fpop();
return;
case 0xf9:
@@ -478,7 +478,7 @@ static void do_emu(struct info * info)
return;
}
printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
- math_abort(info,1<<(SIGFPE-1));
+ math_abort(info,SIGFPE);
}
void math_emulate(long ___false)
@@ -495,7 +495,7 @@ void math_emulate(long ___false)
void __math_abort(struct info * info, unsigned int signal)
{
EIP = ORIG_EIP;
- current->signal |= signal;
+ send_sig(signal,current,1);
__asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
}
@@ -542,7 +542,7 @@ static temp_real_unaligned * __st(int i)
void math_emulate(long ___false)
{
- current->signal |= 1<<(SIGFPE-1);
+ send_sig(SIGFPE,current,1);
schedule();
}
diff --git a/kernel/sys.c b/kernel/sys.c
index 9a0ede8..013b6d4 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -164,6 +164,9 @@ void ctrl_alt_del(void)
{
if (C_A_D)
hard_reset_now();
+ else
+ if (task[1])
+ send_sig(SIGINT,task[1],1);
}
diff --git a/kernel/sys_call.S b/kernel/sys_call.S
index 41014da..55dfce7 100644
--- a/kernel/sys_call.S
+++ b/kernel/sys_call.S
@@ -89,7 +89,7 @@ ENOSYS = 38
.globl _general_protection,_irq13,_reserved
.globl _alignment_check,_page_fault
.globl _keyboard_interrupt,_hd_interrupt
-.globl _IRQ3_interrupt,_IRQ4_interrupt
+.globl _IRQ3_interrupt,_IRQ4_interrupt,_IRQ5_interrupt,_IRQ9_interrupt
#define SAVE_ALL \
cld; \
@@ -275,6 +275,28 @@ _IRQ4_interrupt:
jmp ret_from_sys_call
.align 2
+_IRQ5_interrupt:
+ pushl $-1
+ SAVE_ALL
+ ACK_FIRST(0x20)
+ sti
+ call _do_IRQ5
+ cli
+ UNBLK_FIRST(0x20)
+ jmp ret_from_sys_call
+
+.align 2
+_IRQ9_interrupt:
+ pushl $-1
+ SAVE_ALL
+ ACK_SECOND(0x02)
+ sti
+ call _do_IRQ9
+ cli
+ UNBLK_SECOND(0x02)
+ jmp ret_from_sys_call
+
+.align 2
_timer_interrupt:
pushl $-1 # mark this as an int
SAVE_ALL